diff options
author | Junio C Hamano <gitster@pobox.com> | 2010-01-22 16:08:13 -0800 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2010-01-22 16:08:13 -0800 |
commit | 4ca1b623865a9dc100f95a7867e35a9f73d7507a (patch) | |
tree | f9ad811bb2e2b5b612a4b756707c9d3483e97366 | |
parent | Merge branch 'jl/submodule-diff' (diff) | |
parent | Teach @{upstream} syntax to strbuf_branchanme() (diff) | |
download | tgif-4ca1b623865a9dc100f95a7867e35a9f73d7507a.tar.xz |
Merge branch 'js/refer-upstream'
* js/refer-upstream:
Teach @{upstream} syntax to strbuf_branchanme()
t1506: more test for @{upstream} syntax
Introduce <branch>@{upstream} notation
-rw-r--r-- | Documentation/git-rev-parse.txt | 4 | ||||
-rw-r--r-- | sha1_name.c | 91 | ||||
-rwxr-xr-x | t/t1506-rev-parse-upstream.sh | 110 |
3 files changed, 192 insertions, 13 deletions
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index d375f1af10..818eb48d67 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -234,6 +234,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 1739e9e612..9215ad1d05 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) { @@ -240,7 +241,8 @@ static int ambiguous_path(const char *path, int len) /* * *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 a magic short-hand form + * to name a branch. */ static char *substitute_branch_name(const char **string, int *len) { @@ -323,6 +325,20 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log) return logs_found; } +static inline int upstream_mark(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 (suffix_len <= len + && !memcmp(string, suffix[i], suffix_len)) + return suffix_len; + } + return 0; +} + static int get_sha1_1(const char *name, int len, unsigned char *sha1); static int get_sha1_basic(const char *str, int len, unsigned char *sha1) @@ -340,8 +356,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 (!upstream_mark(str + at, len - at)) { + reflog_len = (len-1) - (at+2); + len = at; + } break; } } @@ -740,17 +758,10 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1, } /* - * This reads "@{-N}" syntax, finds the name of the Nth previous - * branch we were on, and places the name of the branch in the given - * buf and returns the number of characters parsed if successful. - * - * If the input is not of the accepted format, it returns a negative - * number to signal an error. - * - * If the input was ok but there are not N branch switches in the - * reflog, it returns 0. + * Parse @{-N} syntax, return the number of characters parsed + * if successful; otherwise signal an error with negative value. */ -int interpret_branch_name(const char *name, struct strbuf *buf) +static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf) { long nth; int i, retval; @@ -837,6 +848,60 @@ int get_sha1_mb(const char *name, unsigned char *sha1) } /* + * This reads short-hand syntax that not only evaluates to a commit + * object name, but also can act as if the end user spelled the name + * of the branch from the command line. + * + * - "@{-N}" finds the name of the Nth previous branch we were on, and + * places the name of the branch in the given buf and returns the + * number of characters parsed if successful. + * + * - "<branch>@{upstream}" finds the name of the other ref that + * <branch> is configured to merge with (missing <branch> defaults + * to the current branch), and places the name of the branch in the + * given buf and returns the number of characters parsed if + * successful. + * + * If the input is not of the accepted format, it returns a negative + * number to signal an error. + * + * If the input was ok but there are not N branch switches in the + * reflog, it returns 0. + */ +int interpret_branch_name(const char *name, struct strbuf *buf) +{ + char *cp; + struct branch *upstream; + int namelen = strlen(name); + int len = interpret_nth_prior_checkout(name, buf); + int tmp_len; + + if (!len) + return len; /* syntax Ok, not enough switches */ + if (0 < len) + return len; /* consumed from the front */ + cp = strchr(name, '@'); + if (!cp) + return -1; + tmp_len = upstream_mark(cp, namelen - (cp - name)); + if (!tmp_len) + return -1; + len = cp + tmp_len - name; + cp = xstrndup(name, cp - name); + upstream = branch_get(*cp ? cp : NULL); + if (!upstream + || !upstream->merge + || !upstream->merge[0]->dst) + return error("No upstream branch found for '%s'", cp); + free(cp); + cp = shorten_unambiguous_ref(upstream->merge[0]->dst, 0); + strbuf_reset(buf); + strbuf_addstr(buf, cp); + free(cp); + return len; +} + +/* * This is like "get_sha1_basic()", except it allows "sha1 expressions", * notably "xyz^" for "parent of xyz" */ diff --git a/t/t1506-rev-parse-upstream.sh b/t/t1506-rev-parse-upstream.sh new file mode 100755 index 0000000000..95c9b0923f --- /dev/null +++ b/t/t1506-rev-parse-upstream.sh @@ -0,0 +1,110 @@ +#!/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_expect_success 'checkout -b new my-side@{u} forks from the same' ' +( + cd clone && + git checkout -b new my-side@{u} && + git rev-parse --symbolic-full-name my-side@{u} >expect && + git rev-parse --symbolic-full-name new@{u} >actual && + test_cmp expect actual +) +' + +test_expect_success 'merge my-side@{u} records the correct name' ' +( + sq="'\''" && + cd clone || exit + git checkout master || exit + git branch -D new ;# can fail but is ok + git branch -t new my-side@{u} && + git merge -s ours new@{u} && + git show -s --pretty=format:%s >actual && + echo "Merge remote branch ${sq}origin/side${sq}" >expect && + test_cmp expect actual +) +' + +test_expect_success 'branch -d other@{u}' ' + git checkout -t -b other master && + git branch -d @{u} && + git for-each-ref refs/heads/master >actual && + >expect && + test_cmp expect actual +' + +test_expect_success 'checkout other@{u}' ' + git branch -f master HEAD && + git checkout -t -b another master && + git checkout @{u} && + git symbolic-ref HEAD >actual && + echo refs/heads/master >expect && + test_cmp expect actual +' + +test_done |