From: Steven Grimm <koreth@midwinter.com>
To: git@vger.kernel.org
Subject: [PATCH v3] Allow update hooks to update refs on their own
Date: Wed, 28 Nov 2007 14:14:03 -0800 [thread overview]
Message-ID: <20071128221403.GA3256@midwinter.com> (raw)
In-Reply-To: <C1321BD5-8F6B-47F9-9BDB-C2BF819D6F17@midwinter.com>
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
next prev parent reply other threads:[~2007-11-28 22:14 UTC|newest]
Thread overview: 44+ messages / expand[flat|nested] mbox.gz Atom feed top
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 ` Steven Grimm [this message]
2007-11-28 23:03 ` [PATCH v3] " 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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20071128221403.GA3256@midwinter.com \
--to=koreth@midwinter.com \
--cc=git@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).