From: Joey Hess <joey@kitenet.net>
To: git@vger.kernel.org
Cc: Joey Hess <joey@kitenet.net>
Subject: [PATCH 3/3] add tweak-fetch hook
Date: Thu, 29 Dec 2011 21:07:20 -0400 [thread overview]
Message-ID: <1325207240-22622-4-git-send-email-joey@kitenet.net> (raw)
In-Reply-To: <1325207240-22622-1-git-send-email-joey@kitenet.net>
The tweak-fetch hook is fed lines on stdin for all refs that were fetched,
and outputs on stdout possibly modified lines. Its output is parsed and
used when git fetch updates the remote tracking refs, records the entries
in FETCH_HEAD, and produces its report.
This is implemented using the new run_hook_complex API.
Signed-off-by: Joey Hess <joey@kitenet.net>
---
Documentation/githooks.txt | 29 +++++++++
builtin/fetch.c | 144 +++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 172 insertions(+), 1 deletions(-)
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 28edefa..bea450a 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -162,6 +162,35 @@ This hook can be used to perform repository validity checks, auto-display
differences from the previous HEAD if different, or set working dir metadata
properties.
+tweak-fetch
+~~~~~~~~~~~
+
+This hook is invoked by 'git fetch' (commonly called by 'git pull'), after
+refs have been fetched from the remote repository. It is not executed, if
+nothing was fetched.
+
+The output of the hook is used to update the remote-tracking branches, and
+`.git/FETCH_HEAD`, in preparation for for a later merge operation done by
+'git merge'.
+
+It takes no arguments, but is fed a line of the following format on
+its standard input for each ref that was fetched.
+
+ <sha1> SP not-for-merge|merge SP <remote-refname> SP <local-refname> LF
+
+Where the "not-for-merge" flag indicates the ref is not to be merged into the
+current branch, and the "merge" flag indicates that 'git merge' should
+later merge it. The `<remote-refname>` is the remote's name for the ref
+that was fetched, and `<local-refname>` is a name of a remote-tracking branch,
+like "refs/remotes/origin/master", or can be empty if the fetched ref is not
+being stored in a local refname.
+
+The hook must consume all of its standard input, and output back lines
+of the same format. It can modify its input as desired, including
+adding or removing lines, updating the sha1 (i.e. re-point the
+remote-tracking branch), changing the merge flag, and changing the
+`<local-refname>` (i.e. use different remote-tracking branch).
+
post-merge
~~~~~~~~~~
diff --git a/builtin/fetch.c b/builtin/fetch.c
index a48358a..80178d0 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -103,6 +103,148 @@ static int add_existing(const char *refname, const unsigned char *sha1,
return 0;
}
+struct strbuf *tweak_fetch_hook_generator(struct hook *hook)
+{
+ struct ref *ref;
+ struct strbuf *buf = xmalloc(sizeof(struct strbuf));
+
+ strbuf_init(buf, 128);
+ for (ref = hook->data; ref; ref = ref->next)
+ strbuf_addf(buf, "%s %s %s %s\n",
+ sha1_to_hex(ref->old_sha1),
+ ref->merge ? "merge" : "not-for-merge",
+ ref->name ? ref->name : "",
+ ref->peer_ref && ref->peer_ref->name ?
+ ref->peer_ref->name : "");
+ return buf;
+}
+
+static struct ref *parse_tweak_fetch_hook_line(char *l,
+ struct string_list *existing_refs, struct hook *hook)
+{
+ struct ref *ref = NULL, *peer_ref = NULL;
+ struct string_list_item *peer_item = NULL;
+ char *p, *words[4];
+ char *problem;
+ int i;
+
+ for (i = 0 ; i <= ARRAY_SIZE(words) - 1; i++) {
+ p = strchr(l, ' ');
+ words[i] = l;
+ if (!p)
+ break;
+ p[0] = '\0';
+ l = p+1;
+ }
+ if (i != ARRAY_SIZE(words) - 1) {
+ problem = "wrong number of words";
+ goto unparsable;
+ }
+
+ ref = alloc_ref(words[2]);
+ peer_ref = ref->peer_ref = alloc_ref(words[3]);
+ ref->peer_ref->force = 1;
+
+ if (get_sha1_hex(words[0], ref->old_sha1)) {
+ problem = "bad sha1";
+ goto unparsable;
+ }
+
+ if (!strcmp(words[1], "merge")) {
+ ref->merge = 1;
+ }
+ else if (strcmp(words[1], "not-for-merge")) {
+ problem = "bad merge flag";
+ goto unparsable;
+ }
+
+ peer_item = string_list_lookup(existing_refs, peer_ref->name);
+ if (peer_item)
+ hashcpy(peer_ref->old_sha1, peer_item->util);
+
+ return ref;
+
+ unparsable:
+ warning("%s hook output a wrongly formed line: %s",
+ hook->name, problem);
+ free(ref);
+ free(peer_ref);
+ return NULL;
+}
+
+int tweak_fetch_hook_reader(int handle, struct hook *hook)
+{
+ FILE *f;
+ struct strbuf buf;
+ struct string_list existing_refs = STRING_LIST_INIT_NODUP;
+ struct ref *old_refs, *ref, *prevref = NULL;
+ int res = 0;
+
+ f = fdopen(handle, "r");
+ if (f == NULL)
+ return 1;
+
+ old_refs = hook->data;
+ hook->data = NULL;
+
+ strbuf_init(&buf, 128);
+ for_each_ref(add_existing, &existing_refs);
+
+ while (strbuf_getline(&buf, f, '\n') != EOF) {
+ char *l = strbuf_detach(&buf, NULL);
+ ref = parse_tweak_fetch_hook_line(l, &existing_refs, hook);
+ if (ref) {
+ if (prevref) {
+ prevref->next = ref;
+ prevref = ref;
+ } else {
+ hook->data = prevref = ref;
+ }
+ } else {
+ res = 1;
+ }
+ free(l);
+ }
+
+ string_list_clear(&existing_refs, 0);
+ strbuf_release(&buf);
+ fclose(f);
+
+ if (res)
+ hook->data = old_refs;
+ else
+ free_refs(old_refs);
+
+ return res;
+}
+
+/*
+ * The tweak-fetch hook is fed lines of the form:
+ * <sha1> SP <not-for-merge|merge> SP <remote-refname> SP <local-refname> LF
+ * And should output rewritten lines of the same form, which are used
+ * to generate a tweaked set of fetched_refs.
+ */
+static struct ref *run_tweak_fetch_hook(struct ref *fetched_refs)
+{
+ struct hook hook;
+
+ if (! fetched_refs)
+ return fetched_refs;
+
+ memset(&hook, 0, sizeof(hook));
+ hook.name = "tweak-fetch";
+ hook.generator = tweak_fetch_hook_generator;
+ hook.feeder = feed_hook_in_full;
+ hook.reader = tweak_fetch_hook_reader;
+ hook.data = fetched_refs;
+
+ if (run_hook_complex(&hook)) {
+ warning("%s hook failed, ignoring its output", hook.name);
+ }
+
+ return hook.data;
+}
+
static void unlock_pack(void)
{
if (transport)
@@ -529,7 +671,7 @@ static struct refs_result fetch_refs(struct transport *transport,
if (res.status)
res.status = transport_fetch_refs(transport, ref_map);
if (!res.status) {
- res.new_refs = ref_map;
+ res.new_refs = run_tweak_fetch_hook(ref_map);
res.status |= store_updated_refs(transport->url,
transport->remote->name,
--
1.7.7.3
prev parent reply other threads:[~2011-12-30 2:30 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-12-30 1:07 extended hook api and tweak-fetch hook Joey Hess
2011-12-30 1:07 ` [PATCH 1/3] expanded hook api with stdio support Joey Hess
2011-12-30 9:47 ` Johannes Sixt
2011-12-30 17:13 ` Joey Hess
2011-12-30 18:04 ` Johannes Sixt
2012-01-03 19:53 ` Junio C Hamano
2012-01-03 20:06 ` Jeff King
2012-01-03 21:44 ` Junio C Hamano
2011-12-30 1:07 ` [PATCH 2/3] preparations for tweak-fetch hook Joey Hess
2011-12-30 1:07 ` Joey Hess [this message]
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=1325207240-22622-4-git-send-email-joey@kitenet.net \
--to=joey@kitenet.net \
--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).