Re: tracking branch for a rebase

18 messages, 5 authors, 2016-06-15 · open the first message on its own page

Re: tracking branch for a rebase

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:47:23

Jeff King [off-list ref] writes:
I wonder if it is worth adding @{upstream} now, which is fairly safe,
letting it cook for a while, and then adding a "%" alias later after the
concept has proved itself (and people say "I like this feature, but it
really is too much to type").
That's a sane suggestion, I think.  We can always have a descriptive
longhand (e.g. "branch@{upstream}") and later add a shorthand for often
used ones (e.g. "branch^up", "branch/.up" --- the former is possible
because it cannot be upth parent of the commit at the tip of the branch,
and the latter is possible because component of hierarchical refname
cannot begin with a dot).

I find a prefix % not descriptive enough (besides being ugly); if it were
"^branch", as some people said, it would probably have matched its meaning
"up", but that notation is already taken for "uninteresting".  If we are
going to use funny symbol as a notation to invoke magic, I think it is
easier for new people if we limit the number of symbols used.  We already
have ^n, ^{type} and ~n magic, all of which operate on objects and peel
them in three different ways.

Then there currently is only one kind of magic that works on refs and it
is spelled as ref@{magic}.  Let's try not to introduce more notation
before it is absolutely necessary.

When I say there is only one kind of magic notation for refs, I am
primarily talking about the end-user perception.  @{time}, @{number} and
@{-number} all do their magic using the reflog, but that is about _how_
they do what they do.  End-user perception begins with _what_ they do, and
at that level, the magic consistently works on refs and different genie is
summoned depending on what is inside {}.

The @{upstream} thing won't be using reflog to do its job, but that is
about _how_ it is implemented, and the end users don't care.

What is more important is _what_ it does.  Given a ref, the @{} magic
notation finds something that is related to the ref.  @{time} and
@{number} finds what the ref itself used to point at. @{-number}
(applicable only to HEAD ref) finds the ref it used to point at.  And
@{upstream} will find another ref that it merges from (or rebases onto).

Re: tracking branch for a rebase

From: Johannes Schindelin <hidden>
Date: 2016-06-15 22:47:23

Hi,

On Wed, 9 Sep 2009, Junio C Hamano wrote:
I find a prefix % not descriptive enough (besides being ugly); if it were
"^branch", as some people said, it would probably have matched its meaning
"up", but that notation is already taken for "uninteresting".
How about ^^branch? *ducks*

Seriously again, I think that ^{tracking} (with shorthand ^t, maybe) is 
not too shabby an option.  The point is: if we make this unattractive 
enough by requiring a lot of typing, we will never get to the point where 
it is popular enough to make a shorthand: it just will not be used at all.
When I say there is only one kind of magic notation for refs, I am
primarily talking about the end-user perception.  @{time}, @{number} and
@{-number} all do their magic using the reflog, but that is about _how_
they do what they do.  End-user perception begins with _what_ they do, and
at that level, the magic consistently works on refs and different genie is
summoned depending on what is inside {}.

The @{upstream} thing won't be using reflog to do its job, but that is
about _how_ it is implemented, and the end users don't care.
Ah, I get what you're saying.  @{2.days.ago} says something about this 
branch locally, but @{upstream} says something about this branch remotely 
(well, our local cache).  From that view point, it makes sense.

But my point stands: @{upstream} is too awkward to type.  Let's have _at 
least_ a shortcut '@{up}'.

Ciao,
Dscho

[PATCH] Introduce <branch>@{tracked} as shortcut to the tracked branch

From: Johannes Schindelin <hidden>
Date: 2016-06-15 22:47:23

Often, it is quite interesting to inspect the branch tracked by a given
branch.  This patch introduces a nice notation to get at the tracked
branch: '<branch>@{tracked}' can be used to access that tracked branch.

A special shortcut '@{tracked}' refers to the branch tracked by the
current branch.

Suggested by Pasky.

The syntax was suggested by Junio.

Signed-off-by: Johannes Schindelin <redacted>
---

	I decided that I like @{tracked} better than @{upstream} (which
	reads to me more like the upstream _repository_), but hey, I made
	the code flexible enough to change that at a whim.

 Documentation/git-rev-parse.txt |    4 ++
 sha1_name.c                     |   37 ++++++++++++++++++++--
 t/t1506-rev-parse-tracked.sh    |   64 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 102 insertions(+), 3 deletions(-)
 create mode 100755 t/t1506-rev-parse-tracked.sh
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 82045a2..09a2145 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 '@{tracked}' to a ref (short form 'blabla@{t}') refers to
+  the branch tracked by that ref.  If no ref was specified, it means the
+  branch tracked by 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..a886846 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[] = { "@{tracked}", "@{t}" };
+	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>@{tracked}.
  */
 static char *substitute_branch_name(const char **string, int *len)
 {
@@ -254,6 +270,19 @@ 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);
+
+		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 +369,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-tracked.sh b/t/t1506-rev-parse-tracked.sh
new file mode 100755
index 0000000..89193e1
--- /dev/null
+++ b/t/t1506-rev-parse-tracked.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+test_description='test <branch>@{tracked} 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 '@{tracked} resolves to correct full name' '
+	test refs/remotes/origin/master = "$(full_name @{tracked})"
+'
+
+test_expect_success '@{t} resolves to correct full name' '
+	test refs/remotes/origin/master = "$(full_name @{t})"
+'
+
+test_expect_success 'my-side@{tracked} resolves to correct full name' '
+	test refs/remotes/origin/side = "$(full_name my-side@{t})"
+'
+
+test_expect_success 'my-side@{t} 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@{t})"
+'
+
+test_expect_success 'not-tracking@{t} fails' '
+	test_must_fail full_name non-tracking@{t} &&
+	(cd clone && git checkout --no-track -b non-tracking) &&
+	test_must_fail full_name non-tracking@{t}
+'
+
+test_expect_success '<branch>@{t}@{1} resolves correctly' '
+	test_commit 6 &&
+	(cd clone && git fetch) &&
+	test 5 = $(commit_subject my-side@{t}@{1})
+'
+
+test_done
-- 
1.6.4.313.g3d9e3

Re: [PATCH] Introduce <branch>@{tracked} as shortcut to the tracked branch

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:47:23

Johannes Schindelin venit, vidit, dixit 10.09.2009 11:36:
Often, it is quite interesting to inspect the branch tracked by a given
branch.  This patch introduces a nice notation to get at the tracked
branch: '<branch>@{tracked}' can be used to access that tracked branch.

A special shortcut '@{tracked}' refers to the branch tracked by the
current branch.
Sorry, I didn't know the name of the long form was up for discussion.
But it should certainly coincide with the key which for-each-ref uses,
shouldn't it? I don't care whether tracked or upstream, but
for-each-ref's "upstream" has set the precedent.
quoted hunk
Suggested by Pasky.

The syntax was suggested by Junio.

Signed-off-by: Johannes Schindelin <redacted>
---

	I decided that I like @{tracked} better than @{upstream} (which
	reads to me more like the upstream _repository_), but hey, I made
	the code flexible enough to change that at a whim.

 Documentation/git-rev-parse.txt |    4 ++
 sha1_name.c                     |   37 ++++++++++++++++++++--
 t/t1506-rev-parse-tracked.sh    |   64 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 102 insertions(+), 3 deletions(-)
 create mode 100755 t/t1506-rev-parse-tracked.sh
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 82045a2..09a2145 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 '@{tracked}' to a ref (short form 'blabla@{t}') refers to
+  the branch tracked by that ref.  If no ref was specified, it means the
+  branch tracked by 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..a886846 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[] = { "@{tracked}", "@{t}" };
+	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>@{tracked}.
  */
 static char *substitute_branch_name(const char **string, int *len)
 {
@@ -254,6 +270,19 @@ 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);
+
+		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 +369,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-tracked.sh b/t/t1506-rev-parse-tracked.sh
new file mode 100755
index 0000000..89193e1
--- /dev/null
+++ b/t/t1506-rev-parse-tracked.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+test_description='test <branch>@{tracked} 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 '@{tracked} resolves to correct full name' '
+	test refs/remotes/origin/master = "$(full_name @{tracked})"
+'
+
+test_expect_success '@{t} resolves to correct full name' '
+	test refs/remotes/origin/master = "$(full_name @{t})"
+'
+
+test_expect_success 'my-side@{tracked} resolves to correct full name' '
+	test refs/remotes/origin/side = "$(full_name my-side@{t})"
+'
+
+test_expect_success 'my-side@{t} 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@{t})"
+'
+
+test_expect_success 'not-tracking@{t} fails' '
+	test_must_fail full_name non-tracking@{t} &&
+	(cd clone && git checkout --no-track -b non-tracking) &&
+	test_must_fail full_name non-tracking@{t}
+'
+
+test_expect_success '<branch>@{t}@{1} resolves correctly' '
+	test_commit 6 &&
+	(cd clone && git fetch) &&
+	test 5 = $(commit_subject my-side@{t}@{1})
+'
+
+test_done

Re: [PATCH] Introduce <branch>@{tracked} as shortcut to the tracked branch

From: Johannes Schindelin <hidden>
Date: 2016-06-15 22:47:23

Hi,

On Thu, 10 Sep 2009, Michael J Gruber wrote:
Johannes Schindelin venit, vidit, dixit 10.09.2009 11:36:
quoted
Often, it is quite interesting to inspect the branch tracked by a given
branch.  This patch introduces a nice notation to get at the tracked
branch: '<branch>@{tracked}' can be used to access that tracked branch.

A special shortcut '@{tracked}' refers to the branch tracked by the
current branch.
Sorry, I didn't know the name of the long form was up for discussion.
But it should certainly coincide with the key which for-each-ref uses,
shouldn't it? I don't care whether tracked or upstream, but
for-each-ref's "upstream" has set the precedent.
You're right.

sed -i -e 's/@{tracked}/@{upstream}/g' -e 's/@{t}/@{u}/g' $PATCH

Ciao,
Dscho

Re: [PATCH] Introduce <branch>@{tracked} as shortcut to the tracked branch

From: Johan Herland <hidden>
Date: 2016-06-15 22:47:23

On Thursday 10 September 2009, Michael J Gruber wrote:
Johannes Schindelin venit, vidit, dixit 10.09.2009 11:36:
quoted
Often, it is quite interesting to inspect the branch tracked by a
given branch.  This patch introduces a nice notation to get at the
tracked branch: '<branch>@{tracked}' can be used to access that
tracked branch.

A special shortcut '@{tracked}' refers to the branch tracked by the
current branch.
Sorry, I didn't know the name of the long form was up for discussion.
But it should certainly coincide with the key which for-each-ref
uses, shouldn't it? I don't care whether tracked or upstream, but
for-each-ref's "upstream" has set the precedent.
...and 'git branch --track' set an even earlier precedent...


...Johan


-- 
Johan Herland, [off-list ref]
www.herland.net

Re: [PATCH] Introduce <branch>@{tracked} as shortcut to the tracked branch

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:47:23

Johan Herland venit, vidit, dixit 10.09.2009 12:18:
On Thursday 10 September 2009, Michael J Gruber wrote:
quoted
Johannes Schindelin venit, vidit, dixit 10.09.2009 11:36:
quoted
Often, it is quite interesting to inspect the branch tracked by a
given branch.  This patch introduces a nice notation to get at the
tracked branch: '<branch>@{tracked}' can be used to access that
tracked branch.

A special shortcut '@{tracked}' refers to the branch tracked by the
current branch.
Sorry, I didn't know the name of the long form was up for discussion.
But it should certainly coincide with the key which for-each-ref
uses, shouldn't it? I don't care whether tracked or upstream, but
for-each-ref's "upstream" has set the precedent.
...and 'git branch --track' set an even earlier precedent...
an unfortunate one, yes. It brings us back to an old discussion. The
consensus was what's in the glossary:

       tracking branch
           A regular git branch that is used to follow changes from
another repository. A tracking branch should
           not contain direct modifications or have local commits made
to it. A tracking branch can usually be
           identified as the right-hand-side ref in a Pull: refspec.

I.e., a tracking branch is something under refs/remotes/ (usually; I'll
use that simplification).

If I checkout -b myworkonit refs/remotes/origin/theirstuff then
myworkonit *does not track* origin/theirstuff according to the glossary
(but git checkout says so, unfortunately, and the option is named
likewise); rather, it has origin/theirstuff as its upstream.

In fact, refs/remotes/origin/theirstuff tracks whatever the name is on
the left hand side of the correspondig fetch refspec.

Maybe this is a good time to either

- change the definition of "tracking branch" (to one having an upstream
which is in refs/remotes/; and call "remote upbranch" what's in
refs/remotes/) or
- rename the option and output of git checkout -b/git branch --track.

Accordingly, either tracked or something else (such as upstream) would
be appropriate for the for-each-ref key and the ref specifier.

Cheers,
Michael

Re: [PATCH] Introduce <branch>@{tracked} as shortcut to the tracked branch

From: Jeff King <hidden>
Date: 2016-06-15 22:47:23

On Thu, Sep 10, 2009 at 12:18:06PM +0200, Johan Herland wrote:
quoted
quoted
A special shortcut '@{tracked}' refers to the branch tracked by the
current branch.
Sorry, I didn't know the name of the long form was up for discussion.
But it should certainly coincide with the key which for-each-ref
uses, shouldn't it? I don't care whether tracked or upstream, but
for-each-ref's "upstream" has set the precedent.
...and 'git branch --track' set an even earlier precedent...
FWIW, that came about from this discussion:

  http://article.gmane.org/gmane.comp.version-control.git/115765

-Peff

Re: [PATCH] Introduce <branch>@{tracked} as shortcut to the tracked branch

From: Johan Herland <hidden>
Date: 2016-06-15 22:47:23

On Thursday 10 September 2009, Michael J Gruber wrote:
Johan Herland venit, vidit, dixit 10.09.2009 12:18:
quoted
On Thursday 10 September 2009, Michael J Gruber wrote:
quoted
Johannes Schindelin venit, vidit, dixit 10.09.2009 11:36:
quoted
Often, it is quite interesting to inspect the branch tracked by a
given branch.  This patch introduces a nice notation to get at
the tracked branch: '<branch>@{tracked}' can be used to access
that tracked branch.

A special shortcut '@{tracked}' refers to the branch tracked by
the current branch.
Sorry, I didn't know the name of the long form was up for
discussion. But it should certainly coincide with the key which
for-each-ref uses, shouldn't it? I don't care whether tracked or
upstream, but for-each-ref's "upstream" has set the precedent.
...and 'git branch --track' set an even earlier precedent...
an unfortunate one, yes. It brings us back to an old discussion. The
consensus was what's in the glossary:
[snip]
Maybe this is a good time to either

- change the definition of "tracking branch" (to one having an
upstream which is in refs/remotes/; and call "remote upbranch" what's
in refs/remotes/) or
- rename the option and output of git checkout -b/git branch --track.

Accordingly, either tracked or something else (such as upstream)
would be appropriate for the for-each-ref key and the ref specifier.
Sure, as someone else already stated, I don't care too much what it's 
named, as long as the naming is consistent across all of git. We do 
have an unforunate name clash between remote-tracking branches (i.e. 
branches under refs/remotes/) and 'tracking' branches (i.e. 'git 
branch --track') [1], and I believe 1.7.0 would be a nice opportunity 
to clean this up.

I think I vote for the second option, renaming 'git branch --track' 
to 'git branch --upstream', and s/@{tracked}/@{upstream}/.


Have fun! :)

...Johan

[1]: And also tracked and untracked files, although I believe that is 
unambiguous in most cases.


-- 
Johan Herland, [off-list ref]
www.herland.net

Re: [PATCH] Introduce <branch>@{tracked} as shortcut to the tracked branch

From: Johannes Schindelin <hidden>
Date: 2016-06-15 22:47:23

Hi,

On Thu, 10 Sep 2009, Johan Herland wrote:
I think I vote for the second option, renaming 'git branch --track' 
to 'git branch --upstream', and s/@{tracked}/@{upstream}/.
That does not make any sense, as that --track is to be understood as a 
verb.  How do you "upstream a branch"?

Ciao,
Dscho

Re: [PATCH] Introduce <branch>@{tracked} as shortcut to the tracked branch

From: Jeff King <hidden>
Date: 2016-06-15 22:47:23

On Thu, Sep 10, 2009 at 11:36:42AM +0200, Johannes Schindelin wrote:
quoted hunk
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 82045a2..09a2145 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 '@{tracked}' to a ref (short form 'blabla@{t}') refers to
+  the branch tracked by that ref.  If no ref was specified, it means the
+  branch tracked by the current branch.
+
It looks like the code dereferences symbolic refs when doing the lookup
(so HEAD@{t} will find the upstream of the current branch), which I
think is the best thing to do. However, that does make it behave
slightly differently than HEAD@{2.minutes.ago}, so it may be worth
adding a sentence to this paragraph like:

  If the ref is a symbolic ref, it is dereferenced before searching
  for the upstream ref.

And we may want to add a test for HEAD, as well.

Also, I seem to be able to stimulate a segfault on a detached HEAD, but
I haven't investigated it yet.

-Peff

Re: [PATCH] Introduce <branch>@{tracked} as shortcut to the tracked branch

From: Michael J Gruber <hidden>
Date: 2016-06-15 22:47:23

Johannes Schindelin venit, vidit, dixit 10.09.2009 15:35:
Hi,

On Thu, 10 Sep 2009, Johan Herland wrote:
quoted
I think I vote for the second option, renaming 'git branch --track' 
to 'git branch --upstream', and s/@{tracked}/@{upstream}/.
That does not make any sense, as that --track is to be understood as a 
verb.  How do you "upstream a branch"?
Well, that brunch also got its granma wrong tho his speeling is right ;)

Seriously: In

git branch --track branch1 branch2

who tracks whom if you read "--track" as a verb?
So, "--track" can only be understood as an attribute to the main (first)
argument, saying it's tracking something. Just as --upstream could be
read as an attribute meaning "has an upstream".

(Note also how different this is from how -b works for checkout, -b
actually taking an argument: git checkout -b branch1 branch2)

Cheers,
Michael

Re: [PATCH] Introduce <branch>@{tracked} as shortcut to the tracked branch

From: Jeff King <hidden>
Date: 2016-06-15 22:47:23

On Thu, Sep 10, 2009 at 10:16:18AM -0400, Jeff King wrote:
And we may want to add a test for HEAD, as well.

Also, I seem to be able to stimulate a segfault on a detached HEAD, but
I haven't investigated it yet.
Found it. We should probably squash this in:
diff --git a/sha1_name.c b/sha1_name.c
index a886846..ef4ec11 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -276,7 +276,7 @@ static char *substitute_branch_name(const char **string, int *len)
 		struct branch *tracking = branch_get(*ref ? ref : NULL);
 
 		free(ref);
-		if (tracking->merge && tracking->merge[0]->dst) {
+		if (tracking && tracking->merge && tracking->merge[0]->dst) {
 			*string = xstrdup(tracking->merge[0]->dst);
 			*len = strlen(*string);
 			return (char *)*string;
diff --git a/t/t1506-rev-parse-tracked.sh b/t/t1506-rev-parse-tracked.sh
index 89193e1..2be5b75 100755
--- a/t/t1506-rev-parse-tracked.sh
+++ b/t/t1506-rev-parse-tracked.sh
@@ -61,4 +61,15 @@ test_expect_success '<branch>@{t}@{1} resolves correctly' '
 	test 5 = $(commit_subject my-side@{t}@{1})
 '
 
+test_expect_success 'HEAD@{t} dereferences HEAD' '
+	(cd clone && git checkout master) &&
+	test refs/remotes/origin/master = "$(full_name HEAD@{t})"
+'
+
+test_expect_success 'HEAD@{t} and @{t} on detached HEAD fail' '
+	(cd clone && git checkout master^0) &&
+	test_must_fail full_name @{t} &&
+	test_must_fail full_name HEAD@{t}
+'
+
 test_done

Re: [PATCH] Introduce <branch>@{tracked} as shortcut to the tracked branch

From: Johannes Schindelin <hidden>
Date: 2016-06-15 22:47:23

Hi,

On Thu, 10 Sep 2009, Jeff King wrote:
Also, I seem to be able to stimulate a segfault on a detached HEAD, but 
I haven't investigated it yet.
Aaargh!

Mikachu pointed that out recently, provided a test, I fixed it, and in the 
meantime I managed to forget about it!

/me needs to do less things per day.

Ciao,
Dscho

Re: [PATCH] Introduce <branch>@{tracked} as shortcut to the tracked branch

From: Johannes Schindelin <hidden>
Date: 2016-06-15 22:47:23

Hi,

On Thu, 10 Sep 2009, Jeff King wrote:
quoted hunk
On Thu, Sep 10, 2009 at 10:16:18AM -0400, Jeff King wrote:
diff --git a/sha1_name.c b/sha1_name.c
index a886846..ef4ec11 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -276,7 +276,7 @@ static char *substitute_branch_name(const char **string, int *len)
 		struct branch *tracking = branch_get(*ref ? ref : NULL);
 
 		free(ref);
-		if (tracking->merge && tracking->merge[0]->dst) {
+		if (tracking && tracking->merge && tracking->merge[0]->dst) {
Almost.

My version dies if tracking is NULL.

Ciao,
Dscho

[PATCH v2] Introduce <branch>@{upstream} as shortcut to the tracked branch

From: Johannes Schindelin <hidden>
Date: 2016-06-15 22:47:23

Often, it is quite interesting to inspect the branch tracked by a given
branch.  This patch introduces a nice notation to get at the tracked
branch: '<branch>@{upstream}' can be used to access that tracked branch.

A special shortcut '@{upstream}' refers to the branch tracked by the
current branch.

Suggested by Pasky.

The syntax was suggested by Junio.

A test for a now-fixed crash was provided by Mikael Magnusson.

The crash has been pointed out by Peff again (because this here developer
managed to forget about the fix).

Signed-off-by: Johannes Schindelin <redacted>
---

	Changes since v1:
	- changed to @{upstream} (and @{u})
	- included the fix I forgot about

 Documentation/git-rev-parse.txt |    4 ++
 sha1_name.c                     |   39 ++++++++++++++++++++--
 t/t1506-rev-parse-tracked.sh    |   69 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 109 insertions(+), 3 deletions(-)
 create mode 100755 t/t1506-rev-parse-tracked.sh
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 82045a2..09a2145 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 'blabla@{u}') refers to
+  the branch tracked by that ref.  If no ref was specified, it means the
+  branch tracked by 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..ae4280e 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);
+
+		free(ref);
+		if (!tracking)
+			die ("No tracking branch found for '%s'", 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-tracked.sh b/t/t1506-rev-parse-tracked.sh
new file mode 100755
index 0000000..72d0579
--- /dev/null
+++ b/t/t1506-rev-parse-tracked.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 '% without specifying branch crashes on a detached HEAD' '
+	git checkout HEAD^0 &&
+	test_must_fail git rev-parse @{u}
+'
+
+test_done
-- 
1.6.4.313.g3d9e3

Re: [PATCH v2] Introduce <branch>@{upstream} as shortcut to the tracked branch

From: Jeff King <hidden>
Date: 2016-06-15 22:47:23

On Thu, Sep 10, 2009 at 05:25:57PM +0200, Johannes Schindelin wrote:
	Changes since v1:
	- changed to @{upstream} (and @{u})
Hmm. After applying v2, I accidentally tried "git rev-parse @{t}" and
was surprised to find that it worked! The problem, of course, is that we
saw that it was not one of our keywords and therefore dumped it into
approxidate, which happily converted it into the current time, and
provided me with HEAD@{now}.

Which is sad, because it means "HEAD@{usptream}" will silently produce
incorrect results.

I don't see a way around it, though, short of tightening approxidate's
parsing. Maybe it could keep a flag for "I noticed _anything_ of value
in this string", and we could barf if it isn't set. That would still
allow arbitrary stuff like:

  the 6th of july

and keep Linus' favorite

  I ate 6 hot dogs in July.

but disallow the most obviously wrong dates like:

  t
  usptream
  total bogosity
+	ret = tracked_suffix(*string, *len);
+	if (ret) {
+		char *ref = xstrndup(*string, *len - ret);
+		struct branch *tracking = branch_get(*ref ? ref : NULL);
+
+		free(ref);
+		if (!tracking)
+			die ("No tracking branch found for '%s'", ref);
+		if (tracking->merge && tracking->merge[0]->dst) {
+			*string = xstrdup(tracking->merge[0]->dst);
+			*len = strlen(*string);
+			return (char *)*string;
+		}
+	}
+
I don't think it is a good idea to die for !tracking, but not for
!tracking->merge. That leads to inconsistent user-visible results:

  $ git checkout HEAD^0
  $ git rev-parse HEAD@{u}
  fatal: No tracking branch found for 'HEAD'
  $ git rev-parse bogus@{u}
  bogus@{u}
  fatal: ambiguous argument 'bogus@{u}': unknown revision or path not in the working tree.
  Use '--' to separate paths from revisions

Shouldn't both cases say the same thing?

Also, your die message has two problems:

 1. It looks at ref immediately after it is free'd, spewing junk.

 2. Ref can be the empty string, which gives you the ugly:

       fatal: No tracking branch found for ''

    Should we munge that into HEAD (or "the current branch") for the
    user?

-Peff

Re: [PATCH v2] Introduce <branch>@{upstream} as shortcut to the tracked branch

From: Johannes Schindelin <hidden>
Date: 2016-06-15 22:47:23

Hi,

On Thu, 10 Sep 2009, Jeff King wrote:
I wrote:
quoted
+	ret = tracked_suffix(*string, *len);
+	if (ret) {
+		char *ref = xstrndup(*string, *len - ret);
+		struct branch *tracking = branch_get(*ref ? ref : NULL);
+
+		free(ref);
+		if (!tracking)
+			die ("No tracking branch found for '%s'", ref);
+		if (tracking->merge && tracking->merge[0]->dst) {
+			*string = xstrdup(tracking->merge[0]->dst);
+			*len = strlen(*string);
+			return (char *)*string;
+		}
+	}
+
I don't think it is a good idea to die for !tracking, but not for
!tracking->merge. That leads to inconsistent user-visible results:

  $ git checkout HEAD^0
  $ git rev-parse HEAD@{u}
  fatal: No tracking branch found for 'HEAD'
  $ git rev-parse bogus@{u}
  bogus@{u}
  fatal: ambiguous argument 'bogus@{u}': unknown revision or path not in the working tree.
  Use '--' to separate paths from revisions

Shouldn't both cases say the same thing?

Also, your die message has two problems:

 1. It looks at ref immediately after it is free'd, spewing junk.

 2. Ref can be the empty string, which gives you the ugly:

       fatal: No tracking branch found for ''

    Should we munge that into HEAD (or "the current branch") for the
    user?
All true, but I cannot take care of it today.

Ciao,
Dscho
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help