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