* [PATCH] Allow update hooks to update refs on their own @ 2007-11-27 21:17 Steven Grimm 2007-11-27 21:21 ` Jakub Narebski 2007-11-28 1:19 ` Junio C Hamano 0 siblings, 2 replies; 44+ messages in thread From: Steven Grimm @ 2007-11-27 21:17 UTC (permalink / raw) To: git This is useful in cases where a hook needs to modify an incoming commit in some way, e.g., adding an annotation to the commit message, noting the location of output from a profiling tool, or committing to an svn repository using git-svn. Signed-off-by: Steven Grimm <koreth@midwinter.com> --- I did this to support a bridge between svn and git. The idea is that the people who use git can do "git push" to publish their changes, and if they've pushed to certain branches, the changes will get transparently committed to svn using git-svn. The git users therefore can operate in a totally git-centric environment. (This is a step toward transitioning my company away from svn entirely: get people used to a native git environment with no direct svn interactions.) One problem is that git-svn wants to rewrite history to add its git-svn-id: lines, which means that when the update hook returns after doing the git-svn dcommit, HEAD is already pointed at the updated commit. Without this change or something like it, receive-pack bombs out because HEAD has moved, and the push appears to fail. This patch addresses that particular problem. There are other issues with the history rewriting, but this change is hopefully useful on its own for cases like the ones listed in the commit message. Documentation/git-receive-pack.txt | 8 +++- receive-pack.c | 68 +++++++++++++++++++++++------------- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt index 2633d94..115ae97 100644 --- a/Documentation/git-receive-pack.txt +++ b/Documentation/git-receive-pack.txt @@ -74,8 +74,12 @@ Note that the hook is called before the refname is updated, so either sha1-old is 0\{40} (meaning there is no such ref yet), or it should match what is recorded in refname. -The hook should exit with non-zero status if it wants to disallow -updating the named ref. Otherwise it should exit with zero. +The hook may optionally choose to update the ref on its own, e.g., +if it needs to modify incoming revisions in some way. If it updates +the ref, it should exit with a status of 100. The hook should exit +with a status between 1 and 99 if it wants to disallow updating the +named ref. Otherwise it should exit with zero, and the ref will be +updated automatically. Successful execution (a zero exit status) of this hook does not ensure the ref will actually be updated, it is only a prerequisite. diff --git a/receive-pack.c b/receive-pack.c index 38e35c0..19162ec 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -18,6 +18,9 @@ static int report_status; static char capabilities[] = " report-status delete-refs "; static int capabilities_sent; +/* Update hook exit code: hook has updated ref on its own */ +#define EXIT_CODE_REF_UPDATED 100 + static int receive_pack_config(const char *var, const char *value) { if (strcmp(var, "receive.denynonfastforwards") == 0) { @@ -70,8 +73,11 @@ static struct command *commands; static const char pre_receive_hook[] = "hooks/pre-receive"; static const char post_receive_hook[] = "hooks/post-receive"; -static int hook_status(int code, const char *hook_name) +static int hook_status(int code, const char *hook_name, int ok_start) { + if (ok_start && -code >= ok_start) + return -code; + switch (code) { case 0: return 0; @@ -121,7 +127,7 @@ static int run_hook(const char *hook_name) code = start_command(&proc); if (code) - return hook_status(code, hook_name); + return hook_status(code, hook_name, 0); for (cmd = commands; cmd; cmd = cmd->next) { if (!cmd->error_string) { size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n", @@ -132,7 +138,7 @@ static int run_hook(const char *hook_name) break; } } - return hook_status(finish_command(&proc), hook_name); + return hook_status(finish_command(&proc), hook_name, 0); } static int run_update_hook(struct command *cmd) @@ -155,7 +161,8 @@ static int run_update_hook(struct command *cmd) proc.no_stdin = 1; proc.stdout_to_stderr = 1; - return hook_status(run_command(&proc), update_hook); + return hook_status(run_command(&proc), update_hook, + EXIT_CODE_REF_UPDATED); } static const char *update(struct command *cmd) @@ -194,32 +201,45 @@ static const char *update(struct command *cmd) return "non-fast forward"; } } - if (run_update_hook(cmd)) { - error("hook declined to update %s", name); - return "hook declined"; - } - - if (is_null_sha1(new_sha1)) { - if (delete_ref(name, old_sha1)) { - error("failed to delete %s", name); - return "failed to delete"; + switch (run_update_hook(cmd)) { + case 0: + if (is_null_sha1(new_sha1)) { + if (delete_ref(name, old_sha1)) { + error("failed to delete %s", name); + return "failed to delete"; + } + fprintf(stderr, "%s: %s -> deleted\n", name, + sha1_to_hex(old_sha1)); } - fprintf(stderr, "%s: %s -> deleted\n", name, - sha1_to_hex(old_sha1)); - return NULL; /* good */ - } - else { - lock = lock_any_ref_for_update(name, old_sha1, 0); - if (!lock) { - error("failed to lock %s", name); - return "failed to lock"; + else { + lock = lock_any_ref_for_update(name, old_sha1, 0); + if (!lock) { + error("failed to lock %s", name); + return "failed to lock"; + } + if (write_ref_sha1(lock, new_sha1, "push")) { + return "failed to write"; /* error() already called */ + } + fprintf(stderr, "%s: %s -> %s\n", name, + sha1_to_hex(old_sha1), sha1_to_hex(new_sha1)); } - if (write_ref_sha1(lock, new_sha1, "push")) { - return "failed to write"; /* error() already called */ + return NULL; /* good */ + + case EXIT_CODE_REF_UPDATED: + /* hook has taken care of updating ref, which means it + might be a different revision than we think. */ + if (! resolve_ref(name, new_sha1, 1, NULL)) { + error("can't resolve ref %s after hook updated it", + name); + return "ref not resolvable"; } fprintf(stderr, "%s: %s -> %s\n", name, sha1_to_hex(old_sha1), sha1_to_hex(new_sha1)); return NULL; /* good */ + + default: + error("hook declined to update %s", name); + return "hook declined"; } } -- 1.5.3.6.862.g7acd00-dirty ^ permalink raw reply related [flat|nested] 44+ messages in thread
* Re: [PATCH] Allow update hooks to update refs on their own 2007-11-27 21:17 [PATCH] Allow update hooks to update refs on their own Steven Grimm @ 2007-11-27 21:21 ` Jakub Narebski 2007-11-27 21:23 ` Steven Grimm 2007-11-28 1:19 ` Junio C Hamano 1 sibling, 1 reply; 44+ messages in thread From: Jakub Narebski @ 2007-11-27 21:21 UTC (permalink / raw) To: git Steven Grimm wrote: > +The hook may optionally choose to update the ref on its own, e.g., > +if it needs to modify incoming revisions in some way. If it updates > +the ref, it should exit with a status of 100. Why 100, and not for example 127, 128 or -1? -- Jakub Narebski Warsaw, Poland ShadeHawk on #git ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH] Allow update hooks to update refs on their own 2007-11-27 21:21 ` Jakub Narebski @ 2007-11-27 21:23 ` Steven Grimm 0 siblings, 0 replies; 44+ messages in thread From: Steven Grimm @ 2007-11-27 21:23 UTC (permalink / raw) To: Jakub Narebski; +Cc: git On Nov 27, 2007, at 1:21 PM, Jakub Narebski wrote: >> +The hook may optionally choose to update the ref on its own, e.g., >> +if it needs to modify incoming revisions in some way. If it updates >> +the ref, it should exit with a status of 100. > > Why 100, and not for example 127, 128 or -1? Because those seemed much more likely to be used by existing hook scripts to denote an error condition, and I wanted to reduce the chance that this would break existing scripts. -Steve ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH] Allow update hooks to update refs on their own 2007-11-27 21:17 [PATCH] Allow update hooks to update refs on their own Steven Grimm 2007-11-27 21:21 ` Jakub Narebski @ 2007-11-28 1:19 ` Junio C Hamano 2007-11-28 2:40 ` Steven Grimm 1 sibling, 1 reply; 44+ messages in thread From: Junio C Hamano @ 2007-11-28 1:19 UTC (permalink / raw) To: Steven Grimm; +Cc: git How does this interact with the "pretend to have fetched back immediately" supported by modern git-push? ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH] Allow update hooks to update refs on their own 2007-11-28 1:19 ` Junio C Hamano @ 2007-11-28 2:40 ` Steven Grimm 2007-11-28 3:25 ` Daniel Barkalow 0 siblings, 1 reply; 44+ messages in thread From: Steven Grimm @ 2007-11-28 2:40 UTC (permalink / raw) To: Junio C Hamano; +Cc: git On Nov 27, 2007, at 5:19 PM, Junio C Hamano wrote: > How does this interact with the "pretend to have fetched back > immediately" supported by modern git-push? That continues to fire, but it updates the local tracking ref to point to the SHA1 that was pushed, which isn't the actual remote ref. So you have to do a real fetch to get the local tracking ref pointed to the right place. In other words, that feature doesn't do any good in this context, but it doesn't really hurt anything either. It would of course be better if git-push could notice that it needs to do an actual fetch. I think it'd be sufficient to transmit the final remote ref SHA1 back to git-push, and if it doesn't match what was pushed, that's a sign that a fetch is needed. But that change wouldn't be mutually exclusive with this patch, I believe. -Steve ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH] Allow update hooks to update refs on their own 2007-11-28 2:40 ` Steven Grimm @ 2007-11-28 3:25 ` Daniel Barkalow 2007-11-28 3:49 ` Junio C Hamano 2007-11-28 16:10 ` Jeff King 0 siblings, 2 replies; 44+ messages in thread From: Daniel Barkalow @ 2007-11-28 3:25 UTC (permalink / raw) To: Steven Grimm; +Cc: Junio C Hamano, git On Tue, 27 Nov 2007, Steven Grimm wrote: > On Nov 27, 2007, at 5:19 PM, Junio C Hamano wrote: > > >How does this interact with the "pretend to have fetched back > >immediately" supported by modern git-push? > > > That continues to fire, but it updates the local tracking ref to point to the > SHA1 that was pushed, which isn't the actual remote ref. So you have to do a > real fetch to get the local tracking ref pointed to the right place. In other > words, that feature doesn't do any good in this context, but it doesn't really > hurt anything either. > > It would of course be better if git-push could notice that it needs to do an > actual fetch. I think it'd be sufficient to transmit the final remote ref SHA1 > back to git-push, and if it doesn't match what was pushed, that's a sign that > a fetch is needed. But that change wouldn't be mutually exclusive with this > patch, I believe. Couldn't you do this with a status message? ("ok <refname> changed by hook" or something.) I disagree that the feature doesn't do any good; it records that the state of the remote is at least as new as the local state, so you can tell without a network connection that you don't have any local changes you haven't sent off. -Daniel *This .sig left intentionally blank* ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH] Allow update hooks to update refs on their own 2007-11-28 3:25 ` Daniel Barkalow @ 2007-11-28 3:49 ` Junio C Hamano 2007-11-28 5:20 ` Steven Grimm 2007-11-28 16:10 ` Jeff King 1 sibling, 1 reply; 44+ messages in thread From: Junio C Hamano @ 2007-11-28 3:49 UTC (permalink / raw) To: Daniel Barkalow; +Cc: Steven Grimm, git Daniel Barkalow <barkalow@iabervon.org> writes: > On Tue, 27 Nov 2007, Steven Grimm wrote: > >> On Nov 27, 2007, at 5:19 PM, Junio C Hamano wrote: >> >> >How does this interact with the "pretend to have fetched back >> >immediately" supported by modern git-push? >> >> >> That continues to fire, but it updates the local tracking ref to point to the >> SHA1 that was pushed, which isn't the actual remote ref. So you have to do a >> real fetch to get the local tracking ref pointed to the right place. In other >> words, that feature doesn't do any good in this context, but it doesn't really >> hurt anything either. >> >> It would of course be better if git-push could notice that it needs to do an >> actual fetch. I think it'd be sufficient to transmit the final remote ref SHA1 >> back to git-push, and if it doesn't match what was pushed, that's a sign that >> a fetch is needed. But that change wouldn't be mutually exclusive with this >> patch, I believe. > > Couldn't you do this with a status message? ("ok <refname> changed by > hook" or something.) > > I disagree that the feature doesn't do any good; it records that the state > of the remote is at least as new as the local state, so you can tell > without a network connection that you don't have any local changes you > haven't sent off. Yeah, and I am wondering why update hook needs to be changed for this. Didn't we introduce post-receive exactly for this sort of thing? ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH] Allow update hooks to update refs on their own 2007-11-28 3:49 ` Junio C Hamano @ 2007-11-28 5:20 ` Steven Grimm 0 siblings, 0 replies; 44+ messages in thread From: Steven Grimm @ 2007-11-28 5:20 UTC (permalink / raw) To: Junio C Hamano; +Cc: Daniel Barkalow, git On Nov 27, 2007, at 7:49 PM, Junio C Hamano wrote: > Yeah, and I am wondering why update hook needs to be changed for this. > Didn't we introduce post-receive exactly for this sort of thing? I didn't think the post-receive hook could reject a revision. I need to be able to do that here, e.g., if the user's change fails to commit because it's rejected by an svn commit hook. (Hooks upon hooks upon hooks...) If I do the svn commit in post-receive it's not clear how the user would repush the change after fixing it -- their fix would show up as a delta on top of a revision that I can't commit on its own to svn, so I would somehow have to know to do a squash merge and there would no longer be a one-to-one correspondence between git and svn revisions. That's not a total showstopper (I'm rewriting history anyway) but it sure seems like it'll be confusing and error-prone. Also, running in post-receive will subject me to race conditions that aren't present in the update hook case; if I make my update hook script do its own locking and update the refs on its own, I'm guaranteed no other push will come along and update my ref out from under me. In post-receive there is no such guarantee and I may end up sending two pushes' worth of commits to svn when I think I'm only sending one. If I'm misunderstanding the flow of control, please feel free to correct me. It just seemed like update was the only good place to do what I needed to do. -Steve ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH] Allow update hooks to update refs on their own 2007-11-28 3:25 ` Daniel Barkalow 2007-11-28 3:49 ` Junio C Hamano @ 2007-11-28 16:10 ` Jeff King 2007-11-28 19:00 ` Junio C Hamano 1 sibling, 1 reply; 44+ messages in thread From: Jeff King @ 2007-11-28 16:10 UTC (permalink / raw) To: Daniel Barkalow; +Cc: Steven Grimm, Junio C Hamano, git On Tue, Nov 27, 2007 at 10:25:32PM -0500, Daniel Barkalow wrote: > > It would of course be better if git-push could notice that it needs > > to do an actual fetch. I think it'd be sufficient to transmit the > > final remote ref SHA1 back to git-push, and if it doesn't match what > > was pushed, that's a sign that a fetch is needed. But that change > > wouldn't be mutually exclusive with this patch, I believe. > > Couldn't you do this with a status message? ("ok <refname> changed by > hook" or something.) Having just touched this code, I believe the answer is yes. receive-pack has always sent just "ok <refname>\n", so we could start interpreting anything after the <refname> bit freely (I think "ok <refname> changed-to <hash>" is even more informative, but perhaps not useful given that the sender probably doesn't have that commit object). -Peff ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH] Allow update hooks to update refs on their own 2007-11-28 16:10 ` Jeff King @ 2007-11-28 19:00 ` Junio C Hamano 2007-11-28 19:41 ` Steven Grimm 0 siblings, 1 reply; 44+ messages in thread From: Junio C Hamano @ 2007-11-28 19:00 UTC (permalink / raw) To: Jeff King; +Cc: Daniel Barkalow, Steven Grimm, git Jeff King <peff@peff.net> writes: >> Couldn't you do this with a status message? ("ok <refname> changed by >> hook" or something.) > > Having just touched this code, I believe the answer is yes. receive-pack > has always sent just "ok <refname>\n", so we could start interpreting > anything after the <refname> bit freely... More importantly, no send-pack choked on "ok <refname>" followed by a SP (old ones just checked "ok" and nothing else, the current one NULs out the SP and lets you use the rest as a message), so such a change to receive-pack does not have to break older send-pack. ^ permalink raw reply [flat|nested] 44+ messages in thread
* [PATCH] Allow update hooks to update refs on their own 2007-11-28 19:00 ` Junio C Hamano @ 2007-11-28 19:41 ` Steven Grimm 2007-11-28 19:49 ` Jeff King 0 siblings, 1 reply; 44+ messages in thread From: Steven Grimm @ 2007-11-28 19:41 UTC (permalink / raw) To: git This is useful in cases where a hook needs to modify an incoming commit in some way, e.g., adding an annotation to the commit message, noting the location of output from a profiling tool, or committing to an svn repository using git-svn. recieve-pack will now send the post-update SHA1 back to send-pack. If it's different than the one that was pushed, git-push won't do the fake update of the local tracking ref and will instead inform the user that the tracking ref needs to be fetched. Signed-off-by: Steven Grimm <koreth@midwinter.com> --- The protocol was much easier to tweak than I expected. It seems like a reasonable extension to always send back the resulting SHA1; won't stop other people from adding more information later. It would IMO be better still if git-push were to fetch automatically here rather than just printing a message, but I'm not sure if that would smack too much of automagic behavior to people. Comments welcome. Documentation/git-receive-pack.txt | 8 +++- receive-pack.c | 72 +++++++++++++++++++++++------------- remote.c | 2 +- remote.h | 2 + send-pack.c | 64 +++++++++++++++++++++++++++----- 5 files changed, 109 insertions(+), 39 deletions(-) diff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt index 2633d94..115ae97 100644 --- a/Documentation/git-receive-pack.txt +++ b/Documentation/git-receive-pack.txt @@ -74,8 +74,12 @@ Note that the hook is called before the refname is updated, so either sha1-old is 0\{40} (meaning there is no such ref yet), or it should match what is recorded in refname. -The hook should exit with non-zero status if it wants to disallow -updating the named ref. Otherwise it should exit with zero. +The hook may optionally choose to update the ref on its own, e.g., +if it needs to modify incoming revisions in some way. If it updates +the ref, it should exit with a status of 100. The hook should exit +with a status between 1 and 99 if it wants to disallow updating the +named ref. Otherwise it should exit with zero, and the ref will be +updated automatically. Successful execution (a zero exit status) of this hook does not ensure the ref will actually be updated, it is only a prerequisite. diff --git a/receive-pack.c b/receive-pack.c index 38e35c0..d07dd6d 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -18,6 +18,9 @@ static int report_status; static char capabilities[] = " report-status delete-refs "; static int capabilities_sent; +/* Update hook exit code: hook has updated ref on its own */ +#define EXIT_CODE_REF_UPDATED 100 + static int receive_pack_config(const char *var, const char *value) { if (strcmp(var, "receive.denynonfastforwards") == 0) { @@ -70,8 +73,11 @@ static struct command *commands; static const char pre_receive_hook[] = "hooks/pre-receive"; static const char post_receive_hook[] = "hooks/post-receive"; -static int hook_status(int code, const char *hook_name) +static int hook_status(int code, const char *hook_name, int ok_start) { + if (ok_start && -code >= ok_start) + return -code; + switch (code) { case 0: return 0; @@ -121,7 +127,7 @@ static int run_hook(const char *hook_name) code = start_command(&proc); if (code) - return hook_status(code, hook_name); + return hook_status(code, hook_name, 0); for (cmd = commands; cmd; cmd = cmd->next) { if (!cmd->error_string) { size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n", @@ -132,7 +138,7 @@ static int run_hook(const char *hook_name) break; } } - return hook_status(finish_command(&proc), hook_name); + return hook_status(finish_command(&proc), hook_name, 0); } static int run_update_hook(struct command *cmd) @@ -155,7 +161,8 @@ static int run_update_hook(struct command *cmd) proc.no_stdin = 1; proc.stdout_to_stderr = 1; - return hook_status(run_command(&proc), update_hook); + return hook_status(run_command(&proc), update_hook, + EXIT_CODE_REF_UPDATED); } static const char *update(struct command *cmd) @@ -194,32 +201,45 @@ static const char *update(struct command *cmd) return "non-fast forward"; } } - if (run_update_hook(cmd)) { - error("hook declined to update %s", name); - return "hook declined"; - } - - if (is_null_sha1(new_sha1)) { - if (delete_ref(name, old_sha1)) { - error("failed to delete %s", name); - return "failed to delete"; + switch (run_update_hook(cmd)) { + case 0: + if (is_null_sha1(new_sha1)) { + if (delete_ref(name, old_sha1)) { + error("failed to delete %s", name); + return "failed to delete"; + } + fprintf(stderr, "%s: %s -> deleted\n", name, + sha1_to_hex(old_sha1)); } - fprintf(stderr, "%s: %s -> deleted\n", name, - sha1_to_hex(old_sha1)); - return NULL; /* good */ - } - else { - lock = lock_any_ref_for_update(name, old_sha1, 0); - if (!lock) { - error("failed to lock %s", name); - return "failed to lock"; + else { + lock = lock_any_ref_for_update(name, old_sha1, 0); + if (!lock) { + error("failed to lock %s", name); + return "failed to lock"; + } + if (write_ref_sha1(lock, new_sha1, "push")) { + return "failed to write"; /* error() already called */ + } + fprintf(stderr, "%s: %s -> %s\n", name, + sha1_to_hex(old_sha1), sha1_to_hex(new_sha1)); } - if (write_ref_sha1(lock, new_sha1, "push")) { - return "failed to write"; /* error() already called */ + return NULL; /* good */ + + case EXIT_CODE_REF_UPDATED: + /* hook has taken care of updating ref, which means it + might be a different revision than we think. */ + if (! resolve_ref(name, new_sha1, 1, NULL)) { + error("can't resolve ref %s after hook updated it", + name); + return "ref not resolvable"; } fprintf(stderr, "%s: %s -> %s\n", name, sha1_to_hex(old_sha1), sha1_to_hex(new_sha1)); return NULL; /* good */ + + default: + error("hook declined to update %s", name); + return "hook declined"; } } @@ -419,8 +439,8 @@ static void report(const char *unpack_status) unpack_status ? unpack_status : "ok"); for (cmd = commands; cmd; cmd = cmd->next) { if (!cmd->error_string) - packet_write(1, "ok %s\n", - cmd->ref_name); + packet_write(1, "ok %s %s\n", + cmd->ref_name, sha1_to_hex(cmd->new_sha1)); else packet_write(1, "ng %s %s\n", cmd->ref_name, cmd->error_string); diff --git a/remote.c b/remote.c index bec2ba1..07558c1 100644 --- a/remote.c +++ b/remote.c @@ -684,7 +684,7 @@ static int match_explicit_refs(struct ref *src, struct ref *dst, return -errs; } -static struct ref *find_ref_by_name(struct ref *list, const char *name) +struct ref *find_ref_by_name(struct ref *list, const char *name) { for ( ; list; list = list->next) if (!strcmp(list->name, name)) diff --git a/remote.h b/remote.h index 878b4ec..e822f3c 100644 --- a/remote.h +++ b/remote.h @@ -56,6 +56,8 @@ void ref_remove_duplicates(struct ref *ref_map); struct refspec *parse_ref_spec(int nr_refspec, const char **refspec); +struct ref *find_ref_by_name(struct ref *list, const char *name); + int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, int nr_refspec, char **refspec, int all); diff --git a/send-pack.c b/send-pack.c index b74fd45..bf564ae 100644 --- a/send-pack.c +++ b/send-pack.c @@ -147,9 +147,31 @@ static void get_local_heads(void) for_each_ref(one_local_ref, NULL); } -static int receive_status(int in) +/* Verifies that a remote ref matches the revision we pushed. */ +static void verify_tracking_ref(const char *refname, const char *newsha1_hex, struct ref *remote_refs) +{ + struct ref *ref; + unsigned char newsha1[20]; + if (get_sha1_hex(newsha1_hex, newsha1)) { + fprintf(stderr, "protocol error: bad sha1 %s\n", newsha1_hex); + return; + } + ref = find_ref_by_name(remote_refs, refname); + if (ref != NULL) { + if (hashcmp(ref->new_sha1, newsha1)) { + hashcpy(ref->new_sha1, newsha1); + ref->force = 1; + } + else { + ref->force = 0; + } + } +} + +static int receive_status(int in, struct ref *remote_refs) { char line[1000]; + char *sha1; int ret = 0; int len = packet_read_line(in, line, sizeof(line)); if (len < 10 || memcmp(line, "unpack ", 7)) { @@ -170,8 +192,22 @@ static int receive_status(int in) ret = -1; break; } - if (!memcmp(line, "ok", 2)) + if (!memcmp(line, "ok", 2)) { + /* If the result looks like "ok refname sha1", we can + * compare the actual SHA1 for the remote ref with the + * one we pushed; if they differ then the remote + * may have rewritten our commits and we will need to + * do a real fetch rather than just updating the + * tracking refs. + */ + if (line[2] == ' ' && + (sha1 = strchr(line+3, ' '))) { + /* null-terminate refname */ + *sha1++ = '\0'; + verify_tracking_ref(line+3, sha1, remote_refs); + } continue; + } fputs(line, stderr); ret = -1; } @@ -196,13 +232,21 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref) return; if (!remote_find_tracking(remote, &rs)) { - fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst); - if (is_null_sha1(ref->peer_ref->new_sha1)) { - if (delete_ref(rs.dst, NULL)) - error("Failed to delete"); - } else - update_ref("update by push", rs.dst, - ref->new_sha1, NULL, 0, 0); + if (ref->force) { + fprintf(stderr, + "local tracking ref '%s' needs to be fetched\n", + rs.dst); + } + else { + fprintf(stderr, "updating local tracking ref '%s'\n", + rs.dst); + if (is_null_sha1(ref->peer_ref->new_sha1)) { + if (delete_ref(rs.dst, NULL)) + error("Failed to delete"); + } else + update_ref("update by push", rs.dst, + ref->new_sha1, NULL, 0, 0); + } free(rs.dst); } } @@ -343,7 +387,7 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha close(out); if (expect_status_report) { - if (receive_status(in)) + if (receive_status(in, remote_refs)) ret = -4; } -- 1.5.3.6.862.gb50ba-dirty ^ permalink raw reply related [flat|nested] 44+ messages in thread
* Re: [PATCH] Allow update hooks to update refs on their own 2007-11-28 19:41 ` Steven Grimm @ 2007-11-28 19:49 ` Jeff King 2007-11-28 20:16 ` Steven Grimm 2007-11-28 21:49 ` [PATCH] " Junio C Hamano 0 siblings, 2 replies; 44+ messages in thread From: Jeff King @ 2007-11-28 19:49 UTC (permalink / raw) To: Steven Grimm; +Cc: git On Wed, Nov 28, 2007 at 11:41:59AM -0800, Steven Grimm wrote: > recieve-pack will now send the post-update SHA1 back to send-pack. If > it's different than the one that was pushed, git-push won't do the > fake update of the local tracking ref and will instead inform the user > that the tracking ref needs to be fetched. Hrm, this is going to have nasty conflicts with 'next', which already does the remote ref matching. I think the best way to implement this would probably be on top of the jk/send-pack topic in next, and add a new REF_STATUS_REMOTE_CHANGED status type. -Peff ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH] Allow update hooks to update refs on their own 2007-11-28 19:49 ` Jeff King @ 2007-11-28 20:16 ` Steven Grimm 2007-11-28 20:22 ` Jeff King ` (2 more replies) 2007-11-28 21:49 ` [PATCH] " Junio C Hamano 1 sibling, 3 replies; 44+ messages in thread From: Steven Grimm @ 2007-11-28 20:16 UTC (permalink / raw) To: Jeff King; +Cc: git On Nov 28, 2007, at 11:49 AM, Jeff King wrote: > Hrm, this is going to have nasty conflicts with 'next', which already > does the remote ref matching. I think the best way to implement this > would probably be on top of the jk/send-pack topic in next, and add a > new REF_STATUS_REMOTE_CHANGED status type. Ah, yes, I see what you mean. Okay, please ignore my latest patch -- I will redo it on top of "next" later today/tonight. Well, actually, I would still like opinions on one thing: What do people think of having git-push do a fetch if the remote side changes a ref to point to a revision that doesn't exist locally? Is there a situation where you'd ever want to *not* do that? -Steve ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH] Allow update hooks to update refs on their own 2007-11-28 20:16 ` Steven Grimm @ 2007-11-28 20:22 ` Jeff King 2007-11-28 22:01 ` Junio C Hamano 2007-11-28 22:14 ` [PATCH v3] " Steven Grimm 2 siblings, 0 replies; 44+ messages in thread From: Jeff King @ 2007-11-28 20:22 UTC (permalink / raw) To: Steven Grimm; +Cc: git On Wed, Nov 28, 2007 at 12:16:27PM -0800, Steven Grimm wrote: > Well, actually, I would still like opinions on one thing: What do people > think of having git-push do a fetch if the remote side changes a ref to > point to a revision that doesn't exist locally? Is there a situation where > you'd ever want to *not* do that? It can be slow, since you have to make another connection to the server, so clearly it should only be done when you detect an update (which I think is what you're proposing). A raw "git-fetch" might pull a lot of extra cruft that you didn't want to get right now. So if you did do it, I think it would make sense to construct a set of refspecs that match only the ones which need pulling (i.e., in update_tracking_ref, rather than doing the update, construct a refspec of "local:tracking", and then hand all such refspecs to git-fetch). -Peff ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH] Allow update hooks to update refs on their own 2007-11-28 20:16 ` Steven Grimm 2007-11-28 20:22 ` Jeff King @ 2007-11-28 22:01 ` Junio C Hamano 2007-11-28 22:14 ` [PATCH v3] " Steven Grimm 2 siblings, 0 replies; 44+ messages in thread From: Junio C Hamano @ 2007-11-28 22:01 UTC (permalink / raw) To: Steven Grimm; +Cc: Jeff King, git Steven Grimm <koreth@midwinter.com> writes: > Well, actually, I would still like opinions on one thing: What do > people think of having git-push do a fetch if the remote side changes > a ref to point to a revision that doesn't exist locally? I do not like it at all. git-push is about pushing what you did to the other end; you may or may not want to refetch what other people immediately on top of what you did, and this includes what the hook script did in the repository you have pushed into. If you want an immediate refetch, you can script it so that you call push and then fetch; the latter needs to be a separate connection to a different service anyway. ^ permalink raw reply [flat|nested] 44+ messages in thread
* [PATCH v3] Allow update hooks to update refs on their own 2007-11-28 20:16 ` Steven Grimm 2007-11-28 20:22 ` Jeff King 2007-11-28 22:01 ` Junio C Hamano @ 2007-11-28 22:14 ` Steven Grimm 2007-11-28 23:03 ` Jeff King 2 siblings, 1 reply; 44+ messages in thread From: Steven Grimm @ 2007-11-28 22:14 UTC (permalink / raw) To: git This is useful in cases where a hook needs to modify an incoming commit in some way, e.g., adding an annotation to the commit message, noting the location of output from a profiling tool, or committing to an svn repository using git-svn. recieve-pack will now send the post-update SHA1 back to send-pack. If it's different than the one that was pushed, git-push won't do the fake update of the local tracking ref and will instead inform the user that the tracking ref needs to be fetched. Signed-off-by: Steven Grimm <koreth@midwinter.com> --- This is on top of "next". It doesn't do any automatic fetching, just prints a status message. Documentation/git-receive-pack.txt | 8 +++- builtin-send-pack.c | 65 +++++++++++++++++++++++++++------- cache.h | 1 + receive-pack.c | 68 +++++++++++++++++++++++------------- 4 files changed, 103 insertions(+), 39 deletions(-) diff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt index 2633d94..115ae97 100644 --- a/Documentation/git-receive-pack.txt +++ b/Documentation/git-receive-pack.txt @@ -74,8 +74,12 @@ Note that the hook is called before the refname is updated, so either sha1-old is 0\{40} (meaning there is no such ref yet), or it should match what is recorded in refname. -The hook should exit with non-zero status if it wants to disallow -updating the named ref. Otherwise it should exit with zero. +The hook may optionally choose to update the ref on its own, e.g., +if it needs to modify incoming revisions in some way. If it updates +the ref, it should exit with a status of 100. The hook should exit +with a status between 1 and 99 if it wants to disallow updating the +named ref. Otherwise it should exit with zero, and the ref will be +updated automatically. Successful execution (a zero exit status) of this hook does not ensure the ref will actually be updated, it is only a prerequisite. diff --git a/builtin-send-pack.c b/builtin-send-pack.c index 25ae1fe..4ba716a 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -164,7 +164,9 @@ static int receive_status(int in, struct ref *refs) hint = NULL; while (1) { char *refname; + char *newsha1_hex; char *msg; + unsigned char newsha1[20]; len = packet_read_line(in, line, sizeof(line)); if (!len) break; @@ -177,7 +179,16 @@ static int receive_status(int in, struct ref *refs) line[strlen(line)-1] = '\0'; refname = line + 3; - msg = strchr(refname, ' '); + newsha1_hex = strchr(refname, ' '); + if (newsha1_hex) { + *newsha1_hex++ = '\0'; + if (get_sha1_hex(newsha1_hex, newsha1)) { + fprintf(stderr, "protocol error: bad sha1 %s\n", + newsha1_hex); + newsha1_hex = NULL; + } + } + msg = strchr(newsha1_hex, ' '); if (msg) *msg++ = '\0'; @@ -197,8 +208,16 @@ static int receive_status(int in, struct ref *refs) continue; } - if (line[0] == 'o' && line[1] == 'k') - hint->status = REF_STATUS_OK; + if (line[0] == 'o' && line[1] == 'k') { + if (newsha1_hex != NULL && + hashcmp(hint->new_sha1, newsha1)) { + hint->status = REF_STATUS_REMOTE_CHANGED; + hashcpy(hint->new_sha1, newsha1); + } + else { + hint->status = REF_STATUS_OK; + } + } else { hint->status = REF_STATUS_REMOTE_REJECT; ret = -1; @@ -215,21 +234,34 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref) { struct refspec rs; - if (ref->status != REF_STATUS_OK) + if (ref->status != REF_STATUS_OK && + ref->status != REF_STATUS_REMOTE_CHANGED) return; rs.src = ref->name; rs.dst = NULL; if (!remote_find_tracking(remote, &rs)) { - if (args.verbose) - fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst); - if (ref->deletion) { - if (delete_ref(rs.dst, NULL)) - error("Failed to delete"); - } else - update_ref("update by push", rs.dst, - ref->new_sha1, NULL, 0, 0); + if (ref->status == REF_STATUS_REMOTE_CHANGED) { + if (args.verbose) { + fprintf(stderr, "local tracking ref '%s' " + "needs to be fetched\n", rs.dst); + } + } + else { + if (args.verbose) { + fprintf(stderr, "updating local tracking " + "ref '%s'\n", rs.dst); + } + if (ref->deletion) { + if (delete_ref(rs.dst, NULL)) + error("Failed to delete"); + } + else { + update_ref("update by push", rs.dst, + ref->new_sha1, NULL, 0, 0); + } + } free(rs.dst); } } @@ -329,6 +361,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count) ref->deletion ? NULL : ref->peer_ref, "remote failed to report status"); break; + case REF_STATUS_REMOTE_CHANGED: + print_ref_status('+', "[changed]", ref, ref->peer_ref, + "remote made changes to revisions"); + break; case REF_STATUS_OK: print_ok_ref_status(ref); break; @@ -349,12 +385,14 @@ static void print_push_status(const char *dest, struct ref *refs) } for (ref = refs; ref; ref = ref->next) - if (ref->status == REF_STATUS_OK) + if (ref->status == REF_STATUS_OK || + ref->status == REF_STATUS_REMOTE_CHANGED) n += print_one_push_status(ref, dest, n); for (ref = refs; ref; ref = ref->next) { if (ref->status != REF_STATUS_NONE && ref->status != REF_STATUS_UPTODATE && + ref->status != REF_STATUS_REMOTE_CHANGED && ref->status != REF_STATUS_OK) n += print_one_push_status(ref, dest, n); } @@ -522,6 +560,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest switch (ref->status) { case REF_STATUS_NONE: case REF_STATUS_UPTODATE: + case REF_STATUS_REMOTE_CHANGED: case REF_STATUS_OK: break; default: diff --git a/cache.h b/cache.h index 4e59646..ae2427d 100644 --- a/cache.h +++ b/cache.h @@ -516,6 +516,7 @@ struct ref { REF_STATUS_UPTODATE, REF_STATUS_REMOTE_REJECT, REF_STATUS_EXPECTING_REPORT, + REF_STATUS_REMOTE_CHANGED, } status; char *remote_status; struct ref *peer_ref; /* when renaming */ diff --git a/receive-pack.c b/receive-pack.c index ed44b89..1101e18 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -18,6 +18,9 @@ static int report_status; static char capabilities[] = " report-status delete-refs "; static int capabilities_sent; +/* Update hook exit code: hook has updated ref on its own */ +#define EXIT_CODE_REF_UPDATED 100 + static int receive_pack_config(const char *var, const char *value) { if (strcmp(var, "receive.denynonfastforwards") == 0) { @@ -70,8 +73,11 @@ static struct command *commands; static const char pre_receive_hook[] = "hooks/pre-receive"; static const char post_receive_hook[] = "hooks/post-receive"; -static int hook_status(int code, const char *hook_name) +static int hook_status(int code, const char *hook_name, int ok_start) { + if (ok_start && -code >= ok_start) + return -code; + switch (code) { case 0: return 0; @@ -121,7 +127,7 @@ static int run_hook(const char *hook_name) code = start_command(&proc); if (code) - return hook_status(code, hook_name); + return hook_status(code, hook_name, 0); for (cmd = commands; cmd; cmd = cmd->next) { if (!cmd->error_string) { size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n", @@ -132,7 +138,7 @@ static int run_hook(const char *hook_name) break; } } - return hook_status(finish_command(&proc), hook_name); + return hook_status(finish_command(&proc), hook_name, 0); } static int run_update_hook(struct command *cmd) @@ -155,7 +161,8 @@ static int run_update_hook(struct command *cmd) proc.no_stdin = 1; proc.stdout_to_stderr = 1; - return hook_status(run_command(&proc), update_hook); + return hook_status(run_command(&proc), update_hook, + EXIT_CODE_REF_UPDATED); } static const char *update(struct command *cmd) @@ -194,28 +201,41 @@ static const char *update(struct command *cmd) return "non-fast forward"; } } - if (run_update_hook(cmd)) { - error("hook declined to update %s", name); - return "hook declined"; - } - - if (is_null_sha1(new_sha1)) { - if (delete_ref(name, old_sha1)) { - error("failed to delete %s", name); - return "failed to delete"; + switch (run_update_hook(cmd)) { + case 0: + if (is_null_sha1(new_sha1)) { + if (delete_ref(name, old_sha1)) { + error("failed to delete %s", name); + return "failed to delete"; + } + fprintf(stderr, "%s: %s -> deleted\n", name, + sha1_to_hex(old_sha1)); } - return NULL; /* good */ - } - else { - lock = lock_any_ref_for_update(name, old_sha1, 0); - if (!lock) { - error("failed to lock %s", name); - return "failed to lock"; + else { + lock = lock_any_ref_for_update(name, old_sha1, 0); + if (!lock) { + error("failed to lock %s", name); + return "failed to lock"; + } + if (write_ref_sha1(lock, new_sha1, "push")) { + return "failed to write"; /* error() already called */ + } } - if (write_ref_sha1(lock, new_sha1, "push")) { - return "failed to write"; /* error() already called */ + return NULL; /* good */ + + case EXIT_CODE_REF_UPDATED: + /* hook has taken care of updating ref, which means it + might be a different revision than we think. */ + if (! resolve_ref(name, new_sha1, 1, NULL)) { + error("can't resolve ref %s after hook updated it", + name); + return "ref not resolvable"; } return NULL; /* good */ + + default: + error("hook declined to update %s", name); + return "hook declined"; } } @@ -415,8 +435,8 @@ static void report(const char *unpack_status) unpack_status ? unpack_status : "ok"); for (cmd = commands; cmd; cmd = cmd->next) { if (!cmd->error_string) - packet_write(1, "ok %s\n", - cmd->ref_name); + packet_write(1, "ok %s %s\n", + cmd->ref_name, sha1_to_hex(cmd->new_sha1)); else packet_write(1, "ng %s %s\n", cmd->ref_name, cmd->error_string); -- 1.5.3.6.2040.g97735-dirty ^ permalink raw reply related [flat|nested] 44+ messages in thread
* Re: [PATCH v3] Allow update hooks to update refs on their own 2007-11-28 22:14 ` [PATCH v3] " Steven Grimm @ 2007-11-28 23:03 ` Jeff King 2007-11-28 23:42 ` Junio C Hamano 0 siblings, 1 reply; 44+ messages in thread From: Jeff King @ 2007-11-28 23:03 UTC (permalink / raw) To: Steven Grimm; +Cc: git On Wed, Nov 28, 2007 at 02:14:03PM -0800, Steven Grimm wrote: > @@ -177,7 +179,16 @@ static int receive_status(int in, struct ref *refs) > > line[strlen(line)-1] = '\0'; > refname = line + 3; > - msg = strchr(refname, ' '); > + newsha1_hex = strchr(refname, ' '); > + if (newsha1_hex) { > + *newsha1_hex++ = '\0'; > + if (get_sha1_hex(newsha1_hex, newsha1)) { > + fprintf(stderr, "protocol error: bad sha1 %s\n", > + newsha1_hex); > + newsha1_hex = NULL; > + } > + } > + msg = strchr(newsha1_hex, ' '); > if (msg) > *msg++ = '\0'; Doesn't this always put the first "word" of a response into newsha1_hex? We want to do this only for 'ok' responses; 'ng' responses are already using that space as part of the error message. -Peff ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v3] Allow update hooks to update refs on their own 2007-11-28 23:03 ` Jeff King @ 2007-11-28 23:42 ` Junio C Hamano 2007-11-29 6:44 ` Steven Grimm 0 siblings, 1 reply; 44+ messages in thread From: Junio C Hamano @ 2007-11-28 23:42 UTC (permalink / raw) To: Jeff King; +Cc: Steven Grimm, git Jeff King <peff@peff.net> writes: > On Wed, Nov 28, 2007 at 02:14:03PM -0800, Steven Grimm wrote: > >> @@ -177,7 +179,16 @@ static int receive_status(int in, struct ref *refs) >> >> line[strlen(line)-1] = '\0'; >> refname = line + 3; >> - msg = strchr(refname, ' '); >> + newsha1_hex = strchr(refname, ' '); >> + if (newsha1_hex) { >> + *newsha1_hex++ = '\0'; >> + if (get_sha1_hex(newsha1_hex, newsha1)) { >> + fprintf(stderr, "protocol error: bad sha1 %s\n", >> + newsha1_hex); >> + newsha1_hex = NULL; >> + } >> + } >> + msg = strchr(newsha1_hex, ' '); >> if (msg) >> *msg++ = '\0'; > > Doesn't this always put the first "word" of a response into newsha1_hex? > We want to do this only for 'ok' responses; 'ng' responses are already > using that space as part of the error message. I do not think reporting back the rewritten object name makes much sense nor adds any value; it won't be useful information until you fetch the object. I do not think reporting back _anything_ other than "ok" adds much value at all. Sure, if the update hook did something funky you would get such a report, but the situation is not any different if some warm body is sitting on the other end and building on top of what you pushed immediately he sees any push into the repository, and in such a case your git-push would not get any such reporting anyway. We do not even have to worry about this reporting at all if we do not allow munging the refs in the update hook. In a sense, this patch is creating a problem that does not need to be solved. Perhaps modifying update hook to allow so makes it possible to munge refs while holding a lock, but is it really worth this hassle? Isn't there a better way, I wonder? ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v3] Allow update hooks to update refs on their own 2007-11-28 23:42 ` Junio C Hamano @ 2007-11-29 6:44 ` Steven Grimm 2007-11-30 1:06 ` Junio C Hamano 0 siblings, 1 reply; 44+ messages in thread From: Steven Grimm @ 2007-11-29 6:44 UTC (permalink / raw) To: Junio C Hamano; +Cc: Jeff King, git On Nov 28, 2007, at 3:42 PM, Junio C Hamano wrote: > I do not think reporting back the rewritten object name makes much > sense > nor adds any value; it won't be useful information until you fetch the > object. Right, this was mostly in anticipation of doing an automatic fetch, so that I would avoid fetching anything but the rewritten revisions; if I just fetched the remote ref as normal, then I'd potentially pick up unrelated changes that happened to hit just after my pack was accepted, which wouldn't maintain the "update the tracking ref to point to what I just pushed" semantics. Since it sounds like that's a nonstarter, I agree this part of the patch isn't useful. > I do not think reporting back _anything_ other than "ok" adds much > value > at all. Sure, if the update hook did something funky you would get > such > a report, but the situation is not any different if some warm body is > sitting on the other end and building on top of what you pushed > immediately he sees any push into the repository, and in such a case > your git-push would not get any such reporting anyway. I disagree that it's the same. In this case the updated ref happens as a component of the push operation (which of course includes running update hooks and at the very least looking at their exit codes to see if a change should be rejected), not as a result of some other process that happens to occur at nearly the same time. Reporting back the new ref, at the very least, tells you that it's not useful to update the tracking ref since it's 100% guaranteed to be wrong by the time the push finishes. > We do not even have to worry about this reporting at all if we do not > allow munging the refs in the update hook. In a sense, this patch is > creating a problem that does not need to be solved. Perhaps modifying > update hook to allow so makes it possible to munge refs while > holding a > lock, but is it really worth this hassle? Isn't there a better way, I > wonder? If there is, I'm happy to hear it; for me this patch is a means, not an end. What I actually want is to be able to have a particular set of branches in a particular git repository be as-transparent-as-possible conduits to corresponding branches in an svn repository. I arrived at this approach by following this train of thought: 1. The update hook is the only hook that allows me to reject the push, which I need to do if svn refuses to accept the change. 2. To tell whether svn accepts a change, I need to run git-svn dcommit; thanks to #1, I need to do that from inside the update hook. 3. When I commit, git-svn needs to track that the git revision now corresponds to an svn revision. It does that by modifying the commit message to add its git-svn-id: line. 4. Modifying the commit comment causes the revision's SHA1 to change. 5. Out-of-the-box push thinks a push has failed if the ref's SHA1 changes in the update hook. 6. Therefore push needs to be modified to not do that. If any one of #1-5 wasn't true or was solvable in a different way, then #6 wouldn't be needed. For example, if git-svn kept its mapping of git revisions to svn revisions somewhere else it could leave the commit messages untouched, meaning the SHA1s wouldn't change. -Steve ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v3] Allow update hooks to update refs on their own 2007-11-29 6:44 ` Steven Grimm @ 2007-11-30 1:06 ` Junio C Hamano 2007-12-02 21:22 ` [PATCH v4] " Steven Grimm 0 siblings, 1 reply; 44+ messages in thread From: Junio C Hamano @ 2007-11-30 1:06 UTC (permalink / raw) To: Steven Grimm; +Cc: Jeff King, git Steven Grimm <koreth@midwinter.com> writes: > If any one of #1-5 wasn't true or was solvable in a different way, > then #6 wouldn't be needed. For example, if git-svn kept its mapping > of git revisions to svn revisions somewhere else it could leave the > commit messages untouched, meaning the SHA1s wouldn't change. That does sound like an unfortunate design problem with how git-svn keeps track of the correlation between two systems. But after thinking about this issue a bit more, I think I agree that allowing to munge what was pushed inside update hook may make sense even outside of git-svn context (iow, if this were an ugly workaround for git-svn deficiency then I would be unhappy but I think there are valid use cases). "You push A, I inspect it and may rewrite it to A' but only if A' is reasonable -- otherwise I reject your pushing of A" could be a valid thing to do in other contexts. A stupid example would be an update hook that tries to cleanse what you pushed for whitespace breakage and commit a cleaned-up result only if the result passes the testsuite. ^ permalink raw reply [flat|nested] 44+ messages in thread
* [PATCH v4] Allow update hooks to update refs on their own. 2007-11-30 1:06 ` Junio C Hamano @ 2007-12-02 21:22 ` Steven Grimm 2007-12-02 21:56 ` Junio C Hamano ` (2 more replies) 0 siblings, 3 replies; 44+ messages in thread From: Steven Grimm @ 2007-12-02 21:22 UTC (permalink / raw) To: git This is useful in cases where a hook needs to modify an incoming commit in some way, e.g., fixing whitespace errors, adding an annotation to the commit message, noting the location of output from a profiling tool, or committing to an svn repository using git-svn. Signed-off-by: Steven Grimm <koreth@midwinter.com> --- Since Junio's main objection to this seemed to be the protocol change to bypass the automatic update of the tracking ref in git-send-pack, that code is gone (thus reverting this to the same code change as the initial version!) and I added a section to the git-send-pack manual page describing the automatic tracking ref update behavior, which wasn't documented at all before. Someone please review my terminology there. Documentation/git-receive-pack.txt | 8 +++- Documentation/git-send-pack.txt | 16 ++++++++ receive-pack.c | 70 +++++++++++++++++++++++------------- 3 files changed, 67 insertions(+), 27 deletions(-) diff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt index 2633d94..115ae97 100644 --- a/Documentation/git-receive-pack.txt +++ b/Documentation/git-receive-pack.txt @@ -74,8 +74,12 @@ Note that the hook is called before the refname is updated, so either sha1-old is 0\{40} (meaning there is no such ref yet), or it should match what is recorded in refname. -The hook should exit with non-zero status if it wants to disallow -updating the named ref. Otherwise it should exit with zero. +The hook may optionally choose to update the ref on its own, e.g., +if it needs to modify incoming revisions in some way. If it updates +the ref, it should exit with a status of 100. The hook should exit +with a status between 1 and 99 if it wants to disallow updating the +named ref. Otherwise it should exit with zero, and the ref will be +updated automatically. Successful execution (a zero exit status) of this hook does not ensure the ref will actually be updated, it is only a prerequisite. diff --git a/Documentation/git-send-pack.txt b/Documentation/git-send-pack.txt index a2d9cb6..db64a1b 100644 --- a/Documentation/git-send-pack.txt +++ b/Documentation/git-send-pack.txt @@ -115,6 +115,22 @@ Optionally, a <ref> parameter can be prefixed with a plus '+' sign to disable the fast-forward check only on that ref. +Remote Tracking Refs +-------------------- + +After successfully sending a pack to the remote, 'git-send-pack' +updates the corresponding remote tracking ref in the local repository +to point to the same commit as was just sent to the remote side. In +most cases this eliminates the need to subsequently fetch from the +remote repository since there would be nothing new to fetch. + +If the remote side's update hook modifies the incoming commit +before applying it, the local repository's remote tracking ref will +point at a different commit than the corresponding remote ref (since +the local repository will not have a copy of the modified version). +In that case an explicit fetch will be required. + + Author ------ Written by Linus Torvalds <torvalds@osdl.org> diff --git a/receive-pack.c b/receive-pack.c index fba4cf8..ca906bf 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -18,6 +18,9 @@ static int report_status; static char capabilities[] = " report-status delete-refs "; static int capabilities_sent; +/* Update hook exit code: hook has updated ref on its own */ +#define EXIT_CODE_REF_UPDATED 100 + static int receive_pack_config(const char *var, const char *value) { if (strcmp(var, "receive.denynonfastforwards") == 0) { @@ -70,8 +73,11 @@ static struct command *commands; static const char pre_receive_hook[] = "hooks/pre-receive"; static const char post_receive_hook[] = "hooks/post-receive"; -static int hook_status(int code, const char *hook_name) +static int hook_status(int code, const char *hook_name, int ok_start) { + if (ok_start && -code >= ok_start) + return -code; + switch (code) { case 0: return 0; @@ -121,7 +127,7 @@ static int run_hook(const char *hook_name) code = start_command(&proc); if (code) - return hook_status(code, hook_name); + return hook_status(code, hook_name, 0); for (cmd = commands; cmd; cmd = cmd->next) { if (!cmd->error_string) { size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n", @@ -132,7 +138,7 @@ static int run_hook(const char *hook_name) break; } } - return hook_status(finish_command(&proc), hook_name); + return hook_status(finish_command(&proc), hook_name, 0); } static int run_update_hook(struct command *cmd) @@ -155,7 +161,8 @@ static int run_update_hook(struct command *cmd) proc.no_stdin = 1; proc.stdout_to_stderr = 1; - return hook_status(run_command(&proc), update_hook); + return hook_status(run_command(&proc), update_hook, + EXIT_CODE_REF_UPDATED); } static const char *update(struct command *cmd) @@ -194,32 +201,45 @@ static const char *update(struct command *cmd) return "non-fast forward"; } } - if (run_update_hook(cmd)) { - error("hook declined to update %s", name); - return "hook declined"; - } - - if (is_null_sha1(new_sha1)) { - if (!parse_object(old_sha1)) { - warning ("Allowing deletion of corrupt ref."); - old_sha1 = NULL; + switch (run_update_hook(cmd)) { + case 0: + if (is_null_sha1(new_sha1)) { + if (!parse_object(old_sha1)) { + warning ("Allowing deletion of corrupt ref."); + old_sha1 = NULL; + } + if (delete_ref(name, old_sha1)) { + error("failed to delete %s", name); + return "failed to delete"; + } + fprintf(stderr, "%s: %s -> deleted\n", name, + sha1_to_hex(old_sha1)); } - if (delete_ref(name, old_sha1)) { - error("failed to delete %s", name); - return "failed to delete"; + else { + lock = lock_any_ref_for_update(name, old_sha1, 0); + if (!lock) { + error("failed to lock %s", name); + return "failed to lock"; + } + if (write_ref_sha1(lock, new_sha1, "push")) { + return "failed to write"; /* error() already called */ + } } return NULL; /* good */ - } - else { - lock = lock_any_ref_for_update(name, old_sha1, 0); - if (!lock) { - error("failed to lock %s", name); - return "failed to lock"; - } - if (write_ref_sha1(lock, new_sha1, "push")) { - return "failed to write"; /* error() already called */ + + case EXIT_CODE_REF_UPDATED: + /* hook has taken care of updating ref, which means it + might be a different revision than we think. */ + if (! resolve_ref(name, new_sha1, 1, NULL)) { + error("can't resolve ref %s after hook updated it", + name); + return "ref not resolvable"; } return NULL; /* good */ + + default: + error("hook declined to update %s", name); + return "hook declined"; } } -- 1.5.3.6.2040.g97735-dirty ^ permalink raw reply related [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-02 21:22 ` [PATCH v4] " Steven Grimm @ 2007-12-02 21:56 ` Junio C Hamano 2007-12-03 2:13 ` Jeff King 2007-12-03 4:01 ` Shawn O. Pearce 2 siblings, 0 replies; 44+ messages in thread From: Junio C Hamano @ 2007-12-02 21:56 UTC (permalink / raw) To: Steven Grimm; +Cc: git Steven Grimm <koreth@midwinter.com> writes: > +The hook may optionally choose to update the ref on its own, e.g., > +if it needs to modify incoming revisions in some way. If it updates > +the ref, it should exit with a status of 100. The hook should exit > +with a status between 1 and 99 if it wants to disallow updating the > +named ref. Otherwise it should exit with zero, and the ref will be > +updated automatically. This makes one wonder what happens if it returns 101, iow if there is difference between returning 99 and 101, and if so why. > +Remote Tracking Refs > +-------------------- > + > +After successfully sending a pack to the remote, 'git-send-pack' > +updates the corresponding remote tracking ref in the local repository > +to point to the same commit as was just sent to the remote side. In > +most cases this eliminates the need to subsequently fetch from the > +remote repository since there would be nothing new to fetch. Micronit. The above is all "if exists". Not everybody pushes to somewhere he uses tracking with. > @@ -70,8 +73,11 @@ static struct command *commands; > static const char pre_receive_hook[] = "hooks/pre-receive"; > static const char post_receive_hook[] = "hooks/post-receive"; > > -static int hook_status(int code, const char *hook_name) > +static int hook_status(int code, const char *hook_name, int ok_start) > { > + if (ok_start && -code >= ok_start) > + return -code; > + I've always been puzzled by this "ok_start" parameter from the very beginning edition of your patch. It is not like "if this is true, then it is ok to run the hook but otherwise do not run". In layman's terms, what does the parameter mean? Maybe the variable is misnamed and not expressing the concept well enough. Maybe the concept itself is muddy and hard to understand. I cannot tell. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-02 21:22 ` [PATCH v4] " Steven Grimm 2007-12-02 21:56 ` Junio C Hamano @ 2007-12-03 2:13 ` Jeff King 2007-12-03 2:16 ` Junio C Hamano 2007-12-03 4:01 ` Shawn O. Pearce 2 siblings, 1 reply; 44+ messages in thread From: Jeff King @ 2007-12-03 2:13 UTC (permalink / raw) To: Steven Grimm; +Cc: git On Sun, Dec 02, 2007 at 01:22:24PM -0800, Steven Grimm wrote: > Since Junio's main objection to this seemed to be the protocol > change to bypass the automatic update of the tracking ref in > git-send-pack, that code is gone (thus reverting this to the > same code change as the initial version!) and I added a section > to the git-send-pack manual page describing the automatic > tracking ref update behavior, which wasn't documented at all > before. Someone please review my terminology there. I am dubious of the usefulness of passing back the new commit id, but an "ok, but btw I changed your commit" status from receive-pack seems like it would be useful, for two reasons: - it can be displayed differently, so the user is reminded to do a fetch afterwards - we can avoid updating the tracking ref, which makes it less likely to result in a non-fast forward fetch next time. For example, consider: 1. The remote master and my origin/master are at A. 2. I make a commit B on top of A. 3. I push B to remote, who rewrites it to B' on top of A. At the same time, I move my origin/master to B. 4. I fetch, and get non-ff going from B to B'. If I had never written anything to my origin/master, it would be a fast forward. And obviously git handles it just fine, but it is more useful to the user during the next fetch to see A..B rather than B'...B. -Peff ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-03 2:13 ` Jeff King @ 2007-12-03 2:16 ` Junio C Hamano 2007-12-03 3:45 ` Junio C Hamano 2007-12-05 22:14 ` Steven Grimm 0 siblings, 2 replies; 44+ messages in thread From: Junio C Hamano @ 2007-12-03 2:16 UTC (permalink / raw) To: Jeff King; +Cc: Steven Grimm, git Jeff King <peff@peff.net> writes: > ..., but an > "ok, but btw I changed your commit" status from receive-pack seems like > it would be useful, for two reasons: > > - it can be displayed differently, so the user is reminded to do a > fetch afterwards > - we can avoid updating the tracking ref, which makes it less likely > to result in a non-fast forward fetch next time. For example, > consider: > > 1. The remote master and my origin/master are at A. > 2. I make a commit B on top of A. > 3. I push B to remote, who rewrites it to B' on top of A. At the > same time, I move my origin/master to B. > 4. I fetch, and get non-ff going from B to B'. > > If I had never written anything to my origin/master, it would be a > fast forward. And obviously git handles it just fine, but it is more > useful to the user during the next fetch to see A..B rather than > B'...B. Sensible argument. I stand corrected. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-03 2:16 ` Junio C Hamano @ 2007-12-03 3:45 ` Junio C Hamano 2007-12-05 22:14 ` Steven Grimm 1 sibling, 0 replies; 44+ messages in thread From: Junio C Hamano @ 2007-12-03 3:45 UTC (permalink / raw) To: Jeff King; +Cc: Steven Grimm, git Junio C Hamano <gitster@pobox.com> writes: > Jeff King <peff@peff.net> writes: > >> ..., but an >> "ok, but btw I changed your commit" status from receive-pack seems like >> it would be useful, for two reasons: >> >> - it can be displayed differently, so the user is reminded to do a >> fetch afterwards >> - we can avoid updating the tracking ref, which makes it less likely >> to result in a non-fast forward fetch next time. For example, >> consider: >> >> 1. The remote master and my origin/master are at A. >> 2. I make a commit B on top of A. >> 3. I push B to remote, who rewrites it to B' on top of A. At the >> same time, I move my origin/master to B. >> 4. I fetch, and get non-ff going from B to B'. >> >> If I had never written anything to my origin/master, it would be a >> fast forward. And obviously git handles it just fine, but it is more >> useful to the user during the next fetch to see A..B rather than >> B'...B. > > Sensible argument. I stand corrected. Having said that, I think the workflow that this "letting update hook munge" patch supports has a bit more implications for the people who interact with it. The pusher will need to force fetch the result, but after that he needs to discard his own commit and replace it with whatever the hook did. The question is how much to discard, and how to reconstruct the changes since the last push that was made on top of the commit that was pushed. Maybe the push pushed out a string of five pearls and the hook may have rewritten only the tip, or all of them. You may have built a few commits on top since then. If what you pushed out contained merges and the hook rewrote it, you would need to potentially replay such a merge that was rewritten by the hook. This is all the same with a workflow that deals with a branch that is advertised to constantly rewound and rebased, and the common approach is to use "fetch + rebase" (see recent discussion between Nico and Bruce). So this patch is not creating a new problem (iow, I am not mentioning this as the reason to reject this patch), but I thought I should bring it up so that people know what they are doing. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-03 2:16 ` Junio C Hamano 2007-12-03 3:45 ` Junio C Hamano @ 2007-12-05 22:14 ` Steven Grimm 2007-12-05 22:19 ` Junio C Hamano 1 sibling, 1 reply; 44+ messages in thread From: Steven Grimm @ 2007-12-05 22:14 UTC (permalink / raw) To: Junio C Hamano; +Cc: Jeff King, git On Dec 2, 2007, at 6:16 PM, Junio C Hamano wrote: >> ..., but an >> "ok, but btw I changed your commit" status from receive-pack seems >> like >> it would be useful, for two reasons: > Sensible argument. I stand corrected. If we want that status in principle, I'd argue that sending down the updated commit SHA1 is actually the right way to indicate it, because it gives the client all the information it needs to make an intelligent choice about what to do next. If you don't transmit the modified SHA1, the client will have to do another fetch to find out what rewriting was done by the server, and if another push happened in the meantime, the client will have to basically guess about which commits correspond to the ones it pushed. I'm going to have to modify the "ok" line for this either way, and it's not like the extra 39 bytes (for sending a hex SHA1 instead of a one-character status indicator) is going to hurt in any but the narrowest corner cases. But if people really object to that, I will add a simple flag to the "ok" line. -Steve ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-05 22:14 ` Steven Grimm @ 2007-12-05 22:19 ` Junio C Hamano 2007-12-05 22:29 ` Junio C Hamano 2007-12-06 5:57 ` Jeff King 0 siblings, 2 replies; 44+ messages in thread From: Junio C Hamano @ 2007-12-05 22:19 UTC (permalink / raw) To: Steven Grimm; +Cc: Jeff King, git Steven Grimm <koreth@midwinter.com> writes: > On Dec 2, 2007, at 6:16 PM, Junio C Hamano wrote: >>> ..., but an >>> "ok, but btw I changed your commit" status from receive-pack seems >>> like >>> it would be useful, for two reasons: >> Sensible argument. I stand corrected. > > If we want that status in principle, I'd argue that sending down the > updated commit SHA1 is actually the right way to indicate it, because > it gives the client all the information it needs to make an > intelligent choice about what to do next. If you don't transmit the > modified SHA1, the client will have to do another fetch to find out > what rewriting was done by the server, and if another push happened in > the meantime, the client will have to basically guess about which > commits correspond to the ones it pushed. Ok, but the output from fetch is meant to be human readable and we do not promise parsability, so if we go this route (which I think you made a sensible argument for) we would need a hook on the pushing end to act on this (perhaps record the correspondence of pushed and rewritten sha1 somewhere for the hook's own use). ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-05 22:19 ` Junio C Hamano @ 2007-12-05 22:29 ` Junio C Hamano 2007-12-06 5:57 ` Jeff King 1 sibling, 0 replies; 44+ messages in thread From: Junio C Hamano @ 2007-12-05 22:29 UTC (permalink / raw) To: Steven Grimm; +Cc: Jeff King, git Junio C Hamano <gitster@pobox.com> writes: > Ok, but the output from fetch is meant to be human readable and we do > not promise parsability, so if we go this route (which I think you made s/parsability/machine &/; > a sensible argument for) we would need a hook on the pushing end to act > on this (perhaps record the correspondence of pushed and rewritten sha1 > somewhere for the hook's own use). s/on this/& information/; s/own use/& in machine readable way/; ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-05 22:19 ` Junio C Hamano 2007-12-05 22:29 ` Junio C Hamano @ 2007-12-06 5:57 ` Jeff King 2007-12-06 6:30 ` Junio C Hamano 1 sibling, 1 reply; 44+ messages in thread From: Jeff King @ 2007-12-06 5:57 UTC (permalink / raw) To: Junio C Hamano; +Cc: Steven Grimm, git On Wed, Dec 05, 2007 at 02:19:58PM -0800, Junio C Hamano wrote: > > what rewriting was done by the server, and if another push happened in > > the meantime, the client will have to basically guess about which > > commits correspond to the ones it pushed. > > Ok, but the output from fetch is meant to be human readable and we do > not promise parsability, so if we go this route (which I think you made > a sensible argument for) we would need a hook on the pushing end to act > on this (perhaps record the correspondence of pushed and rewritten sha1 > somewhere for the hook's own use). I am not clear on what you mean. Are you saying that the send-pack code should _not_ recognize the "ok, but I rewrote your commit" status? Because that is how we will avoid updating the tracking ref, which I think is a good goal. Or are you saying "it's ok to understand the 'ok, but...' response and not update the tracking ref, but pulling the new hash from the message is up to a hook on the pushing side"? Which I think it reasonable. Or alternatively, "there should be a hook on the pushing side which is allowed to set the ref status to 'ok, but don't bother updating the tracking ref' or 'ok, but here is the actual thing to put in the tracking ref'"? Which is also fine by me. -Peff ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-06 5:57 ` Jeff King @ 2007-12-06 6:30 ` Junio C Hamano 2007-12-06 6:36 ` Jeff King 0 siblings, 1 reply; 44+ messages in thread From: Junio C Hamano @ 2007-12-06 6:30 UTC (permalink / raw) To: Jeff King; +Cc: Steven Grimm, git Jeff King <peff@peff.net> writes: > On Wed, Dec 05, 2007 at 02:19:58PM -0800, Junio C Hamano wrote: > >> > what rewriting was done by the server, and if another push happened in >> > the meantime, the client will have to basically guess about which >> > commits correspond to the ones it pushed. >> >> Ok, but the output from fetch is meant to be human readable and we do >> not promise parsability, so if we go this route (which I think you made >> a sensible argument for) we would need a hook on the pushing end to act >> on this (perhaps record the correspondence of pushed and rewritten sha1 >> somewhere for the hook's own use). > > I am not clear on what you mean. Are you saying that the send-pack code > should _not_ recognize the "ok, but I rewrote your commit" status? > Because that is how we will avoid updating the tracking ref, which I > think is a good goal. > > Or are you saying "it's ok to understand the 'ok, but...' response and > not update the tracking ref, but pulling the new hash from the message > is up to a hook on the pushing side"? Which I think it reasonable. > > Or alternatively, "there should be a hook on the pushing side which is > allowed to set the ref status to 'ok, but don't bother updating the > tracking ref' or 'ok, but here is the actual thing to put in the > tracking ref'"? Which is also fine by me. What I meant in response to what I thought Steven was talking about was this. * With Steven's patch, the sending side needs to expect what it pushes to be rewritten. If it starts with this history: ---o---o---o---Y---o---o---X where Y is what its remote tracking branch points at for the corresponding branch, three commits were built locally since the last fetch, and the sender pushes X. * Then the receiving end rewrites the history, making the history into this: o'--o'--X' / ---o---o---o---Y---o---o---X * Before the next fetch, the sending side can continue building on top of X, leading to this: o'--o'--X' / ---o---o---o---Y---o---o---X---o---Z * Similarly other people push into the same remote, get their commits rewritten and remote side's history becomes like this (but the original sender does not know about the upper history at all yet). o'--o'--X'--o'--o'--W' / ---o---o---o---Y---o---o---X---o---Z * Then the original sender fetches from the remote, now the tracking branch points at W' (it previously pointed at Y). You would want to rebase your work since the last push on top of that tracking branch. The rebase would be "rebase --onto W' X Z", so it is not strictly necessary to keep the fact that X corresponds to X', but somehow I thought it was necessary, and Steven's message was hinting about that: > If we want that status in principle, I'd argue that sending down the > updated commit SHA1 is actually the right way to indicate it, because > it gives the client all the information it needs to make an > intelligent choice about what to do next. If you don't transmit the > modified SHA1, the client will have to do another fetch to find out > what rewriting was done by the server, and if another push happened in > the meantime, the client will have to basically guess about which > commits correspond to the ones it pushed. (notice the last part). So if we want to transmit minimum amount of information, we can just send a bit ("the ref was rewritten") back to send-pack without telling it what X' is (but it would not hurt to send it back either). With that one bit of information, send-pack can refrain from updating tracking ref from Y to X. In the above scenario I illustrated, it turns out that getting correspondence between X and X' is not strictly necessary to perform a rebase later, but maybe there is some other scenario that keeping track of that information would be helpful. In such a case, a hook on the send-pack end (which currently we do not have) can be called with X and X' as parameters (and perhaps the name of the ref and the corresponding tracking ref) to do whatever it wants to do with that information. Even if we do not send X' back but just one bit, having that hook would probably be needed so that sender can record "I've pushed up to X" and perhaps "now I cannot push out Z until I rebase" after receiving "push was accepted but rewritten" bit. This is all handwaving --- I suspect for this to really work, send-pack might need a pre-send-pack hook that pays attention to such "now I cannot push out Z until I rebase" information the previous round of push may have left and declines to push. Of course, the receiving end would would probably refuse such a push because it is not a fast-forward. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-06 6:30 ` Junio C Hamano @ 2007-12-06 6:36 ` Jeff King 2007-12-06 7:50 ` Steven Grimm 0 siblings, 1 reply; 44+ messages in thread From: Jeff King @ 2007-12-06 6:36 UTC (permalink / raw) To: Junio C Hamano; +Cc: Steven Grimm, git On Wed, Dec 05, 2007 at 10:30:45PM -0800, Junio C Hamano wrote: > The rebase would be "rebase --onto W' X Z", so it is not strictly > necessary to keep the fact that X corresponds to X', but somehow I > thought it was necessary, and Steven's message was hinting about that: > > > If we want that status in principle, I'd argue that sending down the > > updated commit SHA1 is actually the right way to indicate it, because > > it gives the client all the information it needs to make an > > intelligent choice about what to do next. If you don't transmit the > > modified SHA1, the client will have to do another fetch to find out > > what rewriting was done by the server, and if another push happened in > > the meantime, the client will have to basically guess about which > > commits correspond to the ones it pushed. > > (notice the last part). > > So if we want to transmit minimum amount of information, we can just > send a bit ("the ref was rewritten") back to send-pack without telling > it what X' is (but it would not hurt to send it back either). With that > one bit of information, send-pack can refrain from updating tracking ref > from Y to X. Ah, I thought his argument was "we have to send back a bit, so why not just send the hash we made for informational purposes? It doesn't hurt, and maybe we can make use of it later." I was assuming that we were interested _only_ in fixing the send-pack issues at this time, and that the rebasing or merging part of the workflow would be figured out later. But it is probably sensible to consider the whole workflow to see what is necessary at each step. -Peff ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-06 6:36 ` Jeff King @ 2007-12-06 7:50 ` Steven Grimm 0 siblings, 0 replies; 44+ messages in thread From: Steven Grimm @ 2007-12-06 7:50 UTC (permalink / raw) To: Jeff King; +Cc: Junio C Hamano, git On Dec 5, 2007, at 10:36 PM, Jeff King wrote: > Ah, I thought his argument was "we have to send back a bit, so why not > just send the hash we made for informational purposes? It doesn't > hurt, > and maybe we can make use of it later." Yeah, that was more or less my thinking. Keep it simple for now, but it seems like that information is bound to be useful at some point. In particular, if you don't send it down, it's really difficult to unambiguously get back after the fact (given that a fetch might contain subsequent revisions unrelated to yours.) My v3 patch (which I will combine with a modified form of the documentation update now that it sounds like transmitting the SHA1 isn't objectionable) actually sent it down twice: once in the protocol message and once in the human-readable push status report. -Steve ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-02 21:22 ` [PATCH v4] " Steven Grimm 2007-12-02 21:56 ` Junio C Hamano 2007-12-03 2:13 ` Jeff King @ 2007-12-03 4:01 ` Shawn O. Pearce 2007-12-03 5:25 ` Junio C Hamano 2007-12-03 11:47 ` Johannes Schindelin 2 siblings, 2 replies; 44+ messages in thread From: Shawn O. Pearce @ 2007-12-03 4:01 UTC (permalink / raw) To: Steven Grimm; +Cc: git, Junio C Hamano Steven Grimm <koreth@midwinter.com> wrote: > This is useful in cases where a hook needs to modify an incoming commit > in some way, e.g., fixing whitespace errors, adding an annotation to > the commit message, noting the location of output from a profiling tool, > or committing to an svn repository using git-svn. ... > +/* Update hook exit code: hook has updated ref on its own */ > +#define EXIT_CODE_REF_UPDATED 100 Hmm. I would actually rather move the ref locking to before we run the update hook, so the ref is locked *while* the hook executes. If we ran a hook and it exited 0 then re-read the ref to see if the value differs from old_sha1; if it does then we can assume the update hook took care of the update. If the value is still old_sha1 then we know the hook didn't do the update and we need to do it for the hook. This probably requires exporting the name of the ref we currently have locked in an environment variable (and teach lockfile.c it) so we can effectively do recursive locking. That way the update hook can still use git-update-ref to change the ref safely. The advantage of this approach is the hook programmer doesn't need to implement their own locking scheme and we also don't make a special case exit code where there wasn't one before. -- Shawn. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-03 4:01 ` Shawn O. Pearce @ 2007-12-03 5:25 ` Junio C Hamano 2007-12-04 1:55 ` Shawn O. Pearce 2007-12-03 11:47 ` Johannes Schindelin 1 sibling, 1 reply; 44+ messages in thread From: Junio C Hamano @ 2007-12-03 5:25 UTC (permalink / raw) To: Shawn O. Pearce; +Cc: Steven Grimm, git "Shawn O. Pearce" <spearce@spearce.org> writes: > This probably requires exporting the name of the ref we currently > have locked in an environment variable (and teach lockfile.c it) > so we can effectively do recursive locking. That way the update > hook can still use git-update-ref to change the ref safely. Heh, I like that, although I suspect getting this right would mean the topic should be post 1.5.4 (which I do not mind). ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-03 5:25 ` Junio C Hamano @ 2007-12-04 1:55 ` Shawn O. Pearce 0 siblings, 0 replies; 44+ messages in thread From: Shawn O. Pearce @ 2007-12-04 1:55 UTC (permalink / raw) To: Junio C Hamano; +Cc: Steven Grimm, git Junio C Hamano <gitster@pobox.com> wrote: > "Shawn O. Pearce" <spearce@spearce.org> writes: > > > This probably requires exporting the name of the ref we currently > > have locked in an environment variable (and teach lockfile.c it) > > so we can effectively do recursive locking. That way the update > > hook can still use git-update-ref to change the ref safely. > > Heh, I like that, although I suspect getting this right would mean the > topic should be post 1.5.4 (which I do not mind). Yea, most likely. Also I won't have any time in the near future to work on this implementation myself. I threw the idea out there in case someone else can find the time. I'm willing to do the work myself as I think its the right approach to use here for this update hook change, but I just don't see myself getting to it anytime before say Christmas... Its slightly more involved then what Steven originally proposed, but I think it solves the problem better and gives us more room for future improvements where we may want/need something such as a recursive ref locking. -- Shawn. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-03 4:01 ` Shawn O. Pearce 2007-12-03 5:25 ` Junio C Hamano @ 2007-12-03 11:47 ` Johannes Schindelin 2007-12-04 1:51 ` Shawn O. Pearce 1 sibling, 1 reply; 44+ messages in thread From: Johannes Schindelin @ 2007-12-03 11:47 UTC (permalink / raw) To: Shawn O. Pearce; +Cc: Steven Grimm, git, Junio C Hamano Hi, On Sun, 2 Dec 2007, Shawn O. Pearce wrote: > Steven Grimm <koreth@midwinter.com> wrote: > > This is useful in cases where a hook needs to modify an incoming commit > > in some way, e.g., fixing whitespace errors, adding an annotation to > > the commit message, noting the location of output from a profiling tool, > > or committing to an svn repository using git-svn. > ... > > +/* Update hook exit code: hook has updated ref on its own */ > > +#define EXIT_CODE_REF_UPDATED 100 > > Hmm. I would actually rather move the ref locking to before we run > the update hook, so the ref is locked *while* the hook executes. Would that not mean that you cannot use update-ref to update the ref, since that wants to use the same lock? Ciao, Dscho ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-03 11:47 ` Johannes Schindelin @ 2007-12-04 1:51 ` Shawn O. Pearce 2007-12-04 2:12 ` Johannes Schindelin 0 siblings, 1 reply; 44+ messages in thread From: Shawn O. Pearce @ 2007-12-04 1:51 UTC (permalink / raw) To: Johannes Schindelin; +Cc: Steven Grimm, git, Junio C Hamano Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote: > On Sun, 2 Dec 2007, Shawn O. Pearce wrote: > > > Steven Grimm <koreth@midwinter.com> wrote: > > > This is useful in cases where a hook needs to modify an incoming commit > > > in some way, e.g., fixing whitespace errors, adding an annotation to > > > the commit message, noting the location of output from a profiling tool, > > > or committing to an svn repository using git-svn. > > ... > > > +/* Update hook exit code: hook has updated ref on its own */ > > > +#define EXIT_CODE_REF_UPDATED 100 > > > > Hmm. I would actually rather move the ref locking to before we run > > the update hook, so the ref is locked *while* the hook executes. > > Would that not mean that you cannot use update-ref to update the ref, > since that wants to use the same lock? You failed to quote the part of my email where I talked about how we set an evironment variable to pass a hint to lockfile.c running within the git-update-ref subprocess to instruct it to perform a different style of locking, one that would work as a "recursive" lock. Such a recursive lock could be useful for a whole lot more than just the update hook. But it would at least allow the update hook to use git-update-ref to safely change the ref, without receive-pack losing its own lock on the ref. -- Shawn. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-04 1:51 ` Shawn O. Pearce @ 2007-12-04 2:12 ` Johannes Schindelin 2007-12-04 2:20 ` Shawn O. Pearce 0 siblings, 1 reply; 44+ messages in thread From: Johannes Schindelin @ 2007-12-04 2:12 UTC (permalink / raw) To: Shawn O. Pearce; +Cc: Steven Grimm, git, Junio C Hamano Hi, On Mon, 3 Dec 2007, Shawn O. Pearce wrote: > Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote: > > On Sun, 2 Dec 2007, Shawn O. Pearce wrote: > > > > > Steven Grimm <koreth@midwinter.com> wrote: > > > > This is useful in cases where a hook needs to modify an incoming commit > > > > in some way, e.g., fixing whitespace errors, adding an annotation to > > > > the commit message, noting the location of output from a profiling tool, > > > > or committing to an svn repository using git-svn. > > > ... > > > > +/* Update hook exit code: hook has updated ref on its own */ > > > > +#define EXIT_CODE_REF_UPDATED 100 > > > > > > Hmm. I would actually rather move the ref locking to before we run > > > the update hook, so the ref is locked *while* the hook executes. > > > > Would that not mean that you cannot use update-ref to update the ref, > > since that wants to use the same lock? > > You failed to quote the part of my email where I talked about how > we set an evironment variable to pass a hint to lockfile.c running > within the git-update-ref subprocess to instruct it to perform a > different style of locking, one that would work as a "recursive" > lock. > > Such a recursive lock could be useful for a whole lot more than just > the update hook. But it would at least allow the update hook to > use git-update-ref to safely change the ref, without receive-pack > losing its own lock on the ref. Indeed, I even failed to read it fully ;-) What do you propose, though? <filename>.lock.<n>? Ciao, Dscho ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-04 2:12 ` Johannes Schindelin @ 2007-12-04 2:20 ` Shawn O. Pearce 2007-12-04 2:25 ` Johannes Schindelin 0 siblings, 1 reply; 44+ messages in thread From: Shawn O. Pearce @ 2007-12-04 2:20 UTC (permalink / raw) To: Johannes Schindelin; +Cc: Steven Grimm, git, Junio C Hamano Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote: > On Mon, 3 Dec 2007, Shawn O. Pearce wrote: > > You failed to quote the part of my email where I talked about how > > we set an evironment variable to pass a hint to lockfile.c running > > within the git-update-ref subprocess to instruct it to perform a > > different style of locking, one that would work as a "recursive" > > lock. > > > > Such a recursive lock could be useful for a whole lot more than just > > the update hook. But it would at least allow the update hook to > > use git-update-ref to safely change the ref, without receive-pack > > losing its own lock on the ref. > > Indeed, I even failed to read it fully ;-) > > What do you propose, though? <filename>.lock.<n>? Sure. :-) I was also hand-waving. Hoping someone else would fill in the magic details. Actually <n> wouldn't be so bad. We could do something like: GIT_INHERITED_LOCKS="<ref> <depth> <ref> <depth> ..." where <ref> is a ref name (which cannot contain spaces, even though some people seem to forget that rule) and <depth> is the number of times it has been locked already. <depth> of 0 is the current ".lock" file. So the first lock taken out by receive-pack would be setting: GIT_INHERITED_LOCKS="refs/heads/master 0" and another lock on the same ref by a subprocess would then update it to: GIT_INHERITED_LOCKS="refs/heads/master 1" etc... </hand-waving> -- Shawn. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-04 2:20 ` Shawn O. Pearce @ 2007-12-04 2:25 ` Johannes Schindelin 2007-12-04 2:33 ` Steven Grimm 2007-12-04 2:34 ` Shawn O. Pearce 0 siblings, 2 replies; 44+ messages in thread From: Johannes Schindelin @ 2007-12-04 2:25 UTC (permalink / raw) To: Shawn O. Pearce; +Cc: Steven Grimm, git, Junio C Hamano Hi, On Mon, 3 Dec 2007, Shawn O. Pearce wrote: > Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote: > > On Mon, 3 Dec 2007, Shawn O. Pearce wrote: > > > You failed to quote the part of my email where I talked about how > > > we set an evironment variable to pass a hint to lockfile.c running > > > within the git-update-ref subprocess to instruct it to perform a > > > different style of locking, one that would work as a "recursive" > > > lock. > > > > > > Such a recursive lock could be useful for a whole lot more than just > > > the update hook. But it would at least allow the update hook to > > > use git-update-ref to safely change the ref, without receive-pack > > > losing its own lock on the ref. > > > > Indeed, I even failed to read it fully ;-) > > > > What do you propose, though? <filename>.lock.<n>? > > Sure. :-) > > I was also hand-waving. Hoping someone else would fill in the > magic details. > > Actually <n> wouldn't be so bad. We could do something like: > > GIT_INHERITED_LOCKS="<ref> <depth> <ref> <depth> ..." I am somewhat wary of using environment variables in that context, since the variables could leak to subprocesses, or (even worse), they could be set inadvertently by the user or other scripts. Ciao, Dscho ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-04 2:25 ` Johannes Schindelin @ 2007-12-04 2:33 ` Steven Grimm 2007-12-04 2:34 ` Shawn O. Pearce 1 sibling, 0 replies; 44+ messages in thread From: Steven Grimm @ 2007-12-04 2:33 UTC (permalink / raw) To: Johannes Schindelin; +Cc: Shawn O. Pearce, git, Junio C Hamano On Dec 3, 2007, at 6:25 PM, Johannes Schindelin wrote: > I am somewhat wary of using environment variables in that context, > since > the variables could leak to subprocesses, or (even worse), they > could be > set inadvertently by the user or other scripts. Agreed on the inadvertent setting, but isn't leaking to subprocesses the whole point of the exercise here? -Steve ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4] Allow update hooks to update refs on their own. 2007-12-04 2:25 ` Johannes Schindelin 2007-12-04 2:33 ` Steven Grimm @ 2007-12-04 2:34 ` Shawn O. Pearce 1 sibling, 0 replies; 44+ messages in thread From: Shawn O. Pearce @ 2007-12-04 2:34 UTC (permalink / raw) To: Johannes Schindelin; +Cc: Steven Grimm, git, Junio C Hamano Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote: > On Mon, 3 Dec 2007, Shawn O. Pearce wrote: > > Actually <n> wouldn't be so bad. We could do something like: > > > > GIT_INHERITED_LOCKS="<ref> <depth> <ref> <depth> ..." > > I am somewhat wary of using environment variables in that context, since > the variables could leak to subprocesses, or (even worse), they could be > set inadvertently by the user or other scripts. Sure. But as bad as it is, its still more secure than the "repository of record" that my day-job uses for its source code tree (no, it doesn't use Git, and I wish it was as good as Visual Source Suck). </bad-joke> I'd suggest also using something like getppid() to check the pid against a pid in the env, and *gasp* maybe do a SHA-1 hash in there or something to make it challening enough to fake that the average user won't set it unless they really understand what they are doing. -- Shawn. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH] Allow update hooks to update refs on their own 2007-11-28 19:49 ` Jeff King 2007-11-28 20:16 ` Steven Grimm @ 2007-11-28 21:49 ` Junio C Hamano 2007-11-28 22:37 ` Jeff King 1 sibling, 1 reply; 44+ messages in thread From: Junio C Hamano @ 2007-11-28 21:49 UTC (permalink / raw) To: Steven Grimm; +Cc: Jeff King, git Jeff King <peff@peff.net> writes: > Hrm, this is going to have nasty conflicts with 'next', which already > does the remote ref matching. I think the best way to implement this > would probably be on top of the jk/send-pack topic in next, and add a > new REF_STATUS_REMOTE_CHANGED status type. I think Jeff is referring to sp/refspec-match (605b4978). I still have doubts about having this in the update hook, as the hook is about accepting or refusing and has never been about rewriting. If the implementation of the svn hook were to check if you can rebase cleanly in the update hook without actually rewriting the refs, and then to perform the real update of the refs in post-receive or post-update hook, that would feel much cleaner. But the end result would be the same as you rewrote the refs inside the update hook like your patch does, so maybe I am worrying about conceptual cleanliness too much, needlessly. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH] Allow update hooks to update refs on their own 2007-11-28 21:49 ` [PATCH] " Junio C Hamano @ 2007-11-28 22:37 ` Jeff King 0 siblings, 0 replies; 44+ messages in thread From: Jeff King @ 2007-11-28 22:37 UTC (permalink / raw) To: Junio C Hamano; +Cc: Steven Grimm, git On Wed, Nov 28, 2007 at 01:49:42PM -0800, Junio C Hamano wrote: > > Hrm, this is going to have nasty conflicts with 'next', which already > > does the remote ref matching. I think the best way to implement this > > would probably be on top of the jk/send-pack topic in next, and add a > > new REF_STATUS_REMOTE_CHANGED status type. > > I think Jeff is referring to sp/refspec-match (605b4978). No, I was actually referring to the jk/send-pack topic. I had thought it graduated to master, but since Steven's work was based on a much older version (e.g., with send-pack.c rather than builtin-send-pack.c), I assumed it had not graduated and didn't confirm. So let me amend my comment to "base this on a more recent master...". -Peff ^ permalink raw reply [flat|nested] 44+ messages in thread
end of thread, other threads:[~2007-12-06 7:51 UTC | newest] Thread overview: 44+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2007-11-27 21:17 [PATCH] Allow update hooks to update refs on their own Steven Grimm 2007-11-27 21:21 ` Jakub Narebski 2007-11-27 21:23 ` Steven Grimm 2007-11-28 1:19 ` Junio C Hamano 2007-11-28 2:40 ` Steven Grimm 2007-11-28 3:25 ` Daniel Barkalow 2007-11-28 3:49 ` Junio C Hamano 2007-11-28 5:20 ` Steven Grimm 2007-11-28 16:10 ` Jeff King 2007-11-28 19:00 ` Junio C Hamano 2007-11-28 19:41 ` Steven Grimm 2007-11-28 19:49 ` Jeff King 2007-11-28 20:16 ` Steven Grimm 2007-11-28 20:22 ` Jeff King 2007-11-28 22:01 ` Junio C Hamano 2007-11-28 22:14 ` [PATCH v3] " Steven Grimm 2007-11-28 23:03 ` Jeff King 2007-11-28 23:42 ` Junio C Hamano 2007-11-29 6:44 ` Steven Grimm 2007-11-30 1:06 ` Junio C Hamano 2007-12-02 21:22 ` [PATCH v4] " Steven Grimm 2007-12-02 21:56 ` Junio C Hamano 2007-12-03 2:13 ` Jeff King 2007-12-03 2:16 ` Junio C Hamano 2007-12-03 3:45 ` Junio C Hamano 2007-12-05 22:14 ` Steven Grimm 2007-12-05 22:19 ` Junio C Hamano 2007-12-05 22:29 ` Junio C Hamano 2007-12-06 5:57 ` Jeff King 2007-12-06 6:30 ` Junio C Hamano 2007-12-06 6:36 ` Jeff King 2007-12-06 7:50 ` Steven Grimm 2007-12-03 4:01 ` Shawn O. Pearce 2007-12-03 5:25 ` Junio C Hamano 2007-12-04 1:55 ` Shawn O. Pearce 2007-12-03 11:47 ` Johannes Schindelin 2007-12-04 1:51 ` Shawn O. Pearce 2007-12-04 2:12 ` Johannes Schindelin 2007-12-04 2:20 ` Shawn O. Pearce 2007-12-04 2:25 ` Johannes Schindelin 2007-12-04 2:33 ` Steven Grimm 2007-12-04 2:34 ` Shawn O. Pearce 2007-11-28 21:49 ` [PATCH] " Junio C Hamano 2007-11-28 22:37 ` Jeff King
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).