reset: allow reset on unborn branch

Some users seem to think, knowingly or not, that being on an unborn branch is like having a commit with an empty tree checked out, but when run on an unborn branch, "git reset" currently fails with: fatal: Failed to resolve 'HEAD' as a valid ref. Instead of making users figure out that they should run git rm --cached -r . , let's teach "git reset" without a revision argument, when on an unborn branch, to behave as if the user asked to reset to an empty tree. Don't take the analogy with an empty commit too far, though, but still disallow explictly referring to HEAD in "git reset HEAD". Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Martin von Zweigbergk committed Jan 14, 2013 at 21:47 UTC 166ec2e96e31bac913f44823dadd6c732d353172
2 files changed +62 -6
builtin/reset.c
+10 -6
@@ -240,7 +240,7 @@ static int update_refs(const char *rev, const unsigned char *sha1)
240 int cmd_reset(int argc, const char **argv, const char *prefix)
241 {
242 int reset_type = NONE, update_ref_status = 0, quiet = 0;
243 - int patch_mode = 0;
243 + int patch_mode = 0, unborn;
244 const char *rev;
245 unsigned char sha1[20];
246 const char **pathspec = NULL;
@@ -265,7 +265,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
265 PARSE_OPT_KEEP_DASHDASH);
266 pathspec = parse_args(argv, prefix, &rev);
267
268 - if (!pathspec) {
268 + unborn = !strcmp(rev, "HEAD") && get_sha1("HEAD", sha1);
269 + if (unborn) {
270 + /* reset on unborn branch: treat as reset to empty tree */
271 + hashcpy(sha1, EMPTY_TREE_SHA1_BIN);
272 + } else if (!pathspec) {
273 struct commit *commit;
274 if (get_sha1_committish(rev, sha1))
275 die(_("Failed to resolve '%s' as a valid revision."), rev);
@@ -286,7 +290,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
290 if (patch_mode) {
291 if (reset_type != NONE)
292 die(_("--patch is incompatible with --{hard,mixed,soft}"));
289 - return run_add_interactive(rev, "--patch=reset", pathspec);
293 + return run_add_interactive(sha1_to_hex(sha1), "--patch=reset", pathspec);
294 }
295
296 /* git reset tree [--] paths... can be used to
@@ -340,16 +344,16 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
344 die(_("Could not write new index file."));
345 }
346
343 - if (!pathspec) {
347 + if (!pathspec && !unborn) {
348 /* Any resets without paths update HEAD to the head being
349 * switched to, saving the previous head in ORIG_HEAD before. */
350 update_ref_status = update_refs(rev, sha1);
351
352 if (reset_type == HARD && !update_ref_status && !quiet)
353 print_new_head_line(lookup_commit_reference(sha1));
350 -
351 - remove_branch_state();
354 }
355 + if (!pathspec)
356 + remove_branch_state();
357
358 return update_ref_status;
359 }
t/t7106-reset-unborn-branch.sh new
+52
@@ -0,0 +1,52 @@
1 +#!/bin/sh
2 +
3 +test_description='git reset should work on unborn branch'
4 +. ./test-lib.sh
5 +
6 +test_expect_success 'setup' '
7 + echo a >a &&
8 + echo b >b
9 +'
10 +
11 +test_expect_success 'reset' '
12 + git add a b &&
13 + git reset &&
14 + test "$(git ls-files)" = ""
15 +'
16 +
17 +test_expect_success 'reset HEAD' '
18 + rm .git/index &&
19 + git add a b &&
20 + test_must_fail git reset HEAD
21 +'
22 +
23 +test_expect_success 'reset $file' '
24 + rm .git/index &&
25 + git add a b &&
26 + git reset a &&
27 + test "$(git ls-files)" = "b"
28 +'
29 +
30 +test_expect_success 'reset -p' '
31 + rm .git/index &&
32 + git add a &&
33 + echo y | git reset -p &&
34 + test "$(git ls-files)" = ""
35 +'
36 +
37 +test_expect_success 'reset --soft is a no-op' '
38 + rm .git/index &&
39 + git add a &&
40 + git reset --soft
41 + test "$(git ls-files)" = "a"
42 +'
43 +
44 +test_expect_success 'reset --hard' '
45 + rm .git/index &&
46 + git add a &&
47 + git reset --hard &&
48 + test "$(git ls-files)" = "" &&
49 + test_path_is_missing a
50 +'
51 +
52 +test_done