Skip to content

Commit 41534b6

Browse files
committed
Merge branch 'jk/interpret-branch-name' into maint
"git branch @" created refs/heads/@ as a branch, and in general the code that handled @{-1} and @{upstream} was a bit too loose in disambiguating. * jk/interpret-branch-name: checkout: restrict @-expansions when finding branch strbuf_check_ref_format(): expand only local branches branch: restrict @-expansions when deleting t3204: test git-branch @-expansion corner cases interpret_branch_name: allow callers to restrict expansions strbuf_branchname: add docstring strbuf_branchname: drop return value interpret_branch_name: move docstring to header file interpret_branch_name(): handle auto-namelen for @{-1}
2 parents e25c122 + fd4692f commit 41534b6

File tree

10 files changed

+249
-51
lines changed

10 files changed

+249
-51
lines changed

builtin/branch.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,17 +190,20 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
190190
int ret = 0;
191191
int remote_branch = 0;
192192
struct strbuf bname = STRBUF_INIT;
193+
unsigned allowed_interpret;
193194

194195
switch (kinds) {
195196
case FILTER_REFS_REMOTES:
196197
fmt = "refs/remotes/%s";
197198
/* For subsequent UI messages */
198199
remote_branch = 1;
200+
allowed_interpret = INTERPRET_BRANCH_REMOTE;
199201

200202
force = 1;
201203
break;
202204
case FILTER_REFS_BRANCHES:
203205
fmt = "refs/heads/%s";
206+
allowed_interpret = INTERPRET_BRANCH_LOCAL;
204207
break;
205208
default:
206209
die(_("cannot use -a with -d"));
@@ -215,7 +218,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
215218
char *target = NULL;
216219
int flags = 0;
217220

218-
strbuf_branchname(&bname, argv[i]);
221+
strbuf_branchname(&bname, argv[i], allowed_interpret);
219222
free(name);
220223
name = mkpathdup(fmt, bname.buf);
221224

builtin/checkout.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ static void setup_branch_path(struct branch_info *branch)
452452
{
453453
struct strbuf buf = STRBUF_INIT;
454454

455-
strbuf_branchname(&buf, branch->name);
455+
strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
456456
if (strcmp(buf.buf, branch->name))
457457
branch->name = xstrdup(buf.buf);
458458
strbuf_splice(&buf, 0, 0, "refs/heads/", 11);

builtin/merge.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
438438
char *found_ref;
439439
int len, early;
440440

441-
strbuf_branchname(&bname, remote);
441+
strbuf_branchname(&bname, remote, 0);
442442
remote = bname.buf;
443443

444444
memset(branch_head, 0, sizeof(branch_head));

cache.h

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1316,7 +1316,37 @@ extern char *oid_to_hex_r(char *out, const struct object_id *oid);
13161316
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
13171317
extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
13181318

1319-
extern int interpret_branch_name(const char *str, int len, struct strbuf *);
1319+
/*
1320+
* This reads short-hand syntax that not only evaluates to a commit
1321+
* object name, but also can act as if the end user spelled the name
1322+
* of the branch from the command line.
1323+
*
1324+
* - "@{-N}" finds the name of the Nth previous branch we were on, and
1325+
* places the name of the branch in the given buf and returns the
1326+
* number of characters parsed if successful.
1327+
*
1328+
* - "<branch>@{upstream}" finds the name of the other ref that
1329+
* <branch> is configured to merge with (missing <branch> defaults
1330+
* to the current branch), and places the name of the branch in the
1331+
* given buf and returns the number of characters parsed if
1332+
* successful.
1333+
*
1334+
* If the input is not of the accepted format, it returns a negative
1335+
* number to signal an error.
1336+
*
1337+
* If the input was ok but there are not N branch switches in the
1338+
* reflog, it returns 0.
1339+
*
1340+
* If "allowed" is non-zero, it is a treated as a bitfield of allowable
1341+
* expansions: local branches ("refs/heads/"), remote branches
1342+
* ("refs/remotes/"), or "HEAD". If no "allowed" bits are set, any expansion is
1343+
* allowed, even ones to refs outside of those namespaces.
1344+
*/
1345+
#define INTERPRET_BRANCH_LOCAL (1<<0)
1346+
#define INTERPRET_BRANCH_REMOTE (1<<1)
1347+
#define INTERPRET_BRANCH_HEAD (1<<2)
1348+
extern int interpret_branch_name(const char *str, int len, struct strbuf *,
1349+
unsigned allowed);
13201350
extern int get_oid_mb(const char *str, struct object_id *oid);
13211351

13221352
extern int validate_headref(const char *ref);

refs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ int refname_match(const char *abbrev_name, const char *full_name)
404404
static char *substitute_branch_name(const char **string, int *len)
405405
{
406406
struct strbuf buf = STRBUF_INIT;
407-
int ret = interpret_branch_name(*string, *len, &buf);
407+
int ret = interpret_branch_name(*string, *len, &buf, 0);
408408

409409
if (ret == *len) {
410410
size_t size;

revision.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ static void add_pending_object_with_path(struct rev_info *revs,
147147
revs->no_walk = 0;
148148
if (revs->reflog_info && obj->type == OBJ_COMMIT) {
149149
struct strbuf buf = STRBUF_INIT;
150-
int len = interpret_branch_name(name, 0, &buf);
150+
int len = interpret_branch_name(name, 0, &buf, 0);
151151
int st;
152152

153153
if (0 < len && name[len] && buf.len)

sha1_name.c

Lines changed: 49 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,15 +1176,16 @@ static int interpret_empty_at(const char *name, int namelen, int len, struct str
11761176
return 1;
11771177
}
11781178

1179-
static int reinterpret(const char *name, int namelen, int len, struct strbuf *buf)
1179+
static int reinterpret(const char *name, int namelen, int len,
1180+
struct strbuf *buf, unsigned allowed)
11801181
{
11811182
/* we have extra data, which might need further processing */
11821183
struct strbuf tmp = STRBUF_INIT;
11831184
int used = buf->len;
11841185
int ret;
11851186

11861187
strbuf_add(buf, name + len, namelen - len);
1187-
ret = interpret_branch_name(buf->buf, buf->len, &tmp);
1188+
ret = interpret_branch_name(buf->buf, buf->len, &tmp, allowed);
11881189
/* that data was not interpreted, remove our cruft */
11891190
if (ret < 0) {
11901191
strbuf_setlen(buf, used);
@@ -1205,11 +1206,27 @@ static void set_shortened_ref(struct strbuf *buf, const char *ref)
12051206
free(s);
12061207
}
12071208

1209+
static int branch_interpret_allowed(const char *refname, unsigned allowed)
1210+
{
1211+
if (!allowed)
1212+
return 1;
1213+
1214+
if ((allowed & INTERPRET_BRANCH_LOCAL) &&
1215+
starts_with(refname, "refs/heads/"))
1216+
return 1;
1217+
if ((allowed & INTERPRET_BRANCH_REMOTE) &&
1218+
starts_with(refname, "refs/remotes/"))
1219+
return 1;
1220+
1221+
return 0;
1222+
}
1223+
12081224
static int interpret_branch_mark(const char *name, int namelen,
12091225
int at, struct strbuf *buf,
12101226
int (*get_mark)(const char *, int),
12111227
const char *(*get_data)(struct branch *,
1212-
struct strbuf *))
1228+
struct strbuf *),
1229+
unsigned allowed)
12131230
{
12141231
int len;
12151232
struct branch *branch;
@@ -1234,87 +1251,75 @@ static int interpret_branch_mark(const char *name, int namelen,
12341251
if (!value)
12351252
die("%s", err.buf);
12361253

1254+
if (!branch_interpret_allowed(value, allowed))
1255+
return -1;
1256+
12371257
set_shortened_ref(buf, value);
12381258
return len + at;
12391259
}
12401260

1241-
/*
1242-
* This reads short-hand syntax that not only evaluates to a commit
1243-
* object name, but also can act as if the end user spelled the name
1244-
* of the branch from the command line.
1245-
*
1246-
* - "@{-N}" finds the name of the Nth previous branch we were on, and
1247-
* places the name of the branch in the given buf and returns the
1248-
* number of characters parsed if successful.
1249-
*
1250-
* - "<branch>@{upstream}" finds the name of the other ref that
1251-
* <branch> is configured to merge with (missing <branch> defaults
1252-
* to the current branch), and places the name of the branch in the
1253-
* given buf and returns the number of characters parsed if
1254-
* successful.
1255-
*
1256-
* If the input is not of the accepted format, it returns a negative
1257-
* number to signal an error.
1258-
*
1259-
* If the input was ok but there are not N branch switches in the
1260-
* reflog, it returns 0.
1261-
*/
1262-
int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)
1261+
int interpret_branch_name(const char *name, int namelen, struct strbuf *buf,
1262+
unsigned allowed)
12631263
{
12641264
char *at;
12651265
const char *start;
1266-
int len = interpret_nth_prior_checkout(name, namelen, buf);
1266+
int len;
12671267

12681268
if (!namelen)
12691269
namelen = strlen(name);
12701270

1271-
if (!len) {
1272-
return len; /* syntax Ok, not enough switches */
1273-
} else if (len > 0) {
1274-
if (len == namelen)
1275-
return len; /* consumed all */
1276-
else
1277-
return reinterpret(name, namelen, len, buf);
1271+
if (!allowed || (allowed & INTERPRET_BRANCH_LOCAL)) {
1272+
len = interpret_nth_prior_checkout(name, namelen, buf);
1273+
if (!len) {
1274+
return len; /* syntax Ok, not enough switches */
1275+
} else if (len > 0) {
1276+
if (len == namelen)
1277+
return len; /* consumed all */
1278+
else
1279+
return reinterpret(name, namelen, len, buf, allowed);
1280+
}
12781281
}
12791282

12801283
for (start = name;
12811284
(at = memchr(start, '@', namelen - (start - name)));
12821285
start = at + 1) {
12831286

1284-
len = interpret_empty_at(name, namelen, at - name, buf);
1285-
if (len > 0)
1286-
return reinterpret(name, namelen, len, buf);
1287+
if (!allowed || (allowed & INTERPRET_BRANCH_HEAD)) {
1288+
len = interpret_empty_at(name, namelen, at - name, buf);
1289+
if (len > 0)
1290+
return reinterpret(name, namelen, len, buf,
1291+
allowed);
1292+
}
12871293

12881294
len = interpret_branch_mark(name, namelen, at - name, buf,
1289-
upstream_mark, branch_get_upstream);
1295+
upstream_mark, branch_get_upstream,
1296+
allowed);
12901297
if (len > 0)
12911298
return len;
12921299

12931300
len = interpret_branch_mark(name, namelen, at - name, buf,
1294-
push_mark, branch_get_push);
1301+
push_mark, branch_get_push,
1302+
allowed);
12951303
if (len > 0)
12961304
return len;
12971305
}
12981306

12991307
return -1;
13001308
}
13011309

1302-
int strbuf_branchname(struct strbuf *sb, const char *name)
1310+
void strbuf_branchname(struct strbuf *sb, const char *name, unsigned allowed)
13031311
{
13041312
int len = strlen(name);
1305-
int used = interpret_branch_name(name, len, sb);
1313+
int used = interpret_branch_name(name, len, sb, allowed);
13061314

1307-
if (used == len)
1308-
return 0;
13091315
if (used < 0)
13101316
used = 0;
13111317
strbuf_add(sb, name + used, len - used);
1312-
return len;
13131318
}
13141319

13151320
int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
13161321
{
1317-
strbuf_branchname(sb, name);
1322+
strbuf_branchname(sb, name, INTERPRET_BRANCH_LOCAL);
13181323
if (name[0] == '-')
13191324
return -1;
13201325
strbuf_splice(sb, 0, 0, "refs/heads/", 11);

strbuf.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,26 @@ static inline void strbuf_complete_line(struct strbuf *sb)
574574
strbuf_complete(sb, '\n');
575575
}
576576

577-
extern int strbuf_branchname(struct strbuf *sb, const char *name);
577+
/*
578+
* Copy "name" to "sb", expanding any special @-marks as handled by
579+
* interpret_branch_name(). The result is a non-qualified branch name
580+
* (so "foo" or "origin/master" instead of "refs/heads/foo" or
581+
* "refs/remotes/origin/master").
582+
*
583+
* Note that the resulting name may not be a syntactically valid refname.
584+
*
585+
* If "allowed" is non-zero, restrict the set of allowed expansions. See
586+
* interpret_branch_name() for details.
587+
*/
588+
extern void strbuf_branchname(struct strbuf *sb, const char *name,
589+
unsigned allowed);
590+
591+
/*
592+
* Like strbuf_branchname() above, but confirm that the result is
593+
* syntactically valid to be used as a local branch name in refs/heads/.
594+
*
595+
* The return value is "0" if the result is valid, and "-1" otherwise.
596+
*/
578597
extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
579598

580599
extern void strbuf_addstr_urlencode(struct strbuf *, const char *,

t/t0100-previous.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,13 @@ test_expect_success 'merge @{-100} before checking out that many branches yet' '
5656
test_must_fail git merge @{-100}
5757
'
5858

59+
test_expect_success 'log -g @{-1}' '
60+
git checkout -b last_branch &&
61+
git checkout -b new_branch &&
62+
echo "last_branch@{0}" >expect &&
63+
git log -g --format=%gd @{-1} >actual &&
64+
test_cmp expect actual
65+
'
66+
5967
test_done
6068

0 commit comments

Comments
 (0)