Skip to content

Commit 5a8ed3f

Browse files
spectre10gitster
authored andcommitted
add-patch: classify '@' as a synonym for 'HEAD'
Currently, (restore, checkout, reset) commands correctly take '@' as a synonym for 'HEAD'. However, in patch mode different prompts/messages are given on command line due to patch mode machinery not considering '@' to be a synonym for 'HEAD' due to literal string comparison with the word 'HEAD', and therefore assigning patch_mode_($command)_nothead and triggering reverse mode (-R in diff-index). The NEEDSWORK comment suggested comparing commit objects to get around this. However, doing so would also take a non-checked out branch pointing to the same commit as HEAD, as HEAD. This would cause confusion to the user. Therefore, after parsing '@', replace it with 'HEAD' as reasonably early as possible. This also solves another problem of disparity between 'git checkout HEAD' and 'git checkout @' (latter detaches at the HEAD commit and the former does not). Trade-offs: - Some of the errors would show the revision argument as 'HEAD' when given '@'. This should be fine, as most users who probably use '@' would be aware that it is a shortcut for 'HEAD' and most probably used to use 'HEAD'. There is also relevant documentation in 'gitrevisions' manpage about '@' being the shortcut for 'HEAD'. Also, the simplicity of the solution far outweighs this cost. - Consider '@' as a shortcut for 'HEAD' even if 'refs/heads/@' exists at a different commit. Naming a branch '@' is an obvious foot-gun and many existing commands already take '@' for 'HEAD' even if 'refs/heads/@' exists at a different commit or does not exist at all (e.g. 'git log @', 'git push origin @' etc.). Therefore this is an existing assumption and should not be a problem. Helped-by: Junio C Hamano <[email protected]> Helped-by: Phillip Wood <[email protected]> Signed-off-by: Ghanshyam Thakkar <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 2a540e4 commit 5a8ed3f

File tree

7 files changed

+65
-43
lines changed

7 files changed

+65
-43
lines changed

add-patch.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1729,14 +1729,6 @@ int run_add_p(struct repository *r, enum add_p_mode mode,
17291729
if (mode == ADD_P_STASH)
17301730
s.mode = &patch_mode_stash;
17311731
else if (mode == ADD_P_RESET) {
1732-
/*
1733-
* NEEDSWORK: Instead of comparing to the literal "HEAD",
1734-
* compare the commit objects instead so that other ways of
1735-
* saying the same thing (such as "@") are also handled
1736-
* appropriately.
1737-
*
1738-
* This applies to the cases below too.
1739-
*/
17401732
if (!revision || !strcmp(revision, "HEAD"))
17411733
s.mode = &patch_mode_reset_head;
17421734
else

builtin/checkout.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1224,7 +1224,9 @@ static void setup_new_branch_info_and_source_tree(
12241224
struct tree **source_tree = &opts->source_tree;
12251225
struct object_id branch_rev;
12261226

1227-
new_branch_info->name = xstrdup(arg);
1227+
/* treat '@' as a shortcut for 'HEAD' */
1228+
new_branch_info->name = !strcmp(arg, "@") ? xstrdup("HEAD") :
1229+
xstrdup(arg);
12281230
setup_branch_path(new_branch_info);
12291231

12301232
if (!check_refname_format(new_branch_info->path, 0) &&

builtin/reset.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,9 @@ static void parse_args(struct pathspec *pathspec,
281281
verify_filename(prefix, argv[0], 1);
282282
}
283283
}
284-
*rev_ret = rev;
284+
285+
/* treat '@' as a shortcut for 'HEAD' */
286+
*rev_ret = !strcmp("@", rev) ? "HEAD" : rev;
285287

286288
parse_pathspec(pathspec, 0,
287289
PATHSPEC_PREFER_FULL |

t/t2016-checkout-patch.sh

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,26 +38,32 @@ test_expect_success 'git checkout -p with staged changes' '
3838
verify_state dir/foo index index
3939
'
4040

41-
test_expect_success 'git checkout -p HEAD with NO staged changes: abort' '
42-
set_and_save_state dir/foo work head &&
43-
test_write_lines n y n | git checkout -p HEAD &&
44-
verify_saved_state bar &&
45-
verify_saved_state dir/foo
46-
'
47-
48-
test_expect_success 'git checkout -p HEAD with NO staged changes: apply' '
49-
test_write_lines n y y | git checkout -p HEAD &&
50-
verify_saved_state bar &&
51-
verify_state dir/foo head head
52-
'
53-
54-
test_expect_success 'git checkout -p HEAD with change already staged' '
55-
set_state dir/foo index index &&
56-
# the third n is to get out in case it mistakenly does not apply
57-
test_write_lines n y n | git checkout -p HEAD &&
58-
verify_saved_state bar &&
59-
verify_state dir/foo head head
60-
'
41+
for opt in "HEAD" "@"
42+
do
43+
test_expect_success "git checkout -p $opt with NO staged changes: abort" '
44+
set_and_save_state dir/foo work head &&
45+
test_write_lines n y n | git checkout -p $opt >output &&
46+
verify_saved_state bar &&
47+
verify_saved_state dir/foo &&
48+
test_grep "Discard" output
49+
'
50+
51+
test_expect_success "git checkout -p $opt with NO staged changes: apply" '
52+
test_write_lines n y y | git checkout -p $opt >output &&
53+
verify_saved_state bar &&
54+
verify_state dir/foo head head &&
55+
test_grep "Discard" output
56+
'
57+
58+
test_expect_success "git checkout -p $opt with change already staged" '
59+
set_state dir/foo index index &&
60+
# the third n is to get out in case it mistakenly does not apply
61+
test_write_lines n y n | git checkout -p $opt >output &&
62+
verify_saved_state bar &&
63+
verify_state dir/foo head head &&
64+
test_grep "Discard" output
65+
'
66+
done
6167

6268
test_expect_success 'git checkout -p HEAD^...' '
6369
# the third n is to get out in case it mistakenly does not apply

t/t2020-checkout-detach.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ test_expect_success 'checkout branch does not detach' '
4545
check_not_detached
4646
'
4747

48+
for opt in "HEAD" "@"
49+
do
50+
test_expect_success "checkout $opt no-op/don't detach" '
51+
reset &&
52+
cat .git/HEAD >expect &&
53+
git checkout $opt &&
54+
cat .git/HEAD >actual &&
55+
check_not_detached &&
56+
test_cmp expect actual
57+
'
58+
done
59+
4860
test_expect_success 'checkout tag detaches' '
4961
reset &&
5062
git checkout tag &&

t/t2071-restore-patch.sh

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,17 @@ test_expect_success PERL 'git restore -p with staged changes' '
4444
verify_state dir/foo index index
4545
'
4646

47-
test_expect_success PERL 'git restore -p --source=HEAD' '
48-
set_state dir/foo work index &&
49-
# the third n is to get out in case it mistakenly does not apply
50-
test_write_lines n y n | git restore -p --source=HEAD &&
51-
verify_saved_state bar &&
52-
verify_state dir/foo head index
53-
'
47+
for opt in "HEAD" "@"
48+
do
49+
test_expect_success PERL "git restore -p --source=$opt" '
50+
set_state dir/foo work index &&
51+
# the third n is to get out in case it mistakenly does not apply
52+
test_write_lines n y n | git restore -p --source=$opt >output &&
53+
verify_saved_state bar &&
54+
verify_state dir/foo head index &&
55+
test_grep "Discard" output
56+
'
57+
done
5458

5559
test_expect_success PERL 'git restore -p --source=HEAD^' '
5660
set_state dir/foo work index &&

t/t7105-reset-patch.sh

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,16 @@ test_expect_success PERL 'saying "n" does nothing' '
2626
verify_saved_state bar
2727
'
2828

29-
test_expect_success PERL 'git reset -p' '
30-
test_write_lines n y | git reset -p >output &&
31-
verify_state dir/foo work head &&
32-
verify_saved_state bar &&
33-
test_grep "Unstage" output
34-
'
29+
for opt in "HEAD" "@" ""
30+
do
31+
test_expect_success PERL "git reset -p $opt" '
32+
set_and_save_state dir/foo work work &&
33+
test_write_lines n y | git reset -p $opt >output &&
34+
verify_state dir/foo work head &&
35+
verify_saved_state bar &&
36+
test_grep "Unstage" output
37+
'
38+
done
3539

3640
test_expect_success PERL 'git reset -p HEAD^' '
3741
test_write_lines n y | git reset -p HEAD^ >output &&

0 commit comments

Comments
 (0)