Browse Source

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>
maint
Johannes Schindelin 15 years ago committed by Junio C Hamano
parent
commit
28fb84382b
  1. 4
      Documentation/git-rev-parse.txt
  2. 39
      sha1_name.c
  3. 69
      t/t1506-rev-parse-upstream.sh

4
Documentation/git-rev-parse.txt

@ -231,6 +231,10 @@ when you run 'git-merge'. @@ -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}'

39
sha1_name.c

@ -5,6 +5,7 @@ @@ -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) @@ -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) @@ -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) @@ -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;
}
}

69
t/t1506-rev-parse-upstream.sh

@ -0,0 +1,69 @@ @@ -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
Loading…
Cancel
Save