@matheusbsbs / matheus-bagliano / commits / 28fb84382b
Fork 0

Introduce <branch>@{upstream} notation

A new notation '<branch>@{upstream}' refers to the branch <branch> is set to build on top of. Missing <branch> (i.e. '@{upstream}') defaults to the current branch. This allows you to run, for example, for l in list of local branches do git log --oneline --left-right $l...$l@{upstream} done to inspect each of the local branches you are interested in for the divergence from its upstream. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Sep 10, 2009 at 17:25 UTC 28fb84382b0eb728534dbe2972bbfec3f3d83dd9
3 files changed +109 -3
Documentation/git-rev-parse.txt
+4
@@ -231,6 +231,10 @@ when you run 'git-merge'.
231 * The special construct '@\{-<n>\}' means the <n>th branch checked out
232 before the current one.
233
234 +* The suffix '@{upstream}' to a ref (short form 'ref@{u}') refers to
235 + the branch the ref is set to build on top of. Missing ref defaults
236 + to the current branch.
237 +
238 * A suffix '{caret}' to a revision parameter means the first parent of
239 that commit object. '{caret}<n>' means the <n>th parent (i.e.
240 'rev{caret}'
sha1_name.c
+36 -3
@@ -5,6 +5,7 @@
5 #include "blob.h"
6 #include "tree-walk.h"
7 #include "refs.h"
8 +#include "remote.h"
9
10 static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
11 {
@@ -238,9 +239,24 @@ static int ambiguous_path(const char *path, int len)
239 return slash;
240 }
241
242 +static inline int tracked_suffix(const char *string, int len)
243 +{
244 + const char *suffix[] = { "@{upstream}", "@{u}" };
245 + int i;
246 +
247 + for (i = 0; i < ARRAY_SIZE(suffix); i++) {
248 + int suffix_len = strlen(suffix[i]);
249 + if (len >= suffix_len && !memcmp(string + len - suffix_len,
250 + suffix[i], suffix_len))
251 + return suffix_len;
252 + }
253 + return 0;
254 +}
255 +
256 /*
257 * *string and *len will only be substituted, and *string returned (for
243 - * later free()ing) if the string passed in is of the form @{-<n>}.
258 + * later free()ing) if the string passed in is of the form @{-<n>} or
259 + * of the form <branch>@{upstream}.
260 */
261 static char *substitute_branch_name(const char **string, int *len)
262 {
@@ -254,6 +270,21 @@ static char *substitute_branch_name(const char **string, int *len)
270 return (char *)*string;
271 }
272
273 + ret = tracked_suffix(*string, *len);
274 + if (ret) {
275 + char *ref = xstrndup(*string, *len - ret);
276 + struct branch *tracking = branch_get(*ref ? ref : NULL);
277 +
278 + if (!tracking)
279 + die ("No tracking branch found for '%s'", ref);
280 + free(ref);
281 + if (tracking->merge && tracking->merge[0]->dst) {
282 + *string = xstrdup(tracking->merge[0]->dst);
283 + *len = strlen(*string);
284 + return (char *)*string;
285 + }
286 + }
287 +
288 return NULL;
289 }
290
@@ -340,8 +371,10 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
371 if (len && str[len-1] == '}') {
372 for (at = len-2; at >= 0; at--) {
373 if (str[at] == '@' && str[at+1] == '{') {
343 - reflog_len = (len-1) - (at+2);
344 - len = at;
374 + if (!tracked_suffix(str + at, len - at)) {
375 + reflog_len = (len-1) - (at+2);
376 + len = at;
377 + }
378 break;
379 }
380 }
t/t1506-rev-parse-upstream.sh new
+69
@@ -0,0 +1,69 @@
1 +#!/bin/sh
2 +
3 +test_description='test <branch>@{upstream} syntax'
4 +
5 +. ./test-lib.sh
6 +
7 +
8 +test_expect_success 'setup' '
9 +
10 + test_commit 1 &&
11 + git checkout -b side &&
12 + test_commit 2 &&
13 + git checkout master &&
14 + git clone . clone &&
15 + test_commit 3 &&
16 + (cd clone &&
17 + test_commit 4 &&
18 + git branch --track my-side origin/side)
19 +
20 +'
21 +
22 +full_name () {
23 + (cd clone &&
24 + git rev-parse --symbolic-full-name "$@")
25 +}
26 +
27 +commit_subject () {
28 + (cd clone &&
29 + git show -s --pretty=format:%s "$@")
30 +}
31 +
32 +test_expect_success '@{upstream} resolves to correct full name' '
33 + test refs/remotes/origin/master = "$(full_name @{upstream})"
34 +'
35 +
36 +test_expect_success '@{u} resolves to correct full name' '
37 + test refs/remotes/origin/master = "$(full_name @{u})"
38 +'
39 +
40 +test_expect_success 'my-side@{upstream} resolves to correct full name' '
41 + test refs/remotes/origin/side = "$(full_name my-side@{u})"
42 +'
43 +
44 +test_expect_success 'my-side@{u} resolves to correct commit' '
45 + git checkout side &&
46 + test_commit 5 &&
47 + (cd clone && git fetch) &&
48 + test 2 = "$(commit_subject my-side)" &&
49 + test 5 = "$(commit_subject my-side@{u})"
50 +'
51 +
52 +test_expect_success 'not-tracking@{u} fails' '
53 + test_must_fail full_name non-tracking@{u} &&
54 + (cd clone && git checkout --no-track -b non-tracking) &&
55 + test_must_fail full_name non-tracking@{u}
56 +'
57 +
58 +test_expect_success '<branch>@{u}@{1} resolves correctly' '
59 + test_commit 6 &&
60 + (cd clone && git fetch) &&
61 + test 5 = $(commit_subject my-side@{u}@{1})
62 +'
63 +
64 +test_expect_success '@{u} without specifying branch fails on a detached HEAD' '
65 + git checkout HEAD^0 &&
66 + test_must_fail git rev-parse @{u}
67 +'
68 +
69 +test_done