rev-parse: match @{upstream}, @{u} and @{push} case-insensitively

Change the revision parsing logic to match @{upstream}, @{u} & @{push} case-insensitively. Before this change supplying anything except the lower-case forms emits an "unknown revision or path not in the working tree" error. This change makes upper-case & mixed-case versions equivalent to the lower-case versions. The use-case for this is being able to hold the shift key down while typing @{u} on certain keyboard layouts, which makes the sequence easier to type, and reduces cases where git throws an error at the user where it could do what he means instead. These suffixes now join various other suffixes & special syntax documented in gitrevisions(7) that matches case-insensitively. A table showing the status of the various forms documented there before & after this patch is shown below. The key for the table is: - CI = Case Insensitive - CIP = Case Insensitive Possible (without ambiguities) - AG = Accepts Garbage (.e.g. @{./.4.minutes./.}) Before this change: |----------------+-----+------+-----| | What? | CI? | CIP? | AG? | |----------------+-----+------+-----| | @{<date>} | Y | Y | Y | | @{upstream} | N | Y | N | | @{push} | N | Y | N | |----------------+-----+------+-----| After it: |----------------+-----+------+-----| | What? | CI? | CIP? | AG? | |----------------+-----+------+-----| | @{<date>} | Y | Y | Y | | @{upstream} | Y | Y | N | | @{push} | Y | Y | N | |----------------+-----+------+-----| The ^{<type>} suffix is not made case-insensitive, because other places that take <type> like "cat-file -t <type>" do want them case sensitively (after all we never declared that type names are case insensitive). Allowing case-insensitive typename only with this syntax will make the resulting Git as a whole inconsistent. This change was independently authored to scratch a longtime itch, but when I was about to submit it I discovered that a similar patch had been submitted unsuccessfully before by Conrad Irwin in August 2011 as "rev-parse: Allow @{U} as a synonym for @{u}" (<1313287071-7851-1-git-send-email-conrad.irwin@gmail.com>). The tests for this patch are more exhaustive than in the 2011 submission. The starting point for them was to first change the code to only support upper-case versions of the existing words, seeing what broke, and amending the breaking tests to check upper case & mixed case as appropriate, and where not redundant to other similar tests. The implementation itself is equivalent. Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ævar Arnfjörð Bjarmason committed Mar 27, 2017 at 11:16 UTC 244ea1b5e49081530e0ba5c717db1fdbe2bda73e
4 files changed +23 -8
Documentation/revisions.txt
+5 -1
@@ -96,7 +96,8 @@ some output processing may assume ref names in UTF-8.
96 refers to the branch that the branch specified by branchname is set to build on
97 top of (configured with `branch.<name>.remote` and
98 `branch.<name>.merge`). A missing branchname defaults to the
99 - current one.
99 + current one. These suffixes are also accepted when spelled in uppercase, and
100 + they mean the same thing no matter the case.
101
102 '<branchname>@\{push\}', e.g. 'master@\{push\}', '@\{push\}'::
103 The suffix '@\{push}' reports the branch "where we would push to" if
@@ -122,6 +123,9 @@ refs/remotes/myfork/mybranch
123 Note in the example that we set up a triangular workflow, where we pull
124 from one location and push to another. In a non-triangular workflow,
125 '@\{push}' is the same as '@\{upstream}', and there is no need for it.
126 ++
127 +This suffix is also accepted when spelled in uppercase, and means the same
128 +thing no matter the case.
129
130 '<rev>{caret}', e.g. 'HEAD{caret}, v1.5.1{caret}0'::
131 A suffix '{caret}' to a revision parameter means the first parent of
sha1_name.c
+1 -1
@@ -549,7 +549,7 @@ static inline int at_mark(const char *string, int len,
549 for (i = 0; i < nr; i++) {
550 int suffix_len = strlen(suffix[i]);
551 if (suffix_len <= len
552 - && !memcmp(string, suffix[i], suffix_len))
552 + && !strncasecmp(string, suffix[i], suffix_len))
553 return suffix_len;
554 }
555 return 0;
t/t1507-rev-parse-upstream.sh
+11 -4
@@ -46,11 +46,14 @@ error_message () {
46 }
47
48 test_expect_success '@{upstream} resolves to correct full name' '
49 - test refs/remotes/origin/master = "$(full_name @{upstream})"
49 + test refs/remotes/origin/master = "$(full_name @{upstream})" &&
50 + test refs/remotes/origin/master = "$(full_name @{UPSTREAM})" &&
51 + test refs/remotes/origin/master = "$(full_name @{UpSTReam})"
52 '
53
54 test_expect_success '@{u} resolves to correct full name' '
53 - test refs/remotes/origin/master = "$(full_name @{u})"
55 + test refs/remotes/origin/master = "$(full_name @{u})" &&
56 + test refs/remotes/origin/master = "$(full_name @{U})"
57 '
58
59 test_expect_success 'my-side@{upstream} resolves to correct full name' '
@@ -60,6 +63,8 @@ test_expect_success 'my-side@{upstream} resolves to correct full name' '
63 test_expect_success 'upstream of branch with @ in middle' '
64 full_name fun@ny@{u} >actual &&
65 echo refs/remotes/origin/side >expect &&
66 + test_cmp expect actual &&
67 + full_name fun@ny@{U} >actual &&
68 test_cmp expect actual
69 '
70
@@ -96,12 +101,14 @@ test_expect_success 'not-tracking@{u} fails' '
101 test_expect_success '<branch>@{u}@{1} resolves correctly' '
102 test_commit 6 &&
103 (cd clone && git fetch) &&
99 - test 5 = $(commit_subject my-side@{u}@{1})
104 + test 5 = $(commit_subject my-side@{u}@{1}) &&
105 + test 5 = $(commit_subject my-side@{U}@{1})
106 '
107
108 test_expect_success '@{u} without specifying branch fails on a detached HEAD' '
109 git checkout HEAD^0 &&
104 - test_must_fail git rev-parse @{u}
110 + test_must_fail git rev-parse @{u} &&
111 + test_must_fail git rev-parse @{U}
112 '
113
114 test_expect_success 'checkout -b new my-side@{u} forks from the same' '
t/t1514-rev-parse-push.sh
+6 -2
@@ -24,12 +24,16 @@ test_expect_success 'setup' '
24
25 test_expect_success '@{push} with default=nothing' '
26 test_config push.default nothing &&
27 - test_must_fail git rev-parse master@{push}
27 + test_must_fail git rev-parse master@{push} &&
28 + test_must_fail git rev-parse master@{PUSH} &&
29 + test_must_fail git rev-parse master@{PuSH}
30 '
31
32 test_expect_success '@{push} with default=simple' '
33 test_config push.default simple &&
32 - resolve master@{push} refs/remotes/origin/master
34 + resolve master@{push} refs/remotes/origin/master &&
35 + resolve master@{PUSH} refs/remotes/origin/master &&
36 + resolve master@{pUSh} refs/remotes/origin/master
37 '
38
39 test_expect_success 'triangular @{push} fails with default=simple' '