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:
@ -234,6 +234,10 @@ when you run 'git merge'.
|
|||||||
* The special construct '@\{-<n>\}' means the <n>th branch checked out
|
* The special construct '@\{-<n>\}' means the <n>th branch checked out
|
||||||
before the current one.
|
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
|
* A suffix '{caret}' to a revision parameter means the first parent of
|
||||||
that commit object. '{caret}<n>' means the <n>th parent (i.e.
|
that commit object. '{caret}<n>' means the <n>th parent (i.e.
|
||||||
'rev{caret}'
|
'rev{caret}'
|
||||||
|
91
sha1_name.c
91
sha1_name.c
@ -5,6 +5,7 @@
|
|||||||
#include "blob.h"
|
#include "blob.h"
|
||||||
#include "tree-walk.h"
|
#include "tree-walk.h"
|
||||||
#include "refs.h"
|
#include "refs.h"
|
||||||
|
#include "remote.h"
|
||||||
|
|
||||||
static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
|
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
|
* *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)
|
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;
|
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_1(const char *name, int len, unsigned char *sha1);
|
||||||
|
|
||||||
static int get_sha1_basic(const char *str, 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] == '}') {
|
if (len && str[len-1] == '}') {
|
||||||
for (at = len-2; at >= 0; at--) {
|
for (at = len-2; at >= 0; at--) {
|
||||||
if (str[at] == '@' && str[at+1] == '{') {
|
if (str[at] == '@' && str[at+1] == '{') {
|
||||||
reflog_len = (len-1) - (at+2);
|
if (!upstream_mark(str + at, len - at)) {
|
||||||
len = at;
|
reflog_len = (len-1) - (at+2);
|
||||||
|
len = at;
|
||||||
|
}
|
||||||
break;
|
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
|
* Parse @{-N} syntax, return the number of characters parsed
|
||||||
* branch we were on, and places the name of the branch in the given
|
* if successful; otherwise signal an error with negative value.
|
||||||
* 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)
|
static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf)
|
||||||
{
|
{
|
||||||
long nth;
|
long nth;
|
||||||
int i, retval;
|
int i, retval;
|
||||||
@ -836,6 +847,60 @@ int get_sha1_mb(const char *name, unsigned char *sha1)
|
|||||||
return st;
|
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",
|
* This is like "get_sha1_basic()", except it allows "sha1 expressions",
|
||||||
* notably "xyz^" for "parent of xyz"
|
* 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
|
Reference in New Issue
Block a user