1. Stefan Saasen
  2. git

Commits

Junio C Hamano  committed dfc8f39

Allow "git-reset path" when unambiguous

Resetting a selected set of index entries is done with
"git reset -- paths" syntax, but we did not allow -- to be omitted
even when the command is unambiguous.

This updates the command to follow the general rule:

* When -- appears, revs come before it, and paths come after it;

* When there is no --, earlier ones are revs and the rest are paths, and
we need to guess. When lack of -- marker forces us to guess, we
protect from user errors and typoes by making sure what we treat as
revs do not appear as filenames in the work tree, and what we treat as
paths do appear as filenames in the work tree, and by erroring out if
that is not the case. We tell the user to disambiguate by using -- in
such a case.

which is employed elsewhere in the system.

When this rule is applied to "reset", because we can have only zero or one
rev to the command, the check can be slightly simpler than other programs.
We have to check only the first one or two tokens after the command name
and options, and when they are:

-- A:
no explicit rev given; "A" and whatever follows it are paths.

A --:
explicit rev "A" given and whatever follows the "--" are paths.

A B:
"A" could be rev or path and we need to guess. "B" could
be missing but if exists that (and everything that follows) would
be paths.

So we apply the guess only in the last case and only to "A" (not "B" and
what comes after it).

* As long as "A" is unambiguously a path, index entries for "A", "B" (and
everything that follows) are reset to the HEAD revision.

* If "A" is unambiguously a rev, on the other hand, the index entries for
"B" (and everything that follows) are reset to the "A" revision.

Signed-off-by: Junio C Hamano <gitster@pobox.com>

  • Participants
  • Parent commits 74b1e12
  • Branches master

Comments (0)

Files changed (2)

File builtin-reset.c

View file
  • Ignore whitespace
 	reflog_action = args_to_str(argv);
 	setenv("GIT_REFLOG_ACTION", reflog_action, 0);
 
-	if (i < argc && strcmp(argv[i], "--"))
-		rev = argv[i++];
+	/*
+	 * Possible arguments are:
+	 *
+	 * git reset [-opts] <rev> <paths>...
+	 * git reset [-opts] <rev> -- <paths>...
+	 * git reset [-opts] -- <paths>...
+	 * git reset [-opts] <paths>...
+	 *
+	 * At this point, argv[i] points immediately after [-opts].
+	 */
+
+	if (i < argc) {
+		if (!strcmp(argv[i], "--")) {
+			i++; /* reset to HEAD, possibly with paths */
+		} else if (i + 1 < argc && !strcmp(argv[i+1], "--")) {
+			rev = argv[i];
+			i += 2;
+		}
+		/*
+		 * Otherwise, argv[i] could be either <rev> or <paths> and
+		 * has to be unambigous.
+		 */
+		else if (!get_sha1(argv[i], sha1)) {
+			/*
+			 * Ok, argv[i] looks like a rev; it should not
+			 * be a filename.
+			 */
+			verify_non_filename(prefix, argv[i]);
+			rev = argv[i++];
+		} else {
+			/* Otherwise we treat this as a filename */
+			verify_filename(prefix, argv[i]);
+		}
+	}
 
 	if (get_sha1(rev, sha1))
 		die("Failed to resolve '%s' as a valid ref.", rev);
 		die("Could not parse object '%s'.", rev);
 	hashcpy(sha1, commit->object.sha1);
 
-	if (i < argc && !strcmp(argv[i], "--"))
-		i++;
-
 	/* git reset tree [--] paths... can be used to
 	 * load chosen paths from the tree into the index without
 	 * affecting the working tree nor HEAD. */

File t/t7102-reset.sh

View file
  • Ignore whitespace
 	test_cmp expect output
 '
 
+test_expect_success 'disambiguation (1)' '
+
+	git reset --hard &&
+	>secondfile &&
+	git add secondfile &&
+	test_must_fail git reset secondfile &&
+	test -z "$(git diff --cached --name-only)" &&
+	test -f secondfile &&
+	test ! -s secondfile
+
+'
+
+test_expect_success 'disambiguation (2)' '
+
+	git reset --hard &&
+	>secondfile &&
+	git add secondfile &&
+	rm -f secondfile &&
+	test_must_fail git reset secondfile &&
+	test -n "$(git diff --cached --name-only -- secondfile)" &&
+	test ! -f secondfile
+
+'
+
+test_expect_success 'disambiguation (3)' '
+
+	git reset --hard &&
+	>secondfile &&
+	git add secondfile &&
+	rm -f secondfile &&
+	test_must_fail git reset HEAD secondfile &&
+	test -z "$(git diff --cached --name-only)" &&
+	test ! -f secondfile
+
+'
+
+test_expect_success 'disambiguation (4)' '
+
+	git reset --hard &&
+	>secondfile &&
+	git add secondfile &&
+	rm -f secondfile &&
+	test_must_fail git reset -- secondfile &&
+	test -z "$(git diff --cached --name-only)" &&
+	test ! -f secondfile
+'
+
 test_done