git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: default behaviour for `gitmerge` (no arguments)

From
Junio C Hamano <gitster@pobox.com>
Date
Jan 13, 2010, 06:57 UTC
Message-ID
<7vwrzmqypn.fsf@alter.siamese.dyndns.org>
In-Reply-To
<20100112182550.GA15696@coredump.intra.peff.net>
Jeff King <peff@peff.net> writes:
Show 16 quoted lines
> Ah, right. I remembered hating "%" even as I typed it, but I had
> forgotten about the followup discussion. Looking at it again, I note:
>
>   1. The last posted patch still has a misplaced free() (patch below),
>      but I think otherwise is not buggy.
>
>   2. We don't complain on "git show @{usptream}" and we probably should.
>      I remember there being some complications because the contents of
>      @{} were passed to approxidate, but I think we can get around that
>      by letting approxidate complain if _nothing_ in the date was
>      useful. So "git show @{2.weeks.and.7.hot.dogs.ago}" would still
>      work, but "git show @{totally.bogus.input}" would complain.
>
>   3. I have actually been running with Dscho's patch for the last couple
>      of months, and I don't remember using it once. So perhaps it is not
>      as useful as I might have thought. :)
I presume we are discussing this patch?
    http://article.gmane.org/gmane.comp.version-control.git/128121
I'll squash the free() fix; thanks.

I wondered why it doesn't hook into interpret_branch_name(), and instead adds itself to the static substitute_branch_name(); it forbids the use of the syntax from by callers of strbuf_branchname().

I agree with your point #2 above.

Regarding your point #3, I don't think the notation should be that useful if your workflow is sane. The original use case that triggered the resurrection of the patch went like this:

        git fetch &&
        for local in my set of local branches
        do
                git checkout $local &&
                git merge $local@{upstream} || {
                        echo failed to merge on $local
                        break
                }
        done

and the new notation might look useful in the scenario. But the thing is, constantly merging with the other side, even if you haven't added anything of value since you merged from there last time, is a bad practice to begin with. I added one use case that is sane _and_ will be helped by the new notation to the rewritten version of Dscho's patch (below).

Just to refresh our memory from the old thread and make sure we are discussing the same patch, here is what I am planning to queue. The log message and documentation are somewhat updated to avoid the word "track" because it seems that everybody gets confused and starts talking different things whenever that word is used. For the same reason, the test script was renamed.

In this set-up, for example:
    [remote "filfre"]
        url = ...
        fetch = +refs/heads/nitfol:refs/heads/rezrov
    [branch "frotz"]
        remote = filfre
        merge = refs/heads/nitfol

some people say rezrov tracks nitfol from filfre but Dscho's patch says frotz tracks nitfol from filfre. They _may_ both track, but they "track" the other in a quite differently way, so the word has become meaningless.

I've been trying to be careful and used different words to disambiguate whenever I had to talk about these concepts:

 - The purpose of rezrov is to keep a tab on the progress of the nitfol
   branch at the remote end.  We say rezrov is a remote tracking branch
   for nitfol from filfre.
 - On the other hand, we have branch frotz that forked from nitfol that
   came from filfre.  It builds on top of that history by occasionally
   merging with it at key points in the history.  So we say frotz builds
   on top of nitfol from filfre.  We also say nitfol at filfre is the
   upstream of frotz.
-- >8 --
Date: Thu, 10 Sep 2009 17:25:57 +0200
Subject: [PATCH] Introduce <branch>@{upstream} notation

A new notation '<branch>@{upstream}' refers to the branch <branch> is set to build on top of. Missing <branch> (i.e. '@{upstream}') defaults to the current branch.

This allows you to run, for example,
	for l in list of local branches
	do
		git log --oneline --left-right $l...$l@{upstream}
	done

to inspect each of the local branches you are interested in for the divergence from its upstream.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/git-rev-parse.txt |    4 ++
 sha1_name.c                     |   39 ++++++++++++++++++++--
 t/t1506-rev-parse-upstream.sh   |   69 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 109 insertions(+), 3 deletions(-)
 create mode 100755 t/t1506-rev-parse-upstream.sh
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 82045a2..923b56a 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -231,6 +231,10 @@ when you run 'git-merge'.
 * The special construct '@\{-<n>\}' means the <n>th branch checked out
   before the current one.
 
+* The suffix '@{upstream}' to a ref (short form 'ref@{u}') refers to
+  the branch the ref is set to build on top of.  Missing ref defaults
+  to the current branch.
+
 * A suffix '{caret}' to a revision parameter means the first parent of
   that commit object.  '{caret}<n>' means the <n>th parent (i.e.
   'rev{caret}'
diff --git a/sha1_name.c b/sha1_name.c
index 44bb62d..fb4e214 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -5,6 +5,7 @@
 #include "blob.h"
 #include "tree-walk.h"
 #include "refs.h"
+#include "remote.h"
 
 static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
 {
@@ -238,9 +239,24 @@ static int ambiguous_path(const char *path, int len)
 	return slash;
 }
 
+static inline int tracked_suffix(const char *string, int len)
+{
+	const char *suffix[] = { "@{upstream}", "@{u}" };
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(suffix); i++) {
+		int suffix_len = strlen(suffix[i]);
+		if (len >= suffix_len && !memcmp(string + len - suffix_len,
+					suffix[i], suffix_len))
+			return suffix_len;
+	}
+	return 0;
+}
+
 /*
  * *string and *len will only be substituted, and *string returned (for
- * later free()ing) if the string passed in is of the form @{-<n>}.
+ * later free()ing) if the string passed in is of the form @{-<n>} or
+ * of the form <branch>@{upstream}.
  */
 static char *substitute_branch_name(const char **string, int *len)
 {
@@ -254,6 +270,21 @@ static char *substitute_branch_name(const char **string, int *len)
 		return (char *)*string;
 	}
 
+	ret = tracked_suffix(*string, *len);
+	if (ret) {
+		char *ref = xstrndup(*string, *len - ret);
+		struct branch *tracking = branch_get(*ref ? ref : NULL);
+
+		if (!tracking)
+			die ("No tracking branch found for '%s'", ref);
+		free(ref);
+		if (tracking->merge && tracking->merge[0]->dst) {
+			*string = xstrdup(tracking->merge[0]->dst);
+			*len = strlen(*string);
+			return (char *)*string;
+		}
+	}
+
 	return NULL;
 }
 
@@ -340,8 +371,10 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 	if (len && str[len-1] == '}') {
 		for (at = len-2; at >= 0; at--) {
 			if (str[at] == '@' && str[at+1] == '{') {
-				reflog_len = (len-1) - (at+2);
-				len = at;
+				if (!tracked_suffix(str + at, len - at)) {
+					reflog_len = (len-1) - (at+2);
+					len = at;
+				}
 				break;
 			}
 		}
diff --git a/t/t1506-rev-parse-upstream.sh b/t/t1506-rev-parse-upstream.sh
new file mode 100755
index 0000000..5abdc13
--- /dev/null
+++ b/t/t1506-rev-parse-upstream.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='test <branch>@{upstream} syntax'
+
+. ./test-lib.sh
+
+
+test_expect_success 'setup' '
+
+	test_commit 1 &&
+	git checkout -b side &&
+	test_commit 2 &&
+	git checkout master &&
+	git clone . clone &&
+	test_commit 3 &&
+	(cd clone &&
+	 test_commit 4 &&
+	 git branch --track my-side origin/side)
+
+'
+
+full_name () {
+	(cd clone &&
+	 git rev-parse --symbolic-full-name "$@")
+}
+
+commit_subject () {
+	(cd clone &&
+	 git show -s --pretty=format:%s "$@")
+}
+
+test_expect_success '@{upstream} resolves to correct full name' '
+	test refs/remotes/origin/master = "$(full_name @{upstream})"
+'
+
+test_expect_success '@{u} resolves to correct full name' '
+	test refs/remotes/origin/master = "$(full_name @{u})"
+'
+
+test_expect_success 'my-side@{upstream} resolves to correct full name' '
+	test refs/remotes/origin/side = "$(full_name my-side@{u})"
+'
+
+test_expect_success 'my-side@{u} resolves to correct commit' '
+	git checkout side &&
+	test_commit 5 &&
+	(cd clone && git fetch) &&
+	test 2 = "$(commit_subject my-side)" &&
+	test 5 = "$(commit_subject my-side@{u})"
+'
+
+test_expect_success 'not-tracking@{u} fails' '
+	test_must_fail full_name non-tracking@{u} &&
+	(cd clone && git checkout --no-track -b non-tracking) &&
+	test_must_fail full_name non-tracking@{u}
+'
+
+test_expect_success '<branch>@{u}@{1} resolves correctly' '
+	test_commit 6 &&
+	(cd clone && git fetch) &&
+	test 5 = $(commit_subject my-side@{u}@{1})
+'
+
+test_expect_success '@{u} without specifying branch fails on a detached HEAD' '
+	git checkout HEAD^0 &&
+	test_must_fail git rev-parse @{u}
+'
+
+test_done
-- 
1.6.6.280.ge295b7.dirty
Previous: Jeff KingNext: Johannes Schindelin
Message 6 of 30 in “default behaviour for `gitmerge` (no arguments)”
  1. Gareth AdamsJan 11, 2010
  2. Junio C HamanoJan 11, 2010
  3. Jeff KingJan 12, 2010
  4. Junio C HamanoJan 12, 2010
  5. Jeff KingJan 12, 2010
  6. Junio C HamanoJan 13, 2010
  7. Johannes SchindelinJan 13, 2010
  8. Junio C HamanoJan 13, 2010
  9. Johannes SchindelinJan 13, 2010
  10. Junio C HamanoJan 13, 2010
  11. Johannes SchindelinJan 13, 2010
  12. Junio C HamanoJan 13, 2010
  13. 0/2 @{u} updatesJunio C Hamano, Jan 20, 2010
  14. 1/2 t1506: more test for @{upstream} syntaxJunio C Hamano, Jan 20, 2010
  15. Jeff KingJan 26, 2010
  16. Junio C HamanoJan 26, 2010
  17. Jeff KingJan 27, 2010
  18. Junio C HamanoJan 27, 2010
  19. Jeff KingJan 28, 2010
  20. Junio C HamanoJan 26, 2010
  21. Jeff KingJan 27, 2010
  22. Junio C HamanoJan 27, 2010
  23. Jeff KingJan 28, 2010
  24. 1/3 test combinations of @{} syntaxJeff King, Jan 28, 2010
  25. 2/3 fix parsing of @{-1}@{u} combinationJeff King, Jan 28, 2010
  26. 3/3 reject @{-1} not at beginning of object nameJeff King, Jan 28, 2010
  27. Junio C HamanoJan 28, 2010
  28. Jeff KingJan 29, 2010
  29. 2/2 Teach @{upstream} syntax to strbuf_branchanme()Junio C Hamano, Jan 20, 2010
  30. Johannes SchindelinJan 20, 2010

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.