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 <johannes.schindelin@xxxxxx> --- 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 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html