Merge branch 'js/refer-upstream'
* js/refer-upstream: Teach @{upstream} syntax to strbuf_branchanme() t1506: more test for @{upstream} syntax Introduce <branch>@{upstream} notation
This commit is contained in:
commit
4ca1b62386
@ -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}'
|
||||
|
91
sha1_name.c
91
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;
|
||||
@ -836,6 +847,60 @@ int get_sha1_mb(const char *name, unsigned char *sha1)
|
||||
return st;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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"
|
||||
|
110
t/t1506-rev-parse-upstream.sh
Executable file
110
t/t1506-rev-parse-upstream.sh
Executable file
@ -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
|
Loading…
Reference in New Issue
Block a user