git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [PATCH 00/19] reset improvements
@ 2013-01-09  8:15 Martin von Zweigbergk
  2013-01-09  8:15 ` [PATCH 01/19] reset $pathspec: no need to discard index Martin von Zweigbergk
                   ` (19 more replies)
  0 siblings, 20 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:15 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

This is kind of a re-roll of [1] (wow, apparently it took me almost
two months to get done). The goal was, then and now, to teach "git
reset" to work on an unborn branch and to not require a commit when a
tree would do. This time, I also made some tangential improvements
along the way, mostly related to readability and performance.

As usual, the risker patches are towards the end. In particular, I
find it hard to evaluate how risky the last patch is. That last patch
is responsible for much of the improvements in the timing table below,
so it would be nice if it doesn't break things too badly (test pass,
of course). The timings are best-of-five, wall time.

Command                  Before     After
reset (warm)             0.23        0.07
reset -q (warm)          0.23        0.03
reset . (warm)           0.09        0.07
reset -q . (warm)        0.09        0.03
reset --keep (warm)      0.31        0.29
reset --keep -q (warm)   0.31        0.29
reset (cold)             9.74        2.60
reset -q (cold)          9.85        0.37
reset . (cold)           2.66        2.51
reset -q . (cold)        2.59        0.33
reset --keep (cold)      7.58        7.52
reset --keep -q (cold)   7.37        7.21



  [1] http://thread.gmane.org/gmane.comp.version-control.git/210568/focus=210855

Martin von Zweigbergk (19):
  reset $pathspec: no need to discard index
  reset $pathspec: exit with code 0 if successful
  reset.c: pass pathspec around instead of (prefix, argv) pair
  reset: don't allow "git reset -- $pathspec" in bare repo
  reset.c: extract function for parsing arguments
  reset.c: remove unnecessary variable 'i'
  reset.c: extract function for updating {ORIG,}HEAD
  reset.c: share call to die_if_unmerged_cache()
  reset.c: replace switch by if-else
  reset --keep: only write index file once
  reset: avoid redundant error message
  reset.c: move update_index_refresh() call out of read_from_tree()
  reset.c: move lock, write and commit out of update_index_refresh()
  reset [--mixed]: don't write index file twice
  reset.c: finish entire cmd_reset() whether or not pathspec is given
  reset [--mixed] --quiet: don't refresh index
  reset $sha1 $pathspec: require $sha1 only to be treeish
  reset: allow reset on unborn branch
  reset [--mixed]: use diff-based reset whether or not pathspec was
    given

 builtin/reset.c                | 281 +++++++++++++++++++----------------------
 t/t2013-checkout-submodule.sh  |   2 +-
 t/t7102-reset.sh               |  26 +++-
 t/t7106-reset-unborn-branch.sh |  52 ++++++++
 4 files changed, 200 insertions(+), 161 deletions(-)
 create mode 100755 t/t7106-reset-unborn-branch.sh

-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH 01/19] reset $pathspec: no need to discard index
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
@ 2013-01-09  8:15 ` Martin von Zweigbergk
  2013-01-09  8:15 ` [PATCH 02/19] reset $pathspec: exit with code 0 if successful Martin von Zweigbergk
                   ` (18 subsequent siblings)
  19 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:15 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

Since 34110cd (Make 'unpack_trees()' have a separate source and
destination index, 2008-03-06), the index no longer gets clobbered by
do_diff_cache() and we can remove the code for discarding and
re-reading it.

There are two paths to update_index_refresh() from cmd_reset(), but on
both paths, either read_cache() or read_cache_unmerged() will have
been called, so the call to read_cache() in this method is redundant
(although practically free).

This speeds up "git reset -- ." a little on the linux-2.6 repo (best
of five, warm cache):

        Before      After
real    0m0.093s    0m0.080s
user    0m0.040s    0m0.020s
sys     0m0.050s    0m0.050s
---
 builtin/reset.c | 16 +---------------
 1 file changed, 1 insertion(+), 15 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 915cc9f..8cc7c72 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -126,9 +126,6 @@ static int update_index_refresh(int fd, struct lock_file *index_lock, int flags)
 		fd = hold_locked_index(index_lock, 1);
 	}
 
-	if (read_cache() < 0)
-		return error(_("Could not read index"));
-
 	result = refresh_index(&the_index, (flags), NULL, NULL,
 			       _("Unstaged changes after reset:")) ? 1 : 0;
 	if (write_cache(fd, active_cache, active_nr) ||
@@ -141,12 +138,6 @@ static void update_index_from_diff(struct diff_queue_struct *q,
 		struct diff_options *opt, void *data)
 {
 	int i;
-	int *discard_flag = data;
-
-	/* do_diff_cache() mangled the index */
-	discard_cache();
-	*discard_flag = 1;
-	read_cache();
 
 	for (i = 0; i < q->nr; i++) {
 		struct diff_filespec *one = q->queue[i]->one;
@@ -179,17 +170,15 @@ static int read_from_tree(const char *prefix, const char **argv,
 		unsigned char *tree_sha1, int refresh_flags)
 {
 	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
-	int index_fd, index_was_discarded = 0;
+	int index_fd;
 	struct diff_options opt;
 
 	memset(&opt, 0, sizeof(opt));
 	diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt);
 	opt.output_format = DIFF_FORMAT_CALLBACK;
 	opt.format_callback = update_index_from_diff;
-	opt.format_callback_data = &index_was_discarded;
 
 	index_fd = hold_locked_index(lock, 1);
-	index_was_discarded = 0;
 	read_cache();
 	if (do_diff_cache(tree_sha1, &opt))
 		return 1;
@@ -197,9 +186,6 @@ static int read_from_tree(const char *prefix, const char **argv,
 	diff_flush(&opt);
 	diff_tree_release_paths(&opt);
 
-	if (!index_was_discarded)
-		/* The index is still clobbered from do_diff_cache() */
-		discard_cache();
 	return update_index_refresh(index_fd, lock, refresh_flags);
 }
 
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH 02/19] reset $pathspec: exit with code 0 if successful
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
  2013-01-09  8:15 ` [PATCH 01/19] reset $pathspec: no need to discard index Martin von Zweigbergk
@ 2013-01-09  8:15 ` Martin von Zweigbergk
  2013-01-09  8:16 ` [PATCH 03/19] reset.c: pass pathspec around instead of (prefix, argv) pair Martin von Zweigbergk
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:15 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

"git reset $pathspec" currently exits with a non-zero exit code if the
worktree is dirty after resetting, which is inconsistent with reset
without pathspec, and it makes it harder to know whether the command
really failed. Change it to exit with code 0 regardless of whether the
worktree is dirty so that non-zero indicates an error.

This makes the 4 "disambiguation" test cases in t7102 clearer since
they all used to "fail", 3 of which "failed" due to changes in the
work tree. Now only the ambiguous one fails.
---
I suppose this makes documenting the exit code unnecessary, since
"return zero iff successful" is probably understood to be the default.

The variable in refresh_index() containing the value to be returned is
called has_errors. I'm guessing I shouldn't take the name too
seriously.

 builtin/reset.c               |  8 +++-----
 t/t2013-checkout-submodule.sh |  2 +-
 t/t7102-reset.sh              | 18 ++++++++++++------
 3 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 8cc7c72..65413d0 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -119,19 +119,17 @@ static void print_new_head_line(struct commit *commit)
 
 static int update_index_refresh(int fd, struct lock_file *index_lock, int flags)
 {
-	int result;
-
 	if (!index_lock) {
 		index_lock = xcalloc(1, sizeof(struct lock_file));
 		fd = hold_locked_index(index_lock, 1);
 	}
 
-	result = refresh_index(&the_index, (flags), NULL, NULL,
-			       _("Unstaged changes after reset:")) ? 1 : 0;
+	refresh_index(&the_index, (flags), NULL, NULL,
+		      _("Unstaged changes after reset:"));
 	if (write_cache(fd, active_cache, active_nr) ||
 			commit_locked_index(index_lock))
 		return error ("Could not refresh index");
-	return result;
+	return 0;
 }
 
 static void update_index_from_diff(struct diff_queue_struct *q,
diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh
index 70edbb3..06b18f8 100755
--- a/t/t2013-checkout-submodule.sh
+++ b/t/t2013-checkout-submodule.sh
@@ -23,7 +23,7 @@ test_expect_success '"reset <submodule>" updates the index' '
 	git update-index --refresh &&
 	git diff-files --quiet &&
 	git diff-index --quiet --cached HEAD &&
-	test_must_fail git reset HEAD^ submodule &&
+	git reset HEAD^ submodule &&
 	test_must_fail git diff-files --quiet &&
 	git reset submodule &&
 	git diff-files --quiet
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
index b096dc8..81b2570 100755
--- a/t/t7102-reset.sh
+++ b/t/t7102-reset.sh
@@ -388,7 +388,8 @@ test_expect_success 'test --mixed <paths>' '
 	echo 4 > file4 &&
 	echo 5 > file1 &&
 	git add file1 file3 file4 &&
-	test_must_fail git reset HEAD -- file1 file2 file3 &&
+	git reset HEAD -- file1 file2 file3 &&
+	test_must_fail git diff --quiet &&
 	git diff > output &&
 	test_cmp output expect &&
 	git diff --cached > output &&
@@ -402,7 +403,8 @@ test_expect_success 'test resetting the index at give paths' '
 	>sub/file2 &&
 	git update-index --add sub/file1 sub/file2 &&
 	T=$(git write-tree) &&
-	test_must_fail git reset HEAD sub/file2 &&
+	git reset HEAD sub/file2 &&
+	test_must_fail git diff --quiet &&
 	U=$(git write-tree) &&
 	echo "$T" &&
 	echo "$U" &&
@@ -440,7 +442,8 @@ test_expect_success 'resetting specific path that is unmerged' '
 		echo "100644 $F3 3	file2"
 	} | git update-index --index-info &&
 	git ls-files -u &&
-	test_must_fail git reset HEAD file2 &&
+	git reset HEAD file2 &&
+	test_must_fail git diff --quiet &&
 	git diff-index --exit-code --cached HEAD
 '
 
@@ -449,7 +452,8 @@ test_expect_success 'disambiguation (1)' '
 	git reset --hard &&
 	>secondfile &&
 	git add secondfile &&
-	test_must_fail git reset secondfile &&
+	git reset secondfile &&
+	test_must_fail git diff --quiet -- secondfile &&
 	test -z "$(git diff --cached --name-only)" &&
 	test -f secondfile &&
 	test ! -s secondfile
@@ -474,7 +478,8 @@ test_expect_success 'disambiguation (3)' '
 	>secondfile &&
 	git add secondfile &&
 	rm -f secondfile &&
-	test_must_fail git reset HEAD secondfile &&
+	git reset HEAD secondfile &&
+	test_must_fail git diff --quiet &&
 	test -z "$(git diff --cached --name-only)" &&
 	test ! -f secondfile
 
@@ -486,7 +491,8 @@ test_expect_success 'disambiguation (4)' '
 	>secondfile &&
 	git add secondfile &&
 	rm -f secondfile &&
-	test_must_fail git reset -- secondfile &&
+	git reset -- secondfile &&
+	test_must_fail git diff --quiet &&
 	test -z "$(git diff --cached --name-only)" &&
 	test ! -f secondfile
 '
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH 03/19] reset.c: pass pathspec around instead of (prefix, argv) pair
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
  2013-01-09  8:15 ` [PATCH 01/19] reset $pathspec: no need to discard index Martin von Zweigbergk
  2013-01-09  8:15 ` [PATCH 02/19] reset $pathspec: exit with code 0 if successful Martin von Zweigbergk
@ 2013-01-09  8:16 ` Martin von Zweigbergk
  2013-01-09 11:42   ` Matt Kraai
  2013-01-09 19:26   ` Junio C Hamano
  2013-01-09  8:16 ` [PATCH 04/19] reset: don't allow "git reset -- $pathspec" in bare repo Martin von Zweigbergk
                   ` (16 subsequent siblings)
  19 siblings, 2 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

We use the path arguments in two places in reset.c: in
interactive_reset() and read_from_tree(). Both of these call
get_pathspec(), so we pass the (prefix, arv) pair to both
functions. Move the call to get_pathspec() out of these methods, for
two reasons: 1) One argument is simpler than two. 2) It lets us use
the (arguably clearer) "if (pathspec)" in place of "if (i < argc)".
---
If I understand correctly, this should be rebased on top of
nd/parse-pathspec. Please let me know.

 builtin/reset.c | 27 ++++++++++-----------------
 1 file changed, 10 insertions(+), 17 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 65413d0..045c960 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -153,26 +153,15 @@ static void update_index_from_diff(struct diff_queue_struct *q,
 	}
 }
 
-static int interactive_reset(const char *revision, const char **argv,
-			     const char *prefix)
-{
-	const char **pathspec = NULL;
-
-	if (*argv)
-		pathspec = get_pathspec(prefix, argv);
-
-	return run_add_interactive(revision, "--patch=reset", pathspec);
-}
-
-static int read_from_tree(const char *prefix, const char **argv,
-		unsigned char *tree_sha1, int refresh_flags)
+static int read_from_tree(const char **pathspec, unsigned char *tree_sha1,
+			  int refresh_flags)
 {
 	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 	int index_fd;
 	struct diff_options opt;
 
 	memset(&opt, 0, sizeof(opt));
-	diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt);
+	diff_tree_setup_paths(pathspec, &opt);
 	opt.output_format = DIFF_FORMAT_CALLBACK;
 	opt.format_callback = update_index_from_diff;
 
@@ -216,6 +205,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	const char *rev = "HEAD";
 	unsigned char sha1[20], *orig = NULL, sha1_orig[20],
 				*old_orig = NULL, sha1_old_orig[20];
+	const char **pathspec = NULL;
 	struct commit *commit;
 	struct strbuf msg = STRBUF_INIT;
 	const struct option options[] = {
@@ -287,22 +277,25 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		die(_("Could not parse object '%s'."), rev);
 	hashcpy(sha1, commit->object.sha1);
 
+	if (i < argc)
+		pathspec = get_pathspec(prefix, argv + i);
+
 	if (patch_mode) {
 		if (reset_type != NONE)
 			die(_("--patch is incompatible with --{hard,mixed,soft}"));
-		return interactive_reset(rev, argv + i, prefix);
+		return run_add_interactive(rev, "--patch=reset", pathspec);
 	}
 
 	/* git reset tree [--] paths... can be used to
 	 * load chosen paths from the tree into the index without
 	 * affecting the working tree nor HEAD. */
-	if (i < argc) {
+	if (pathspec) {
 		if (reset_type == MIXED)
 			warning(_("--mixed with paths is deprecated; use 'git reset -- <paths>' instead."));
 		else if (reset_type != NONE)
 			die(_("Cannot do %s reset with paths."),
 					_(reset_type_names[reset_type]));
-		return read_from_tree(prefix, argv + i, sha1,
+		return read_from_tree(pathspec, sha1,
 				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
 	}
 	if (reset_type == NONE)
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH 04/19] reset: don't allow "git reset -- $pathspec" in bare repo
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
                   ` (2 preceding siblings ...)
  2013-01-09  8:16 ` [PATCH 03/19] reset.c: pass pathspec around instead of (prefix, argv) pair Martin von Zweigbergk
@ 2013-01-09  8:16 ` Martin von Zweigbergk
  2013-01-09 19:32   ` Junio C Hamano
  2013-01-09  8:16 ` [PATCH 05/19] reset.c: extract function for parsing arguments Martin von Zweigbergk
                   ` (15 subsequent siblings)
  19 siblings, 1 reply; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

---
 builtin/reset.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 045c960..664fad9 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -295,8 +295,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		else if (reset_type != NONE)
 			die(_("Cannot do %s reset with paths."),
 					_(reset_type_names[reset_type]));
-		return read_from_tree(pathspec, sha1,
-				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
 	}
 	if (reset_type == NONE)
 		reset_type = MIXED; /* by default */
@@ -308,6 +306,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		die(_("%s reset is not allowed in a bare repository"),
 		    _(reset_type_names[reset_type]));
 
+	if (pathspec)
+		return read_from_tree(pathspec, sha1,
+				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+
 	/* Soft reset does not touch the index file nor the working tree
 	 * at all, but requires them in a good order.  Other resets reset
 	 * the index file to the tree object we are switching to. */
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH 05/19] reset.c: extract function for parsing arguments
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
                   ` (3 preceding siblings ...)
  2013-01-09  8:16 ` [PATCH 04/19] reset: don't allow "git reset -- $pathspec" in bare repo Martin von Zweigbergk
@ 2013-01-09  8:16 ` Martin von Zweigbergk
  2013-01-09  8:16 ` [PATCH 06/19] reset.c: remove unnecessary variable 'i' Martin von Zweigbergk
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

Declutter cmd_reset() a bit by moving out the argument parsing to its
own function.
---
 builtin/reset.c | 71 ++++++++++++++++++++++++++++++---------------------------
 1 file changed, 38 insertions(+), 33 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 664fad9..9473725 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -198,36 +198,10 @@ static void die_if_unmerged_cache(int reset_type)
 
 }
 
-int cmd_reset(int argc, const char **argv, const char *prefix)
-{
-	int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
-	int patch_mode = 0;
+const char **parse_args(int argc, const char **argv, const char *prefix, const char **rev_ret) {
+	int i = 0;
 	const char *rev = "HEAD";
-	unsigned char sha1[20], *orig = NULL, sha1_orig[20],
-				*old_orig = NULL, sha1_old_orig[20];
-	const char **pathspec = NULL;
-	struct commit *commit;
-	struct strbuf msg = STRBUF_INIT;
-	const struct option options[] = {
-		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
-		OPT_SET_INT(0, "mixed", &reset_type,
-						N_("reset HEAD and index"), MIXED),
-		OPT_SET_INT(0, "soft", &reset_type, N_("reset only HEAD"), SOFT),
-		OPT_SET_INT(0, "hard", &reset_type,
-				N_("reset HEAD, index and working tree"), HARD),
-		OPT_SET_INT(0, "merge", &reset_type,
-				N_("reset HEAD, index and working tree"), MERGE),
-		OPT_SET_INT(0, "keep", &reset_type,
-				N_("reset HEAD but keep local changes"), KEEP),
-		OPT_BOOLEAN('p', "patch", &patch_mode, N_("select hunks interactively")),
-		OPT_END()
-	};
-
-	git_config(git_default_config, NULL);
-
-	argc = parse_options(argc, argv, prefix, options, git_reset_usage,
-						PARSE_OPT_KEEP_DASHDASH);
-
+	unsigned char unused[20];
 	/*
 	 * Possible arguments are:
 	 *
@@ -250,7 +224,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		 * Otherwise, argv[i] could be either <rev> or <paths> and
 		 * has to be unambiguous.
 		 */
-		else if (!get_sha1_committish(argv[i], sha1)) {
+		else if (!get_sha1_committish(argv[i], unused)) {
 			/*
 			 * Ok, argv[i] looks like a rev; it should not
 			 * be a filename.
@@ -262,6 +236,40 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 			verify_filename(prefix, argv[i], 1);
 		}
 	}
+	*rev_ret = rev;
+	return i < argc ? get_pathspec(prefix, argv + i) : NULL;
+}
+
+int cmd_reset(int argc, const char **argv, const char *prefix)
+{
+	int reset_type = NONE, update_ref_status = 0, quiet = 0;
+	int patch_mode = 0;
+	const char *rev;
+	unsigned char sha1[20], *orig = NULL, sha1_orig[20],
+				*old_orig = NULL, sha1_old_orig[20];
+	const char **pathspec = NULL;
+	struct commit *commit;
+	struct strbuf msg = STRBUF_INIT;
+	const struct option options[] = {
+		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
+		OPT_SET_INT(0, "mixed", &reset_type,
+						N_("reset HEAD and index"), MIXED),
+		OPT_SET_INT(0, "soft", &reset_type, N_("reset only HEAD"), SOFT),
+		OPT_SET_INT(0, "hard", &reset_type,
+				N_("reset HEAD, index and working tree"), HARD),
+		OPT_SET_INT(0, "merge", &reset_type,
+				N_("reset HEAD, index and working tree"), MERGE),
+		OPT_SET_INT(0, "keep", &reset_type,
+				N_("reset HEAD but keep local changes"), KEEP),
+		OPT_BOOLEAN('p', "patch", &patch_mode, N_("select hunks interactively")),
+		OPT_END()
+	};
+
+	git_config(git_default_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, options, git_reset_usage,
+						PARSE_OPT_KEEP_DASHDASH);
+	pathspec = parse_args(argc, argv, prefix, &rev);
 
 	if (get_sha1_committish(rev, sha1))
 		die(_("Failed to resolve '%s' as a valid ref."), rev);
@@ -277,9 +285,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		die(_("Could not parse object '%s'."), rev);
 	hashcpy(sha1, commit->object.sha1);
 
-	if (i < argc)
-		pathspec = get_pathspec(prefix, argv + i);
-
 	if (patch_mode) {
 		if (reset_type != NONE)
 			die(_("--patch is incompatible with --{hard,mixed,soft}"));
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH 06/19] reset.c: remove unnecessary variable 'i'
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
                   ` (4 preceding siblings ...)
  2013-01-09  8:16 ` [PATCH 05/19] reset.c: extract function for parsing arguments Martin von Zweigbergk
@ 2013-01-09  8:16 ` Martin von Zweigbergk
  2013-01-09 19:39   ` Junio C Hamano
  2013-01-09  8:16 ` [PATCH 07/19] reset.c: extract function for updating {ORIG,}HEAD Martin von Zweigbergk
                   ` (13 subsequent siblings)
  19 siblings, 1 reply; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

Throughout most of parse_args(), the variable 'i' remains at 0. In the
remaining few cases, we can do pointer arithmentic on argv itself
instead.
---
This is clearly mostly a matter of taste. The remainder of the series
does not depend on it in any way.

 builtin/reset.c | 29 ++++++++++++++---------------
 1 file changed, 14 insertions(+), 15 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 9473725..68be05c 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -199,7 +199,6 @@ static void die_if_unmerged_cache(int reset_type)
 }
 
 const char **parse_args(int argc, const char **argv, const char *prefix, const char **rev_ret) {
-	int i = 0;
 	const char *rev = "HEAD";
 	unsigned char unused[20];
 	/*
@@ -210,34 +209,34 @@ const char **parse_args(int argc, const char **argv, const char *prefix, const c
 	 * git reset [-opts] -- <paths>...
 	 * git reset [-opts] <paths>...
 	 *
-	 * At this point, argv[i] points immediately after [-opts].
+	 * At this point, argv 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;
+	if (argc) {
+		if (!strcmp(argv[0], "--")) {
+			argv++; /* reset to HEAD, possibly with paths */
+		} else if (argc > 1 && !strcmp(argv[1], "--")) {
+			rev = argv[0];
+			argv += 2;
 		}
 		/*
-		 * Otherwise, argv[i] could be either <rev> or <paths> and
+		 * Otherwise, argv[0] could be either <rev> or <paths> and
 		 * has to be unambiguous.
 		 */
-		else if (!get_sha1_committish(argv[i], unused)) {
+		else if (!get_sha1_committish(argv[0], unused)) {
 			/*
-			 * Ok, argv[i] looks like a rev; it should not
+			 * Ok, argv[0] looks like a rev; it should not
 			 * be a filename.
 			 */
-			verify_non_filename(prefix, argv[i]);
-			rev = argv[i++];
+			verify_non_filename(prefix, argv[0]);
+			rev = *argv++;
 		} else {
 			/* Otherwise we treat this as a filename */
-			verify_filename(prefix, argv[i], 1);
+			verify_filename(prefix, argv[0], 1);
 		}
 	}
 	*rev_ret = rev;
-	return i < argc ? get_pathspec(prefix, argv + i) : NULL;
+	return *argv ? get_pathspec(prefix, argv) : NULL;
 }
 
 int cmd_reset(int argc, const char **argv, const char *prefix)
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH 07/19] reset.c: extract function for updating {ORIG,}HEAD
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
                   ` (5 preceding siblings ...)
  2013-01-09  8:16 ` [PATCH 06/19] reset.c: remove unnecessary variable 'i' Martin von Zweigbergk
@ 2013-01-09  8:16 ` Martin von Zweigbergk
  2013-01-09 11:54   ` Matt Kraai
  2013-01-09  8:16 ` [PATCH 08/19] reset.c: share call to die_if_unmerged_cache() Martin von Zweigbergk
                   ` (12 subsequent siblings)
  19 siblings, 1 reply; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

By extracting the code for updating the HEAD and ORIG_HEAD symbolic
references to a separate function, we declutter cmd_reset() a bit and
we make it clear that e.g. the four variables {,sha1_}{,old_}orig are
only used by this code.
---
 builtin/reset.c | 39 +++++++++++++++++++++++----------------
 1 file changed, 23 insertions(+), 16 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 68be05c..4d556e7 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -239,16 +239,35 @@ const char **parse_args(int argc, const char **argv, const char *prefix, const c
 	return *argv ? get_pathspec(prefix, argv) : NULL;
 }
 
+static int update_refs(const char *rev, const unsigned char *sha1) {
+	int update_ref_status;
+	struct strbuf msg = STRBUF_INIT;
+	unsigned char *orig = NULL, sha1_orig[20],
+		*old_orig = NULL, sha1_old_orig[20];
+
+	if (!get_sha1("ORIG_HEAD", sha1_old_orig))
+		old_orig = sha1_old_orig;
+	if (!get_sha1("HEAD", sha1_orig)) {
+		orig = sha1_orig;
+		set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
+		update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
+	}
+	else if (old_orig)
+		delete_ref("ORIG_HEAD", old_orig, 0);
+	set_reflog_message(&msg, "updating HEAD", rev);
+	update_ref_status = update_ref(msg.buf, "HEAD", sha1, orig, 0, MSG_ON_ERR);
+	strbuf_release(&msg);
+	return update_ref_status;
+}
+
 int cmd_reset(int argc, const char **argv, const char *prefix)
 {
 	int reset_type = NONE, update_ref_status = 0, quiet = 0;
 	int patch_mode = 0;
 	const char *rev;
-	unsigned char sha1[20], *orig = NULL, sha1_orig[20],
-				*old_orig = NULL, sha1_old_orig[20];
+	unsigned char sha1[20];
 	const char **pathspec = NULL;
 	struct commit *commit;
-	struct strbuf msg = STRBUF_INIT;
 	const struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_SET_INT(0, "mixed", &reset_type,
@@ -332,17 +351,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
 	/* Any resets update HEAD to the head being switched to,
 	 * saving the previous head in ORIG_HEAD before. */
-	if (!get_sha1("ORIG_HEAD", sha1_old_orig))
-		old_orig = sha1_old_orig;
-	if (!get_sha1("HEAD", sha1_orig)) {
-		orig = sha1_orig;
-		set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
-		update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
-	}
-	else if (old_orig)
-		delete_ref("ORIG_HEAD", old_orig, 0);
-	set_reflog_message(&msg, "updating HEAD", rev);
-	update_ref_status = update_ref(msg.buf, "HEAD", sha1, orig, 0, MSG_ON_ERR);
+	update_ref_status = update_refs(rev, sha1);
 
 	switch (reset_type) {
 	case HARD:
@@ -359,7 +368,5 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
 	remove_branch_state();
 
-	strbuf_release(&msg);
-
 	return update_ref_status;
 }
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH 08/19] reset.c: share call to die_if_unmerged_cache()
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
                   ` (6 preceding siblings ...)
  2013-01-09  8:16 ` [PATCH 07/19] reset.c: extract function for updating {ORIG,}HEAD Martin von Zweigbergk
@ 2013-01-09  8:16 ` Martin von Zweigbergk
  2013-01-09 19:48   ` Junio C Hamano
  2013-01-09  8:16 ` [PATCH 09/19] reset.c: replace switch by if-else Martin von Zweigbergk
                   ` (11 subsequent siblings)
  19 siblings, 1 reply; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

Use a single condition to guard the call to die_if_unmerged_cache for
both --soft and --keep. This avoids the small distraction of the
precondition check from the logic following it.

Also change an instance of

  if (e)
    err = err || f();

to the almost as short, but clearer

  if (e && !err)
    err = f();

(which is equivalent since we only care whether exit code is 0)
---
 builtin/reset.c | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 4d556e7..42d1563 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -336,15 +336,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	/* Soft reset does not touch the index file nor the working tree
 	 * at all, but requires them in a good order.  Other resets reset
 	 * the index file to the tree object we are switching to. */
-	if (reset_type == SOFT)
+	if (reset_type == SOFT || reset_type == KEEP)
 		die_if_unmerged_cache(reset_type);
-	else {
-		int err;
-		if (reset_type == KEEP)
-			die_if_unmerged_cache(reset_type);
-		err = reset_index_file(sha1, reset_type, quiet);
-		if (reset_type == KEEP)
-			err = err || reset_index_file(sha1, MIXED, quiet);
+
+	if (reset_type != SOFT) {
+		int err = reset_index_file(sha1, reset_type, quiet);
+		if (reset_type == KEEP && !err)
+			err = reset_index_file(sha1, MIXED, quiet);
 		if (err)
 			die(_("Could not reset index file to revision '%s'."), rev);
 	}
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH 09/19] reset.c: replace switch by if-else
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
                   ` (7 preceding siblings ...)
  2013-01-09  8:16 ` [PATCH 08/19] reset.c: share call to die_if_unmerged_cache() Martin von Zweigbergk
@ 2013-01-09  8:16 ` Martin von Zweigbergk
  2013-01-09 19:53   ` Junio C Hamano
  2013-01-09  8:16 ` [PATCH 10/19] reset --keep: only write index file once Martin von Zweigbergk
                   ` (10 subsequent siblings)
  19 siblings, 1 reply; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

---
 builtin/reset.c | 13 +++----------
 1 file changed, 3 insertions(+), 10 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 42d1563..05ccfd4 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -351,18 +351,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	 * saving the previous head in ORIG_HEAD before. */
 	update_ref_status = update_refs(rev, sha1);
 
-	switch (reset_type) {
-	case HARD:
-		if (!update_ref_status && !quiet)
-			print_new_head_line(commit);
-		break;
-	case SOFT: /* Nothing else to do. */
-		break;
-	case MIXED: /* Report what has not been updated. */
+	if (reset_type == HARD && !update_ref_status && !quiet)
+		print_new_head_line(commit);
+	else if (reset_type == MIXED) /* Report what has not been updated. */
 		update_index_refresh(0, NULL,
 				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
-		break;
-	}
 
 	remove_branch_state();
 
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH 10/19] reset --keep: only write index file once
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
                   ` (8 preceding siblings ...)
  2013-01-09  8:16 ` [PATCH 09/19] reset.c: replace switch by if-else Martin von Zweigbergk
@ 2013-01-09  8:16 ` Martin von Zweigbergk
  2013-01-09 19:55   ` Junio C Hamano
  2013-01-09  8:16 ` [PATCH 11/19] reset: avoid redundant error message Martin von Zweigbergk
                   ` (9 subsequent siblings)
  19 siblings, 1 reply; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

"git reset --keep" calls reset_index_file() twice, first doing a
two-way merge to the target revision, updating the index and worktree,
and then resetting the index. After each call, we write the index
file.

In the unlikely event that the second call to reset_index_file()
fails, the index will have been merged to the target revision, but
HEAD will not be updated, leaving the user with a dirty index.

By moving the locking, writing and committing out of
reset_index_file() and into the caller, we can avoid writing the index
twice, thereby making the sure we don't end up in the half-way reset
state. As a bonus, we speed up "git reset --keep" a little on the
linux-2.6 repo (best of five, warm cache):

        Before      After
real    0m0.315s    0m0.296s
user    0m0.290s    0m0.280s
sys     0m0.020s    0m0.010s
---
 builtin/reset.c | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 05ccfd4..8e5d097 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -38,14 +38,12 @@ static inline int is_merge(void)
 	return !access(git_path("MERGE_HEAD"), F_OK);
 }
 
-static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet)
+static int reset_index(const unsigned char *sha1, int reset_type, int quiet)
 {
 	int nr = 1;
-	int newfd;
 	struct tree_desc desc[2];
 	struct tree *tree;
 	struct unpack_trees_options opts;
-	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = 1;
@@ -67,8 +65,6 @@ static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet
 		opts.reset = 1;
 	}
 
-	newfd = hold_locked_index(lock, 1);
-
 	read_cache_unmerged();
 
 	if (reset_type == KEEP) {
@@ -91,10 +87,6 @@ static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet
 		prime_cache_tree(&active_cache_tree, tree);
 	}
 
-	if (write_cache(newfd, active_cache, active_nr) ||
-	    commit_locked_index(lock))
-		return error(_("Could not write new index file."));
-
 	return 0;
 }
 
@@ -340,9 +332,16 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		die_if_unmerged_cache(reset_type);
 
 	if (reset_type != SOFT) {
-		int err = reset_index_file(sha1, reset_type, quiet);
+		struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+		int newfd = hold_locked_index(lock, 1);
+		int err = reset_index(sha1, reset_type, quiet);
 		if (reset_type == KEEP && !err)
-			err = reset_index_file(sha1, MIXED, quiet);
+			err = reset_index(sha1, MIXED, quiet);
+		if (!err &&
+		    (write_cache(newfd, active_cache, active_nr) ||
+		     commit_locked_index(lock))) {
+			err = error(_("Could not write new index file."));
+		}
 		if (err)
 			die(_("Could not reset index file to revision '%s'."), rev);
 	}
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH 11/19] reset: avoid redundant error message
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
                   ` (9 preceding siblings ...)
  2013-01-09  8:16 ` [PATCH 10/19] reset --keep: only write index file once Martin von Zweigbergk
@ 2013-01-09  8:16 ` Martin von Zweigbergk
  2013-01-09  8:16 ` [PATCH 12/19] reset.c: move update_index_refresh() call out of read_from_tree() Martin von Zweigbergk
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

If writing or committing the new index file fails, we print "Could not
write new index file." followed by "Could not reset index file to
revision $rev.". The first message seems to imply the second, so print
only the first message.
---
 builtin/reset.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 8e5d097..54e3c5b 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -337,13 +337,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		int err = reset_index(sha1, reset_type, quiet);
 		if (reset_type == KEEP && !err)
 			err = reset_index(sha1, MIXED, quiet);
-		if (!err &&
-		    (write_cache(newfd, active_cache, active_nr) ||
-		     commit_locked_index(lock))) {
-			err = error(_("Could not write new index file."));
-		}
 		if (err)
 			die(_("Could not reset index file to revision '%s'."), rev);
+		if (write_cache(newfd, active_cache, active_nr) ||
+		    commit_locked_index(lock))
+			die(_("Could not write new index file."));
 	}
 
 	/* Any resets update HEAD to the head being switched to,
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH 12/19] reset.c: move update_index_refresh() call out of read_from_tree()
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
                   ` (10 preceding siblings ...)
  2013-01-09  8:16 ` [PATCH 11/19] reset: avoid redundant error message Martin von Zweigbergk
@ 2013-01-09  8:16 ` Martin von Zweigbergk
  2013-01-09  8:16 ` [PATCH 13/19] reset.c: move lock, write and commit out of update_index_refresh() Martin von Zweigbergk
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

The final part of cmd_reset() essentially looks like:

  if (pathspec) {
    ...
    read_from_tree(...);
  } else {
    ...
    reset_index(...);
    update_index_refresh(...);
    ...
  }

where read_from_tree() internally also calls
update_index_refresh(). Move the call to update_index_refresh() out of
read_from_tree for symmetry with the 'else' block, making
read_from_tree() and reset_index() closer in functionality.
---
 builtin/reset.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 54e3c5b..a21ba31 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -145,11 +145,8 @@ static void update_index_from_diff(struct diff_queue_struct *q,
 	}
 }
 
-static int read_from_tree(const char **pathspec, unsigned char *tree_sha1,
-			  int refresh_flags)
+static int read_from_tree(const char **pathspec, unsigned char *tree_sha1)
 {
-	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
-	int index_fd;
 	struct diff_options opt;
 
 	memset(&opt, 0, sizeof(opt));
@@ -157,7 +154,6 @@ static int read_from_tree(const char **pathspec, unsigned char *tree_sha1,
 	opt.output_format = DIFF_FORMAT_CALLBACK;
 	opt.format_callback = update_index_from_diff;
 
-	index_fd = hold_locked_index(lock, 1);
 	read_cache();
 	if (do_diff_cache(tree_sha1, &opt))
 		return 1;
@@ -165,7 +161,7 @@ static int read_from_tree(const char **pathspec, unsigned char *tree_sha1,
 	diff_flush(&opt);
 	diff_tree_release_paths(&opt);
 
-	return update_index_refresh(index_fd, lock, refresh_flags);
+	return 0;
 }
 
 static void set_reflog_message(struct strbuf *sb, const char *action,
@@ -321,9 +317,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		die(_("%s reset is not allowed in a bare repository"),
 		    _(reset_type_names[reset_type]));
 
-	if (pathspec)
-		return read_from_tree(pathspec, sha1,
-				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+	if (pathspec) {
+		struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+		int index_fd = hold_locked_index(lock, 1);
+		return read_from_tree(pathspec, sha1) ||
+			update_index_refresh(index_fd, lock,
+					quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+	}
 
 	/* Soft reset does not touch the index file nor the working tree
 	 * at all, but requires them in a good order.  Other resets reset
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH 13/19] reset.c: move lock, write and commit out of update_index_refresh()
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
                   ` (11 preceding siblings ...)
  2013-01-09  8:16 ` [PATCH 12/19] reset.c: move update_index_refresh() call out of read_from_tree() Martin von Zweigbergk
@ 2013-01-09  8:16 ` Martin von Zweigbergk
  2013-01-09  8:16 ` [PATCH 14/19] reset [--mixed]: don't write index file twice Martin von Zweigbergk
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

In preparation for the/a following patch, move the locking, writing
and committing of the index file out of update_index_refresh(). The
code duplication caused will soon be taken care of. What remains of
update_index_refresh() is just one line, but it is still called from
two places, so let's leave it for now.

In the process, we expose and fix the minor UI bug that makes us print
"Could not refresh index" when we fail to write the index file when
invoked with a pathspec. Copy the error message from the pathspec-less
codepath ("Could not write new index file.").
---
 builtin/reset.c | 34 ++++++++++++++++++----------------
 1 file changed, 18 insertions(+), 16 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index a21ba31..2243b95 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -109,19 +109,10 @@ static void print_new_head_line(struct commit *commit)
 		printf("\n");
 }
 
-static int update_index_refresh(int fd, struct lock_file *index_lock, int flags)
+static void update_index_refresh(int flags)
 {
-	if (!index_lock) {
-		index_lock = xcalloc(1, sizeof(struct lock_file));
-		fd = hold_locked_index(index_lock, 1);
-	}
-
 	refresh_index(&the_index, (flags), NULL, NULL,
 		      _("Unstaged changes after reset:"));
-	if (write_cache(fd, active_cache, active_nr) ||
-			commit_locked_index(index_lock))
-		return error ("Could not refresh index");
-	return 0;
 }
 
 static void update_index_from_diff(struct diff_queue_struct *q,
@@ -320,9 +311,14 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	if (pathspec) {
 		struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 		int index_fd = hold_locked_index(lock, 1);
-		return read_from_tree(pathspec, sha1) ||
-			update_index_refresh(index_fd, lock,
-					quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+		if (read_from_tree(pathspec, sha1))
+			return 1;
+		update_index_refresh(
+			quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+		if (write_cache(index_fd, active_cache, active_nr) ||
+		    commit_locked_index(lock))
+			return error("Could not write new index file.");
+		return 0;
 	}
 
 	/* Soft reset does not touch the index file nor the working tree
@@ -350,9 +346,15 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
 	if (reset_type == HARD && !update_ref_status && !quiet)
 		print_new_head_line(commit);
-	else if (reset_type == MIXED) /* Report what has not been updated. */
-		update_index_refresh(0, NULL,
-				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+	else if (reset_type == MIXED) { /* Report what has not been updated. */
+		struct lock_file *index_lock = xcalloc(1, sizeof(struct lock_file));
+		int fd = hold_locked_index(index_lock, 1);
+		update_index_refresh(
+			quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+		if (write_cache(fd, active_cache, active_nr) ||
+		    commit_locked_index(index_lock))
+			error("Could not refresh index");
+	}
 
 	remove_branch_state();
 
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH 14/19] reset [--mixed]: don't write index file twice
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
                   ` (12 preceding siblings ...)
  2013-01-09  8:16 ` [PATCH 13/19] reset.c: move lock, write and commit out of update_index_refresh() Martin von Zweigbergk
@ 2013-01-09  8:16 ` Martin von Zweigbergk
  2013-01-09  8:16 ` [PATCH 15/19] reset.c: finish entire cmd_reset() whether or not pathspec is given Martin von Zweigbergk
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

When doing a mixed reset without paths, the index is locked, read,
reset, and written back as part of the actual reset operation (in
reset_index()). Then, when showing the list of worktree modifications,
we lock the index again, refresh it, and write it.

Change this so we only write the index once, making "git reset" a
little faster. It does mean that the index lock will be held a little
longer, but the difference is small compared to the time spent
refreshing the index.

There is one minor functional difference: We used to say "Could not
write new index file." if the first write failed, and "Could not
refresh index" if the second write failed. Now, we will only use the
first message.

This speeds up "git reset" a little on the linux-2.6 repo (best of
five, warm cache):

        Before      After
real    0m0.239s    0m0.214s
user    0m0.160s    0m0.130s
sys     0m0.070s    0m0.080s
---
 builtin/reset.c | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 2243b95..254afa9 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -335,6 +335,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 			err = reset_index(sha1, MIXED, quiet);
 		if (err)
 			die(_("Could not reset index file to revision '%s'."), rev);
+
+		if (reset_type == MIXED) /* Report what has not been updated. */
+			update_index_refresh(
+				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+
 		if (write_cache(newfd, active_cache, active_nr) ||
 		    commit_locked_index(lock))
 			die(_("Could not write new index file."));
@@ -346,15 +351,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
 	if (reset_type == HARD && !update_ref_status && !quiet)
 		print_new_head_line(commit);
-	else if (reset_type == MIXED) { /* Report what has not been updated. */
-		struct lock_file *index_lock = xcalloc(1, sizeof(struct lock_file));
-		int fd = hold_locked_index(index_lock, 1);
-		update_index_refresh(
-			quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
-		if (write_cache(fd, active_cache, active_nr) ||
-		    commit_locked_index(index_lock))
-			error("Could not refresh index");
-	}
 
 	remove_branch_state();
 
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH 15/19] reset.c: finish entire cmd_reset() whether or not pathspec is given
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
                   ` (13 preceding siblings ...)
  2013-01-09  8:16 ` [PATCH 14/19] reset [--mixed]: don't write index file twice Martin von Zweigbergk
@ 2013-01-09  8:16 ` Martin von Zweigbergk
  2013-01-09 19:59   ` Junio C Hamano
  2013-01-09  8:16 ` [PATCH 16/19] reset [--mixed] --quiet: don't refresh index Martin von Zweigbergk
                   ` (4 subsequent siblings)
  19 siblings, 1 reply; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

By not returning from inside the "if (pathspec)" block, we can let the
pathspec-aware and pathspec-less code share a bit more, making it
easier to make future changes that should affect both cases. This also
highlights the similarity between read_from_tree() and reset_index().
---
Should error reporting be aligned too? Speaking of which,
do_diff_cache() never returns anything by 0. Is the return value for
future-proofing?

 builtin/reset.c | 42 ++++++++++++++++++------------------------
 1 file changed, 18 insertions(+), 24 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 254afa9..9bcad29 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -308,19 +308,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		die(_("%s reset is not allowed in a bare repository"),
 		    _(reset_type_names[reset_type]));
 
-	if (pathspec) {
-		struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
-		int index_fd = hold_locked_index(lock, 1);
-		if (read_from_tree(pathspec, sha1))
-			return 1;
-		update_index_refresh(
-			quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
-		if (write_cache(index_fd, active_cache, active_nr) ||
-		    commit_locked_index(lock))
-			return error("Could not write new index file.");
-		return 0;
-	}
-
 	/* Soft reset does not touch the index file nor the working tree
 	 * at all, but requires them in a good order.  Other resets reset
 	 * the index file to the tree object we are switching to. */
@@ -330,11 +317,16 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	if (reset_type != SOFT) {
 		struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 		int newfd = hold_locked_index(lock, 1);
-		int err = reset_index(sha1, reset_type, quiet);
-		if (reset_type == KEEP && !err)
-			err = reset_index(sha1, MIXED, quiet);
-		if (err)
-			die(_("Could not reset index file to revision '%s'."), rev);
+		if (pathspec) {
+			if (read_from_tree(pathspec, sha1))
+				return 1;
+		} else {
+			int err = reset_index(sha1, reset_type, quiet);
+			if (reset_type == KEEP && !err)
+				err = reset_index(sha1, MIXED, quiet);
+			if (err)
+				die(_("Could not reset index file to revision '%s'."), rev);
+		}
 
 		if (reset_type == MIXED) /* Report what has not been updated. */
 			update_index_refresh(
@@ -345,14 +337,16 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 			die(_("Could not write new index file."));
 	}
 
-	/* Any resets update HEAD to the head being switched to,
-	 * saving the previous head in ORIG_HEAD before. */
-	update_ref_status = update_refs(rev, sha1);
+	if (!pathspec) {
+		/* Any resets without paths update HEAD to the head being
+		 * switched to, saving the previous head in ORIG_HEAD before. */
+		update_ref_status = update_refs(rev, sha1);
 
-	if (reset_type == HARD && !update_ref_status && !quiet)
-		print_new_head_line(commit);
+		if (reset_type == HARD && !update_ref_status && !quiet)
+			print_new_head_line(commit);
 
-	remove_branch_state();
+		remove_branch_state();
+	}
 
 	return update_ref_status;
 }
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH 16/19] reset [--mixed] --quiet: don't refresh index
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
                   ` (14 preceding siblings ...)
  2013-01-09  8:16 ` [PATCH 15/19] reset.c: finish entire cmd_reset() whether or not pathspec is given Martin von Zweigbergk
@ 2013-01-09  8:16 ` Martin von Zweigbergk
  2013-01-09 17:01   ` Jeff King
  2013-01-09 20:05   ` Junio C Hamano
  2013-01-09  8:16 ` [PATCH 17/19] reset $sha1 $pathspec: require $sha1 only to be treeish Martin von Zweigbergk
                   ` (3 subsequent siblings)
  19 siblings, 2 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

"git reset [--mixed]" without --quiet refreshes the index in order to
display the "Unstaged changes after reset". When --quiet is given,
that output is suppressed, removing the need to refresh the index.
Other porcelain commands that care about a refreshed index should
already be refreshing it, so running e.g. "git reset -q && git diff"
is still safe.

This commit together with 686b2de (oneway_merge(): only lstat() when
told to update worktree, 2012-12-20) removes all calls to lstat() the
worktree from the command.

This speeds up "git reset -q" a little on the linux-2.6 repo (best
of five, warm cache):

        Before      After
real    0m0.215s    0m0.176s
user    0m0.150s    0m0.130s
sys     0m0.060s    0m0.040s

And with cold cache (best of five):

        Before      After
real    0m11.351s   0m8.420s
user    0m0.230s    0m0.220s
sys     0m0.270s    0m0.060s
---
There is a test case in t7102 called '--mixed refreshes the index',
but it only checks that right output it printed. Is the test case not
testing right or not named right? As you can see, I suspect it's the
name/description that should change.

 builtin/reset.c | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 9bcad29..a2e69eb 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -109,12 +109,6 @@ static void print_new_head_line(struct commit *commit)
 		printf("\n");
 }
 
-static void update_index_refresh(int flags)
-{
-	refresh_index(&the_index, (flags), NULL, NULL,
-		      _("Unstaged changes after reset:"));
-}
-
 static void update_index_from_diff(struct diff_queue_struct *q,
 		struct diff_options *opt, void *data)
 {
@@ -328,9 +322,9 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 				die(_("Could not reset index file to revision '%s'."), rev);
 		}
 
-		if (reset_type == MIXED) /* Report what has not been updated. */
-			update_index_refresh(
-				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+		if (reset_type == MIXED && !quiet) /* Report what has not been updated. */
+			refresh_index(&the_index, REFRESH_IN_PORCELAIN, NULL, NULL,
+				      _("Unstaged changes after reset:"));
 
 		if (write_cache(newfd, active_cache, active_nr) ||
 		    commit_locked_index(lock))
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH 17/19] reset $sha1 $pathspec: require $sha1 only to be treeish
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
                   ` (15 preceding siblings ...)
  2013-01-09  8:16 ` [PATCH 16/19] reset [--mixed] --quiet: don't refresh index Martin von Zweigbergk
@ 2013-01-09  8:16 ` Martin von Zweigbergk
  2013-01-09 20:23   ` Junio C Hamano
  2013-01-09  8:16 ` [PATCH 18/19] reset: allow reset on unborn branch Martin von Zweigbergk
                   ` (2 subsequent siblings)
  19 siblings, 1 reply; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

Resetting with paths does not update HEAD and there is nothing else
that a commit should be needed for. Relax the argument parsing so only
a tree is required.

The sha1 is only passed to read_from_tree(), which already only
requires a tree.

The "rev" variable we pass to run_add_interactive() will resolve to a
tree. This is fine since interactive_reset only needs the parameter to
be a treeish and doesn't use it for display purposes.
---
Is it correct that interactive_reset does not use the revision
specifier for display purposes? Or, worse, that it requires it to be a
commit in some cases? I tried it and didn't see any problem.

Can the two blocks of code that look up commit or tree be made to
share more? I'm not very familiar with what functions are available. I
think I tried keeping a separate "struct object *object" to be able to
put the last three lines outside the blocks, but didn't like the
result.

 builtin/reset.c  | 46 ++++++++++++++++++++++++++--------------------
 t/t7102-reset.sh |  8 ++++++++
 2 files changed, 34 insertions(+), 20 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index a2e69eb..4c223bd 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -177,9 +177,10 @@ const char **parse_args(int argc, const char **argv, const char *prefix, const c
 	/*
 	 * Possible arguments are:
 	 *
-	 * git reset [-opts] <rev> <paths>...
-	 * git reset [-opts] <rev> -- <paths>...
-	 * git reset [-opts] -- <paths>...
+	 * git reset [-opts] [<rev>]
+	 * git reset [-opts] <tree> [<paths>...]
+	 * git reset [-opts] <tree> -- [<paths>...]
+	 * git reset [-opts] -- [<paths>...]
 	 * git reset [-opts] <paths>...
 	 *
 	 * At this point, argv points immediately after [-opts].
@@ -194,11 +195,13 @@ const char **parse_args(int argc, const char **argv, const char *prefix, const c
 		}
 		/*
 		 * Otherwise, argv[0] could be either <rev> or <paths> and
-		 * has to be unambiguous.
+		 * has to be unambiguous. If there is a single argument, it
+		 * can not be a tree
 		 */
-		else if (!get_sha1_committish(argv[0], unused)) {
+		else if ((argc == 1 && !get_sha1_committish(argv[0], unused)) ||
+			 (argc > 1 && !get_sha1_treeish(argv[0], unused))) {
 			/*
-			 * Ok, argv[0] looks like a rev; it should not
+			 * Ok, argv[0] looks like a commit/tree; it should not
 			 * be a filename.
 			 */
 			verify_non_filename(prefix, argv[0]);
@@ -240,7 +243,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	const char *rev;
 	unsigned char sha1[20];
 	const char **pathspec = NULL;
-	struct commit *commit;
+	struct commit *commit = NULL;
 	const struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_SET_INT(0, "mixed", &reset_type,
@@ -262,19 +265,22 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 						PARSE_OPT_KEEP_DASHDASH);
 	pathspec = parse_args(argc, argv, prefix, &rev);
 
-	if (get_sha1_committish(rev, sha1))
-		die(_("Failed to resolve '%s' as a valid ref."), rev);
-
-	/*
-	 * NOTE: As "git reset $treeish -- $path" should be usable on
-	 * any tree-ish, this is not strictly correct. We are not
-	 * moving the HEAD to any commit; we are merely resetting the
-	 * entries in the index to that of a treeish.
-	 */
-	commit = lookup_commit_reference(sha1);
-	if (!commit)
-		die(_("Could not parse object '%s'."), rev);
-	hashcpy(sha1, commit->object.sha1);
+	if (!pathspec) {
+		if (get_sha1_committish(rev, sha1))
+			die(_("Failed to resolve '%s' as a valid revision."), rev);
+		commit = lookup_commit_reference(sha1);
+		if (!commit)
+			die(_("Could not parse object '%s'."), rev);
+		hashcpy(sha1, commit->object.sha1);
+	} else {
+		struct tree *tree;
+		if (get_sha1_treeish(rev, sha1))
+			die(_("Failed to resolve '%s' as a valid tree."), rev);
+		tree = parse_tree_indirect(sha1);
+		if (!tree)
+			die(_("Could not parse object '%s'."), rev);
+		hashcpy(sha1, tree->object.sha1);
+	}
 
 	if (patch_mode) {
 		if (reset_type != NONE)
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
index 81b2570..1fa2a5f 100755
--- a/t/t7102-reset.sh
+++ b/t/t7102-reset.sh
@@ -497,4 +497,12 @@ test_expect_success 'disambiguation (4)' '
 	test ! -f secondfile
 '
 
+test_expect_success 'reset with paths accepts tree' '
+	# for simpler tests, drop last commit containing added files
+	git reset --hard HEAD^ &&
+	git reset HEAD^^{tree} -- . &&
+	git diff --cached HEAD^ --exit-code &&
+	git diff HEAD --exit-code
+'
+
 test_done
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH 18/19] reset: allow reset on unborn branch
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
                   ` (16 preceding siblings ...)
  2013-01-09  8:16 ` [PATCH 17/19] reset $sha1 $pathspec: require $sha1 only to be treeish Martin von Zweigbergk
@ 2013-01-09  8:16 ` Martin von Zweigbergk
  2013-01-09  8:16 ` [PATCH 19/19] reset [--mixed]: use diff-based reset whether or not pathspec was given Martin von Zweigbergk
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
  19 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

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".
---
 builtin/reset.c                | 17 ++++++++------
 t/t7106-reset-unborn-branch.sh | 52 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 62 insertions(+), 7 deletions(-)
 create mode 100755 t/t7106-reset-unborn-branch.sh

diff --git a/builtin/reset.c b/builtin/reset.c
index 4c223bd..5cd48ac 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -239,7 +239,7 @@ static int update_refs(const char *rev, const unsigned char *sha1) {
 int cmd_reset(int argc, const char **argv, const char *prefix)
 {
 	int reset_type = NONE, update_ref_status = 0, quiet = 0;
-	int patch_mode = 0;
+	int patch_mode = 0, unborn;
 	const char *rev;
 	unsigned char sha1[20];
 	const char **pathspec = NULL;
@@ -264,8 +264,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, options, git_reset_usage,
 						PARSE_OPT_KEEP_DASHDASH);
 	pathspec = parse_args(argc, argv, prefix, &rev);
-
-	if (!pathspec) {
+	unborn = !strcmp(rev, "HEAD") && get_sha1("HEAD", sha1);
+	if (unborn) {
+		/* reset on unborn branch: treat as reset to empty tree */
+		hashcpy(sha1, EMPTY_TREE_SHA1_BIN);
+	} else if (!pathspec) {
 		if (get_sha1_committish(rev, sha1))
 			die(_("Failed to resolve '%s' as a valid revision."), rev);
 		commit = lookup_commit_reference(sha1);
@@ -285,7 +288,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	if (patch_mode) {
 		if (reset_type != NONE)
 			die(_("--patch is incompatible with --{hard,mixed,soft}"));
-		return run_add_interactive(rev, "--patch=reset", pathspec);
+		return run_add_interactive(sha1_to_hex(sha1), "--patch=reset", pathspec);
 	}
 
 	/* git reset tree [--] paths... can be used to
@@ -337,16 +340,16 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 			die(_("Could not write new index file."));
 	}
 
-	if (!pathspec) {
+	if (!pathspec && !unborn) {
 		/* Any resets without paths update HEAD to the head being
 		 * switched to, saving the previous head in ORIG_HEAD before. */
 		update_ref_status = update_refs(rev, sha1);
 
 		if (reset_type == HARD && !update_ref_status && !quiet)
 			print_new_head_line(commit);
-
-		remove_branch_state();
 	}
+	if (!pathspec)
+		remove_branch_state();
 
 	return update_ref_status;
 }
diff --git a/t/t7106-reset-unborn-branch.sh b/t/t7106-reset-unborn-branch.sh
new file mode 100755
index 0000000..4ff6af4
--- /dev/null
+++ b/t/t7106-reset-unborn-branch.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+test_description='git reset should work on unborn branch'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	echo a >a &&
+	echo b >b
+'
+
+test_expect_success 'reset' '
+	git add a b &&
+	git reset &&
+	test "$(git ls-files)" == ""
+'
+
+test_expect_success 'reset HEAD' '
+	rm .git/index &&
+	git add a b &&
+	test_must_fail git reset HEAD
+'
+
+test_expect_success 'reset $file' '
+	rm .git/index &&
+	git add a b &&
+	git reset a &&
+	test "$(git ls-files)" == "b"
+'
+
+test_expect_success 'reset -p' '
+	rm .git/index &&
+	git add a &&
+	echo y | git reset -p &&
+	test "$(git ls-files)" == ""
+'
+
+test_expect_success 'reset --soft is a no-op' '
+	rm .git/index &&
+	git add a &&
+	git reset --soft
+	test "$(git ls-files)" == "a"
+'
+
+test_expect_success 'reset --hard' '
+	rm .git/index &&
+	git add a &&
+	git reset --hard &&
+	test "$(git ls-files)" == "" &&
+	test_path_is_missing a
+'
+
+test_done
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH 19/19] reset [--mixed]: use diff-based reset whether or not pathspec was given
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
                   ` (17 preceding siblings ...)
  2013-01-09  8:16 ` [PATCH 18/19] reset: allow reset on unborn branch Martin von Zweigbergk
@ 2013-01-09  8:16 ` Martin von Zweigbergk
  2013-01-09 20:27   ` Junio C Hamano
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
  19 siblings, 1 reply; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09  8:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Martin von Zweigbergk

Thanks to b65982b (Optimize "diff-index --cached" using cache-tree,
2009-05-20), resetting with paths is much faster than resetting
without paths. Some timings for the linux-2.6 repo to illustrate this
(best of five, warm cache):

        reset       reset .
real    0m0.219s    0m0.080s
user    0m0.140s    0m0.040s
sys     0m0.070s    0m0.030s

These two commands should do the same thing, so instead of having the
user type the trailing " ." to get the faster do_diff_cache()-based
implementation, always use it when doing a mixed reset, with or
without paths (so "git reset $rev" would also be faster).

Comparing before and after (best of five):

                       Before     After
reset    (warm cache):   0.21      0.07
reset -q (warm cache)    0.17      0.03
reset    (cold cache):  10.31      2.72
reset -q (cold cache)    7.64      0.38
---
Are unmerged entries handled the same? read_from_tree() calls
read_cache(), while reset_index() calls read_cache_unmerged(). I
haven't figured out if/why they should be different.

Are there other differences, or could unpack_trees() learn the same
optimization as do_diff_cache()? Actually, the commit mentioned above
does say

  Tweak unpack_trees() logic that is used to read in the tree object
  to catch the case where the tree entry we are looking at matches the
  index as a whole by looking at the cache-tree.

If there are differences, we are clearly missing tests for them. And
it seems like any difference between them should be fixed, so "git
reset" and "git reset ." (from root of tree) do the same thing even
before this patch.

 builtin/reset.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 5cd48ac..6db0a10 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -320,7 +320,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	if (reset_type != SOFT) {
 		struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 		int newfd = hold_locked_index(lock, 1);
-		if (pathspec) {
+		if (reset_type == MIXED) {
 			if (read_from_tree(pathspec, sha1))
 				return 1;
 		} else {
-- 
1.8.1.rc3.331.g1ef2165

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* Re: [PATCH 03/19] reset.c: pass pathspec around instead of (prefix, argv) pair
  2013-01-09  8:16 ` [PATCH 03/19] reset.c: pass pathspec around instead of (prefix, argv) pair Martin von Zweigbergk
@ 2013-01-09 11:42   ` Matt Kraai
  2013-01-09 19:26   ` Junio C Hamano
  1 sibling, 0 replies; 68+ messages in thread
From: Matt Kraai @ 2013-01-09 11:42 UTC (permalink / raw)
  To: Martin von Zweigbergk; +Cc: git, Junio C Hamano

On Wed, Jan 09, 2013 at 12:16:00AM -0800, Martin von Zweigbergk wrote:
> We use the path arguments in two places in reset.c: in
> interactive_reset() and read_from_tree(). Both of these call
> get_pathspec(), so we pass the (prefix, arv) pair to both
                                          ^^^
argv is misspelled.

-- 
Matt

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 07/19] reset.c: extract function for updating {ORIG,}HEAD
  2013-01-09  8:16 ` [PATCH 07/19] reset.c: extract function for updating {ORIG,}HEAD Martin von Zweigbergk
@ 2013-01-09 11:54   ` Matt Kraai
  0 siblings, 0 replies; 68+ messages in thread
From: Matt Kraai @ 2013-01-09 11:54 UTC (permalink / raw)
  To: Martin von Zweigbergk; +Cc: git, Junio C Hamano

In the summary, {ORIG,} should be {ORIG_,}.

-- 
Matt

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 16/19] reset [--mixed] --quiet: don't refresh index
  2013-01-09  8:16 ` [PATCH 16/19] reset [--mixed] --quiet: don't refresh index Martin von Zweigbergk
@ 2013-01-09 17:01   ` Jeff King
  2013-01-09 18:43     ` Martin von Zweigbergk
  2013-01-09 20:05   ` Junio C Hamano
  1 sibling, 1 reply; 68+ messages in thread
From: Jeff King @ 2013-01-09 17:01 UTC (permalink / raw)
  To: Martin von Zweigbergk; +Cc: git, Junio C Hamano

On Wed, Jan 09, 2013 at 12:16:13AM -0800, Martin von Zweigbergk wrote:

> "git reset [--mixed]" without --quiet refreshes the index in order to
> display the "Unstaged changes after reset". When --quiet is given,
> that output is suppressed, removing the need to refresh the index.
> Other porcelain commands that care about a refreshed index should
> already be refreshing it, so running e.g. "git reset -q && git diff"
> is still safe.

Hmm. But "git reset -q && git diff-files" would not be?

We have never been very clear about which commands refresh the index.
Since "reset" is about manipulating the index, I'd expect it to be
refreshed afterwards. On the other hand, since we have never guaranteed
anything, perhaps a careful script should always use "git update-index
--refresh". I would not be too surprised if some of our own scripts are
not that careful, though.

-Peff

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 16/19] reset [--mixed] --quiet: don't refresh index
  2013-01-09 17:01   ` Jeff King
@ 2013-01-09 18:43     ` Martin von Zweigbergk
  2013-01-09 19:12       ` Junio C Hamano
  0 siblings, 1 reply; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09 18:43 UTC (permalink / raw)
  To: Jeff King; +Cc: git, Junio C Hamano

On Wed, Jan 9, 2013 at 9:01 AM, Jeff King <peff@peff.net> wrote:
> On Wed, Jan 09, 2013 at 12:16:13AM -0800, Martin von Zweigbergk wrote:
>
>> "git reset [--mixed]" without --quiet refreshes the index in order to
>> display the "Unstaged changes after reset". When --quiet is given,
>> that output is suppressed, removing the need to refresh the index.
>> Other porcelain commands that care about a refreshed index should
>> already be refreshing it, so running e.g. "git reset -q && git diff"
>> is still safe.
>
> Hmm. But "git reset -q && git diff-files" would not be?

Right. Actually, "git reset -q && git diff" was perhaps not a good
example, because its analogous plumbing command would be "git reset -q
&& git diff-files -p", which is also safe. But, as you say, "git reset
-q && git diff-files" (without -p) might list files for which only the
stat information has changed.

> We have never been very clear about which commands refresh the index.

Yes, git-reset's documentation doesn't mention it.

> Since "reset" is about manipulating the index, I'd expect it to be
> refreshed afterwards. On the other hand, since we have never guaranteed
> anything, perhaps a careful script should always use "git update-index
> --refresh".

Since "git diff-files" is a plumbing command, users of it to a
hopefully a bit more careful than regular users, but you never know.

> I would not be too surprised if some of our own scripts are
> not that careful, though.

I didn't find any, but I might have missed something.

Regardless, this patch was tangential. The goal of this series can be
achieved independently of this patch, so if it's too risky, we can
drop easily drop it.

Also, even though it does make "git reset -q" faster, I'm not sure how
important that is in practice. Most use cases would probably refresh
the index afterwards anyway. In such cases, the improvement on warm
cache would still be there, but the relative improvement in the cold
cache case would be pretty much gone (since the entire tree would be
stat'ed by the following refresh anyway).


Martin

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 16/19] reset [--mixed] --quiet: don't refresh index
  2013-01-09 18:43     ` Martin von Zweigbergk
@ 2013-01-09 19:12       ` Junio C Hamano
  2013-01-09 19:38         ` Martin von Zweigbergk
  0 siblings, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2013-01-09 19:12 UTC (permalink / raw)
  To: Martin von Zweigbergk; +Cc: Jeff King, git

Martin von Zweigbergk <martinvonz@gmail.com> writes:

>> We have never been very clear about which commands refresh the index.
>
> Yes, git-reset's documentation doesn't mention it.
>
>> Since "reset" is about manipulating the index, I'd expect it to be
>> refreshed afterwards. On the other hand, since we have never guaranteed
>> anything, perhaps a careful script should always use "git update-index
>> --refresh".
>
> Since "git diff-files" is a plumbing command, users of it to a
> hopefully a bit more careful than regular users, but you never know.
>
>> I would not be too surprised if some of our own scripts are
>> not that careful, though.
>
> I didn't find any, but I might have missed something.

contrib/examples/ have some, but looking at it makes me realize that
we have been fairly careful to avoid using "git reset" which is a
Porcelain.

And as a Porcelain, I would rather expect it to leave the resulting
index refreshed.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 03/19] reset.c: pass pathspec around instead of (prefix, argv) pair
  2013-01-09  8:16 ` [PATCH 03/19] reset.c: pass pathspec around instead of (prefix, argv) pair Martin von Zweigbergk
  2013-01-09 11:42   ` Matt Kraai
@ 2013-01-09 19:26   ` Junio C Hamano
  2013-01-10 11:05     ` Duy Nguyen
  1 sibling, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2013-01-09 19:26 UTC (permalink / raw)
  To: Martin von Zweigbergk; +Cc: git, Nguyễn Thái Ngọc Duy

Martin von Zweigbergk <martinvonz@gmail.com> writes:

> We use the path arguments in two places in reset.c: in
> interactive_reset() and read_from_tree(). Both of these call
> get_pathspec(), so we pass the (prefix, arv) pair to both
> functions. Move the call to get_pathspec() out of these methods, for
> two reasons: 1) One argument is simpler than two. 2) It lets us use
> the (arguably clearer) "if (pathspec)" in place of "if (i < argc)".
> ---
> If I understand correctly, this should be rebased on top of
> nd/parse-pathspec. Please let me know.

Yeah, this will conflict with the get_pathspec-to-parse_pathspec
conversion Duy has been working on.

Without the interactions with that topic, the conversion seems
straightforward to me, though.

>
>  builtin/reset.c | 27 ++++++++++-----------------
>  1 file changed, 10 insertions(+), 17 deletions(-)
>
> diff --git a/builtin/reset.c b/builtin/reset.c
> index 65413d0..045c960 100644
> --- a/builtin/reset.c
> +++ b/builtin/reset.c
> @@ -153,26 +153,15 @@ static void update_index_from_diff(struct diff_queue_struct *q,
>  	}
>  }
>  
> -static int interactive_reset(const char *revision, const char **argv,
> -			     const char *prefix)
> -{
> -	const char **pathspec = NULL;
> -
> -	if (*argv)
> -		pathspec = get_pathspec(prefix, argv);
> -
> -	return run_add_interactive(revision, "--patch=reset", pathspec);
> -}
> -
> -static int read_from_tree(const char *prefix, const char **argv,
> -		unsigned char *tree_sha1, int refresh_flags)
> +static int read_from_tree(const char **pathspec, unsigned char *tree_sha1,
> +			  int refresh_flags)
>  {
>  	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
>  	int index_fd;
>  	struct diff_options opt;
>  
>  	memset(&opt, 0, sizeof(opt));
> -	diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt);
> +	diff_tree_setup_paths(pathspec, &opt);
>  	opt.output_format = DIFF_FORMAT_CALLBACK;
>  	opt.format_callback = update_index_from_diff;
>  
> @@ -216,6 +205,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>  	const char *rev = "HEAD";
>  	unsigned char sha1[20], *orig = NULL, sha1_orig[20],
>  				*old_orig = NULL, sha1_old_orig[20];
> +	const char **pathspec = NULL;
>  	struct commit *commit;
>  	struct strbuf msg = STRBUF_INIT;
>  	const struct option options[] = {
> @@ -287,22 +277,25 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>  		die(_("Could not parse object '%s'."), rev);
>  	hashcpy(sha1, commit->object.sha1);
>  
> +	if (i < argc)
> +		pathspec = get_pathspec(prefix, argv + i);
> +
>  	if (patch_mode) {
>  		if (reset_type != NONE)
>  			die(_("--patch is incompatible with --{hard,mixed,soft}"));
> -		return interactive_reset(rev, argv + i, prefix);
> +		return run_add_interactive(rev, "--patch=reset", pathspec);
>  	}
>  
>  	/* git reset tree [--] paths... can be used to
>  	 * load chosen paths from the tree into the index without
>  	 * affecting the working tree nor HEAD. */
> -	if (i < argc) {
> +	if (pathspec) {
>  		if (reset_type == MIXED)
>  			warning(_("--mixed with paths is deprecated; use 'git reset -- <paths>' instead."));
>  		else if (reset_type != NONE)
>  			die(_("Cannot do %s reset with paths."),
>  					_(reset_type_names[reset_type]));
> -		return read_from_tree(prefix, argv + i, sha1,
> +		return read_from_tree(pathspec, sha1,
>  				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
>  	}
>  	if (reset_type == NONE)

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 04/19] reset: don't allow "git reset -- $pathspec" in bare repo
  2013-01-09  8:16 ` [PATCH 04/19] reset: don't allow "git reset -- $pathspec" in bare repo Martin von Zweigbergk
@ 2013-01-09 19:32   ` Junio C Hamano
  2013-01-10  8:24     ` Martin von Zweigbergk
  0 siblings, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2013-01-09 19:32 UTC (permalink / raw)
  To: Martin von Zweigbergk; +Cc: git

Martin von Zweigbergk <martinvonz@gmail.com> writes:

> ---
>  builtin/reset.c | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)

With the patch that does not have any explicit check for bareness
nor new error message to scold user with, it is rather hard to tell
what is going on, without any description on what (if anything) is
broken at the end user level and what remedy is done about that
breakage...

>
> diff --git a/builtin/reset.c b/builtin/reset.c
> index 045c960..664fad9 100644
> --- a/builtin/reset.c
> +++ b/builtin/reset.c
> @@ -295,8 +295,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>  		else if (reset_type != NONE)
>  			die(_("Cannot do %s reset with paths."),
>  					_(reset_type_names[reset_type]));
> -		return read_from_tree(pathspec, sha1,
> -				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
>  	}
>  	if (reset_type == NONE)
>  		reset_type = MIXED; /* by default */
> @@ -308,6 +306,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>  		die(_("%s reset is not allowed in a bare repository"),
>  		    _(reset_type_names[reset_type]));
>  
> +	if (pathspec)
> +		return read_from_tree(pathspec, sha1,
> +				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
> +
>  	/* Soft reset does not touch the index file nor the working tree
>  	 * at all, but requires them in a good order.  Other resets reset
>  	 * the index file to the tree object we are switching to. */

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 16/19] reset [--mixed] --quiet: don't refresh index
  2013-01-09 19:12       ` Junio C Hamano
@ 2013-01-09 19:38         ` Martin von Zweigbergk
  0 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-09 19:38 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jeff King, git

On Wed, Jan 9, 2013 at 11:12 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Martin von Zweigbergk <martinvonz@gmail.com> writes:
>
> And as a Porcelain, I would rather expect it to leave the resulting
> index refreshed.

Yeah, I guess you're right. Regular users (those using only porcelain)
shouldn't notice, but it does make sense to think that the index would
be refreshed after running a porcelain. And the risk of breaking
people's scripts seems real too. I'll drop patch this from the re-roll
(which I'll also make sure I'll sign off)

(FYI, the reason I wrote this patch was because I was surprised that
"git reset" did anything with the worktree at all.)

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 06/19] reset.c: remove unnecessary variable 'i'
  2013-01-09  8:16 ` [PATCH 06/19] reset.c: remove unnecessary variable 'i' Martin von Zweigbergk
@ 2013-01-09 19:39   ` Junio C Hamano
  2013-01-10  8:41     ` Martin von Zweigbergk
  0 siblings, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2013-01-09 19:39 UTC (permalink / raw)
  To: Martin von Zweigbergk; +Cc: git

Martin von Zweigbergk <martinvonz@gmail.com> writes:

> Throughout most of parse_args(), the variable 'i' remains at 0. In the
> remaining few cases, we can do pointer arithmentic on argv itself
> instead.
> ---
> This is clearly mostly a matter of taste. The remainder of the series
> does not depend on it in any way.

I agree that it indeed is a matter of taste between

 (1) look at av[i], check with (i < ac) for the end, and increment i to
     iterate over the arguments; and

 (2) look at av[0], check with (0 < ac) for the end, and increment
     av and decrement ac at the same time to iterate over the
     arguments.

When (ac, av) appear as a pair, however, adjusting only av without
adjusting ac is asking for future trouble.  It violates a common
expectation that av[ac] points at the NULL at the end of the list.

If a code chooses to use !av[0] as the terminating condition and
never looks at ac, then incrementing only av is fine, but in such a
case, the function probably should lose ac altogether.

>  builtin/reset.c | 29 ++++++++++++++---------------
>  1 file changed, 14 insertions(+), 15 deletions(-)
>
> diff --git a/builtin/reset.c b/builtin/reset.c
> index 9473725..68be05c 100644
> --- a/builtin/reset.c
> +++ b/builtin/reset.c
> @@ -199,7 +199,6 @@ static void die_if_unmerged_cache(int reset_type)
>  }
>  
>  const char **parse_args(int argc, const char **argv, const char *prefix, const char **rev_ret) {
> -	int i = 0;
>  	const char *rev = "HEAD";
>  	unsigned char unused[20];
>  	/*
> @@ -210,34 +209,34 @@ const char **parse_args(int argc, const char **argv, const char *prefix, const c
>  	 * git reset [-opts] -- <paths>...
>  	 * git reset [-opts] <paths>...
>  	 *
> -	 * At this point, argv[i] points immediately after [-opts].
> +	 * At this point, argv 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;
> +	if (argc) {
> +		if (!strcmp(argv[0], "--")) {
> +			argv++; /* reset to HEAD, possibly with paths */
> +		} else if (argc > 1 && !strcmp(argv[1], "--")) {
> +			rev = argv[0];
> +			argv += 2;
>  		}
>  		/*
> -		 * Otherwise, argv[i] could be either <rev> or <paths> and
> +		 * Otherwise, argv[0] could be either <rev> or <paths> and
>  		 * has to be unambiguous.
>  		 */
> -		else if (!get_sha1_committish(argv[i], unused)) {
> +		else if (!get_sha1_committish(argv[0], unused)) {
>  			/*
> -			 * Ok, argv[i] looks like a rev; it should not
> +			 * Ok, argv[0] looks like a rev; it should not
>  			 * be a filename.
>  			 */
> -			verify_non_filename(prefix, argv[i]);
> -			rev = argv[i++];
> +			verify_non_filename(prefix, argv[0]);
> +			rev = *argv++;
>  		} else {
>  			/* Otherwise we treat this as a filename */
> -			verify_filename(prefix, argv[i], 1);
> +			verify_filename(prefix, argv[0], 1);
>  		}
>  	}
>  	*rev_ret = rev;
> -	return i < argc ? get_pathspec(prefix, argv + i) : NULL;
> +	return *argv ? get_pathspec(prefix, argv) : NULL;
>  }
>  
>  int cmd_reset(int argc, const char **argv, const char *prefix)

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 08/19] reset.c: share call to die_if_unmerged_cache()
  2013-01-09  8:16 ` [PATCH 08/19] reset.c: share call to die_if_unmerged_cache() Martin von Zweigbergk
@ 2013-01-09 19:48   ` Junio C Hamano
  2013-01-10  8:51     ` Martin von Zweigbergk
  0 siblings, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2013-01-09 19:48 UTC (permalink / raw)
  To: Martin von Zweigbergk; +Cc: git

Martin von Zweigbergk <martinvonz@gmail.com> writes:

> Use a single condition to guard the call to die_if_unmerged_cache for
> both --soft and --keep. This avoids the small distraction of the
> precondition check from the logic following it.
>
> Also change an instance of
>
>   if (e)
>     err = err || f();
>
> to the almost as short, but clearer
>
>   if (e && !err)
>     err = f();
>
> (which is equivalent since we only care whether exit code is 0)

It is not just equivalent, but should give us identical result, even
if we cared the actual value.

And I tend to agree that the latter is more readable, especially
when f() can be longer, which is often the case in real life.

Happy to see this change.

> ---
>  builtin/reset.c | 14 ++++++--------
>  1 file changed, 6 insertions(+), 8 deletions(-)
>
> diff --git a/builtin/reset.c b/builtin/reset.c
> index 4d556e7..42d1563 100644
> --- a/builtin/reset.c
> +++ b/builtin/reset.c
> @@ -336,15 +336,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>  	/* Soft reset does not touch the index file nor the working tree
>  	 * at all, but requires them in a good order.  Other resets reset
>  	 * the index file to the tree object we are switching to. */
> -	if (reset_type == SOFT)
> +	if (reset_type == SOFT || reset_type == KEEP)
>  		die_if_unmerged_cache(reset_type);
> -	else {
> -		int err;
> -		if (reset_type == KEEP)
> -			die_if_unmerged_cache(reset_type);
> -		err = reset_index_file(sha1, reset_type, quiet);
> -		if (reset_type == KEEP)
> -			err = err || reset_index_file(sha1, MIXED, quiet);
> +
> +	if (reset_type != SOFT) {
> +		int err = reset_index_file(sha1, reset_type, quiet);
> +		if (reset_type == KEEP && !err)
> +			err = reset_index_file(sha1, MIXED, quiet);
>  		if (err)
>  			die(_("Could not reset index file to revision '%s'."), rev);
>  	}

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 09/19] reset.c: replace switch by if-else
  2013-01-09  8:16 ` [PATCH 09/19] reset.c: replace switch by if-else Martin von Zweigbergk
@ 2013-01-09 19:53   ` Junio C Hamano
  2013-01-11  6:35     ` Martin von Zweigbergk
  0 siblings, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2013-01-09 19:53 UTC (permalink / raw)
  To: Martin von Zweigbergk; +Cc: git

Martin von Zweigbergk <martinvonz@gmail.com> writes:

> ---
>  builtin/reset.c | 13 +++----------
>  1 file changed, 3 insertions(+), 10 deletions(-)
>
> diff --git a/builtin/reset.c b/builtin/reset.c
> index 42d1563..05ccfd4 100644
> --- a/builtin/reset.c
> +++ b/builtin/reset.c
> @@ -351,18 +351,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>  	 * saving the previous head in ORIG_HEAD before. */
>  	update_ref_status = update_refs(rev, sha1);
>  
> -	switch (reset_type) {
> -	case HARD:
> -		if (!update_ref_status && !quiet)
> -			print_new_head_line(commit);
> -		break;
> -	case SOFT: /* Nothing else to do. */
> -		break;
> -	case MIXED: /* Report what has not been updated. */
> +	if (reset_type == HARD && !update_ref_status && !quiet)
> +		print_new_head_line(commit);
> +	else if (reset_type == MIXED) /* Report what has not been updated. */
>  		update_index_refresh(0, NULL,
>  				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
> -		break;
> -	}

Justification?

It might be shorter, but I somehow find the original _much_ easier
to follow, and to possibly extend.  The case arms delineate the
major modes of operation, and when somebody is interested in what
happens in "reset --hard", the case labels allow eyes to immediately
spot and skip uninteresting case arms.  On the other hand, the
updated one forces you to read the if/else cascade through.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 10/19] reset --keep: only write index file once
  2013-01-09  8:16 ` [PATCH 10/19] reset --keep: only write index file once Martin von Zweigbergk
@ 2013-01-09 19:55   ` Junio C Hamano
  0 siblings, 0 replies; 68+ messages in thread
From: Junio C Hamano @ 2013-01-09 19:55 UTC (permalink / raw)
  To: Martin von Zweigbergk; +Cc: git

Martin von Zweigbergk <martinvonz@gmail.com> writes:

> "git reset --keep" calls reset_index_file() twice, first doing a
> two-way merge to the target revision, updating the index and worktree,
> and then resetting the index. After each call, we write the index
> file.
>
> In the unlikely event that the second call to reset_index_file()
> fails, the index will have been merged to the target revision, but
> HEAD will not be updated, leaving the user with a dirty index.
>
> By moving the locking, writing and committing out of
> reset_index_file() and into the caller, we can avoid writing the index
> twice, thereby making the sure we don't end up in the half-way reset
> state.

Nice.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 15/19] reset.c: finish entire cmd_reset() whether or not pathspec is given
  2013-01-09  8:16 ` [PATCH 15/19] reset.c: finish entire cmd_reset() whether or not pathspec is given Martin von Zweigbergk
@ 2013-01-09 19:59   ` Junio C Hamano
  0 siblings, 0 replies; 68+ messages in thread
From: Junio C Hamano @ 2013-01-09 19:59 UTC (permalink / raw)
  To: Martin von Zweigbergk; +Cc: git

Martin von Zweigbergk <martinvonz@gmail.com> writes:

> By not returning from inside the "if (pathspec)" block, we can let the
> pathspec-aware and pathspec-less code share a bit more, making it
> easier to make future changes that should affect both cases. This also
> highlights the similarity between read_from_tree() and reset_index().
> ---
> Should error reporting be aligned too? Speaking of which,
> do_diff_cache() never returns anything by 0. Is the return value for
> future-proofing?

Perhaps, and yes.

>
>  builtin/reset.c | 42 ++++++++++++++++++------------------------
>  1 file changed, 18 insertions(+), 24 deletions(-)
>
> diff --git a/builtin/reset.c b/builtin/reset.c
> index 254afa9..9bcad29 100644
> --- a/builtin/reset.c
> +++ b/builtin/reset.c
> @@ -308,19 +308,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>  		die(_("%s reset is not allowed in a bare repository"),
>  		    _(reset_type_names[reset_type]));
>  
> -	if (pathspec) {
> -		struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
> -		int index_fd = hold_locked_index(lock, 1);
> -		if (read_from_tree(pathspec, sha1))
> -			return 1;
> -		update_index_refresh(
> -			quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
> -		if (write_cache(index_fd, active_cache, active_nr) ||
> -		    commit_locked_index(lock))
> -			return error("Could not write new index file.");
> -		return 0;
> -	}
> -
>  	/* Soft reset does not touch the index file nor the working tree
>  	 * at all, but requires them in a good order.  Other resets reset
>  	 * the index file to the tree object we are switching to. */
> @@ -330,11 +317,16 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>  	if (reset_type != SOFT) {
>  		struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
>  		int newfd = hold_locked_index(lock, 1);
> -		int err = reset_index(sha1, reset_type, quiet);
> -		if (reset_type == KEEP && !err)
> -			err = reset_index(sha1, MIXED, quiet);
> -		if (err)
> -			die(_("Could not reset index file to revision '%s'."), rev);
> +		if (pathspec) {
> +			if (read_from_tree(pathspec, sha1))
> +				return 1;
> +		} else {
> +			int err = reset_index(sha1, reset_type, quiet);
> +			if (reset_type == KEEP && !err)
> +				err = reset_index(sha1, MIXED, quiet);
> +			if (err)
> +				die(_("Could not reset index file to revision '%s'."), rev);
> +		}
>  
>  		if (reset_type == MIXED) /* Report what has not been updated. */
>  			update_index_refresh(
> @@ -345,14 +337,16 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>  			die(_("Could not write new index file."));
>  	}
>  
> -	/* Any resets update HEAD to the head being switched to,
> -	 * saving the previous head in ORIG_HEAD before. */
> -	update_ref_status = update_refs(rev, sha1);
> +	if (!pathspec) {
> +		/* Any resets without paths update HEAD to the head being
> +		 * switched to, saving the previous head in ORIG_HEAD before. */
> +		update_ref_status = update_refs(rev, sha1);
>  
> -	if (reset_type == HARD && !update_ref_status && !quiet)
> -		print_new_head_line(commit);
> +		if (reset_type == HARD && !update_ref_status && !quiet)
> +			print_new_head_line(commit);
>  
> -	remove_branch_state();
> +		remove_branch_state();
> +	}
>  
>  	return update_ref_status;
>  }

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 16/19] reset [--mixed] --quiet: don't refresh index
  2013-01-09  8:16 ` [PATCH 16/19] reset [--mixed] --quiet: don't refresh index Martin von Zweigbergk
  2013-01-09 17:01   ` Jeff King
@ 2013-01-09 20:05   ` Junio C Hamano
  1 sibling, 0 replies; 68+ messages in thread
From: Junio C Hamano @ 2013-01-09 20:05 UTC (permalink / raw)
  To: Martin von Zweigbergk; +Cc: git

Martin von Zweigbergk <martinvonz@gmail.com> writes:

> There is a test case in t7102 called '--mixed refreshes the index',
> but it only checks that right output it printed.

I think that comes from 620a6cd (builtin-reset: avoid forking
"update-index --refresh", 2007-11-03).  Before that commit, we
refreshed the index with --mixed, and the test tries to make sure we
continue to do so after the change.  Even though it is not testing
if the index has stat only changes (which is rather cumbersome to
write---you need to futz with timestamp or something) and using the
output from refresh machinery as a substitute, I think the intent of
that commit is fairly clear.

>  builtin/reset.c | 12 +++---------
>  1 file changed, 3 insertions(+), 9 deletions(-)
>
> diff --git a/builtin/reset.c b/builtin/reset.c
> index 9bcad29..a2e69eb 100644
> --- a/builtin/reset.c
> +++ b/builtin/reset.c
> @@ -109,12 +109,6 @@ static void print_new_head_line(struct commit *commit)
>  		printf("\n");
>  }
>  
> -static void update_index_refresh(int flags)
> -{
> -	refresh_index(&the_index, (flags), NULL, NULL,
> -		      _("Unstaged changes after reset:"));
> -}
> -
>  static void update_index_from_diff(struct diff_queue_struct *q,
>  		struct diff_options *opt, void *data)
>  {
> @@ -328,9 +322,9 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>  				die(_("Could not reset index file to revision '%s'."), rev);
>  		}
>  
> -		if (reset_type == MIXED) /* Report what has not been updated. */
> -			update_index_refresh(
> -				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
> +		if (reset_type == MIXED && !quiet) /* Report what has not been updated. */
> +			refresh_index(&the_index, REFRESH_IN_PORCELAIN, NULL, NULL,
> +				      _("Unstaged changes after reset:"));
>  
>  		if (write_cache(newfd, active_cache, active_nr) ||
>  		    commit_locked_index(lock))

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 17/19] reset $sha1 $pathspec: require $sha1 only to be treeish
  2013-01-09  8:16 ` [PATCH 17/19] reset $sha1 $pathspec: require $sha1 only to be treeish Martin von Zweigbergk
@ 2013-01-09 20:23   ` Junio C Hamano
  0 siblings, 0 replies; 68+ messages in thread
From: Junio C Hamano @ 2013-01-09 20:23 UTC (permalink / raw)
  To: Martin von Zweigbergk; +Cc: git

Martin von Zweigbergk <martinvonz@gmail.com> writes:

> Resetting with paths does not update HEAD and there is nothing else
> that a commit should be needed for. Relax the argument parsing so only
> a tree is required.
>
> The sha1 is only passed to read_from_tree(), which already only
> requires a tree.
>
> The "rev" variable we pass to run_add_interactive() will resolve to a
> tree. This is fine since interactive_reset only needs the parameter to
> be a treeish and doesn't use it for display purposes.
> ---
> Is it correct that interactive_reset does not use the revision
> specifier for display purposes? Or, worse, that it requires it to be a
> commit in some cases? I tried it and didn't see any problem.

As far as I know, it is only given to git-diff-index as the tree-ish,
and resulting patch text is used for application via git-apply just
like any patch coming from any origin, so I think it should be fine.

> Can the two blocks of code that look up commit or tree be made to
> share more? I'm not very familiar with what functions are available. I
> think I tried keeping a separate "struct object *object" to be able to
> put the last three lines outside the blocks, but didn't like the
> result.

I think the patch looks fine from the sharing perspective, but it
may be even nicer to have a separate variable to hold a commit
object limited to the scope of if (!pathspec) block to make them
more symmetric.  The commit is only needed later to show "we are now
at this commit", but that code can find the commit itself given the
object name in sha1[].

>  builtin/reset.c  | 46 ++++++++++++++++++++++++++--------------------
>  t/t7102-reset.sh |  8 ++++++++
>  2 files changed, 34 insertions(+), 20 deletions(-)
>
> diff --git a/builtin/reset.c b/builtin/reset.c
> index a2e69eb..4c223bd 100644
> --- a/builtin/reset.c
> +++ b/builtin/reset.c
> @@ -177,9 +177,10 @@ const char **parse_args(int argc, const char **argv, const char *prefix, const c
>  	/*
>  	 * Possible arguments are:
>  	 *
> -	 * git reset [-opts] <rev> <paths>...
> -	 * git reset [-opts] <rev> -- <paths>...
> -	 * git reset [-opts] -- <paths>...
> +	 * git reset [-opts] [<rev>]
> +	 * git reset [-opts] <tree> [<paths>...]
> +	 * git reset [-opts] <tree> -- [<paths>...]
> +	 * git reset [-opts] -- [<paths>...]
>  	 * git reset [-opts] <paths>...
>  	 *
>  	 * At this point, argv points immediately after [-opts].
> @@ -194,11 +195,13 @@ const char **parse_args(int argc, const char **argv, const char *prefix, const c
>  		}
>  		/*
>  		 * Otherwise, argv[0] could be either <rev> or <paths> and
> -		 * has to be unambiguous.
> +		 * has to be unambiguous. If there is a single argument, it
> +		 * can not be a tree
>  		 */
> -		else if (!get_sha1_committish(argv[0], unused)) {
> +		else if ((argc == 1 && !get_sha1_committish(argv[0], unused)) ||
> +			 (argc > 1 && !get_sha1_treeish(argv[0], unused))) {
>  			/*
> -			 * Ok, argv[0] looks like a rev; it should not
> +			 * Ok, argv[0] looks like a commit/tree; it should not
>  			 * be a filename.
>  			 */
>  			verify_non_filename(prefix, argv[0]);
> @@ -240,7 +243,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>  	const char *rev;
>  	unsigned char sha1[20];
>  	const char **pathspec = NULL;
> -	struct commit *commit;
> +	struct commit *commit = NULL;
>  	const struct option options[] = {
>  		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
>  		OPT_SET_INT(0, "mixed", &reset_type,
> @@ -262,19 +265,22 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>  						PARSE_OPT_KEEP_DASHDASH);
>  	pathspec = parse_args(argc, argv, prefix, &rev);
>  
> -	if (get_sha1_committish(rev, sha1))
> -		die(_("Failed to resolve '%s' as a valid ref."), rev);
> -
> -	/*
> -	 * NOTE: As "git reset $treeish -- $path" should be usable on
> -	 * any tree-ish, this is not strictly correct. We are not
> -	 * moving the HEAD to any commit; we are merely resetting the
> -	 * entries in the index to that of a treeish.
> -	 */
> -	commit = lookup_commit_reference(sha1);
> -	if (!commit)
> -		die(_("Could not parse object '%s'."), rev);
> -	hashcpy(sha1, commit->object.sha1);
> +	if (!pathspec) {
> +		if (get_sha1_committish(rev, sha1))
> +			die(_("Failed to resolve '%s' as a valid revision."), rev);
> +		commit = lookup_commit_reference(sha1);
> +		if (!commit)
> +			die(_("Could not parse object '%s'."), rev);
> +		hashcpy(sha1, commit->object.sha1);
> +	} else {
> +		struct tree *tree;
> +		if (get_sha1_treeish(rev, sha1))
> +			die(_("Failed to resolve '%s' as a valid tree."), rev);
> +		tree = parse_tree_indirect(sha1);
> +		if (!tree)
> +			die(_("Could not parse object '%s'."), rev);
> +		hashcpy(sha1, tree->object.sha1);
> +	}
>  
>  	if (patch_mode) {
>  		if (reset_type != NONE)
> diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
> index 81b2570..1fa2a5f 100755
> --- a/t/t7102-reset.sh
> +++ b/t/t7102-reset.sh
> @@ -497,4 +497,12 @@ test_expect_success 'disambiguation (4)' '
>  	test ! -f secondfile
>  '
>  
> +test_expect_success 'reset with paths accepts tree' '
> +	# for simpler tests, drop last commit containing added files
> +	git reset --hard HEAD^ &&
> +	git reset HEAD^^{tree} -- . &&
> +	git diff --cached HEAD^ --exit-code &&
> +	git diff HEAD --exit-code
> +'
> +
>  test_done

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 19/19] reset [--mixed]: use diff-based reset whether or not pathspec was given
  2013-01-09  8:16 ` [PATCH 19/19] reset [--mixed]: use diff-based reset whether or not pathspec was given Martin von Zweigbergk
@ 2013-01-09 20:27   ` Junio C Hamano
  0 siblings, 0 replies; 68+ messages in thread
From: Junio C Hamano @ 2013-01-09 20:27 UTC (permalink / raw)
  To: Martin von Zweigbergk; +Cc: git

Martin von Zweigbergk <martinvonz@gmail.com> writes:

> Thanks to b65982b (Optimize "diff-index --cached" using cache-tree,
> 2009-05-20), resetting with paths is much faster than resetting
> without paths. Some timings for the linux-2.6 repo to illustrate this
> (best of five, warm cache):
>
>         reset       reset .
> real    0m0.219s    0m0.080s
> user    0m0.140s    0m0.040s
> sys     0m0.070s    0m0.030s

Nice.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 04/19] reset: don't allow "git reset -- $pathspec" in bare repo
  2013-01-09 19:32   ` Junio C Hamano
@ 2013-01-10  8:24     ` Martin von Zweigbergk
  2013-01-10 18:04       ` Junio C Hamano
  0 siblings, 1 reply; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-10  8:24 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Wed, Jan 9, 2013 at 11:32 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Martin von Zweigbergk <martinvonz@gmail.com> writes:
>
>> ---
>>  builtin/reset.c | 6 ++++--
>>  1 file changed, 4 insertions(+), 2 deletions(-)
>
> With the patch that does not have any explicit check for bareness
> nor new error message to scold user with, it is rather hard to tell
> what is going on, without any description on what (if anything) is
> broken at the end user level and what remedy is done about that
> breakage...

Will include the following in a re-roll.

    reset: don't allow "git reset -- $pathspec" in bare repo

    Running e.g. "git reset ." in a bare repo results in an index file
    being created from the HEAD commit. The differences compared to the
    index are then printed as usual, but since there is no worktree, it
    will appear as if all files are deleted. For example, in a bare clone
    of git.git:

      Unstaged changes after reset:
      D       .gitattributes
      D       .gitignore
      D       .mailmap
      ...

    This happens because the check for is_bare_repository() happens after
    we branch off into read_from_tree() to reset with paths. Fix by moving
    the branching point after the check.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 06/19] reset.c: remove unnecessary variable 'i'
  2013-01-09 19:39   ` Junio C Hamano
@ 2013-01-10  8:41     ` Martin von Zweigbergk
  0 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-10  8:41 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Wed, Jan 9, 2013 at 11:39 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Martin von Zweigbergk <martinvonz@gmail.com> writes:
>
>> Throughout most of parse_args(), the variable 'i' remains at 0. In the
>> remaining few cases, we can do pointer arithmentic on argv itself
>> instead.
>> ---
>> This is clearly mostly a matter of taste. The remainder of the series
>> does not depend on it in any way.
>
> I agree that it indeed is a matter of taste between
>
>  (1) look at av[i], check with (i < ac) for the end, and increment i to
>      iterate over the arguments; and
>
>  (2) look at av[0], check with (0 < ac) for the end, and increment
>      av and decrement ac at the same time to iterate over the
>      arguments.
>
> When (ac, av) appear as a pair, however, adjusting only av without
> adjusting ac is asking for future trouble.  It violates a common
> expectation that av[ac] points at the NULL at the end of the list.

Good points.

> If a code chooses to use !av[0] as the terminating condition and
> never looks at ac, then incrementing only av is fine, but in such a
> case, the function probably should lose ac altogether.

Makes sense. I've picked this style for now (i.e. dropped both 'i' and
'argc'). I was surprised by the style that referred to the variable in
many places where it was know to be 0, but I'm no experienced C
programmer, so if that's a common practice when it comes to argument
parsing, I'm also happy to drop the patch. Let me know what you
prefer.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 08/19] reset.c: share call to die_if_unmerged_cache()
  2013-01-09 19:48   ` Junio C Hamano
@ 2013-01-10  8:51     ` Martin von Zweigbergk
  0 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-10  8:51 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Wed, Jan 9, 2013 at 11:48 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Martin von Zweigbergk <martinvonz@gmail.com> writes:
>
>> Use a single condition to guard the call to die_if_unmerged_cache for
>> both --soft and --keep. This avoids the small distraction of the
>> precondition check from the logic following it.
>>
>> Also change an instance of
>>
>>   if (e)
>>     err = err || f();
>>
>> to the almost as short, but clearer
>>
>>   if (e && !err)
>>     err = f();
>>
>> (which is equivalent since we only care whether exit code is 0)
>
> It is not just equivalent, but should give us identical result, even
> if we cared the actual value.

If err is initially 0, and f() evaluates to 2, err would be 1 in the
first case, but 2 in the second case, right?

I think the two might be identical in e.g. JavaScript and Python, but
I don't use either much.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 03/19] reset.c: pass pathspec around instead of (prefix, argv) pair
  2013-01-09 19:26   ` Junio C Hamano
@ 2013-01-10 11:05     ` Duy Nguyen
  2013-01-10 23:09       ` Junio C Hamano
  0 siblings, 1 reply; 68+ messages in thread
From: Duy Nguyen @ 2013-01-10 11:05 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Martin von Zweigbergk, git

On Thu, Jan 10, 2013 at 2:26 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Martin von Zweigbergk <martinvonz@gmail.com> writes:
>
>> We use the path arguments in two places in reset.c: in
>> interactive_reset() and read_from_tree(). Both of these call
>> get_pathspec(), so we pass the (prefix, arv) pair to both
>> functions. Move the call to get_pathspec() out of these methods, for
>> two reasons: 1) One argument is simpler than two. 2) It lets us use
>> the (arguably clearer) "if (pathspec)" in place of "if (i < argc)".
>> ---
>> If I understand correctly, this should be rebased on top of
>> nd/parse-pathspec. Please let me know.
>
> Yeah, this will conflict with the get_pathspec-to-parse_pathspec
> conversion Duy has been working on.

Or I could hold off nd/parse-pathspec if this series has a better
chance of graduation first. Decision?
-- 
Duy

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 04/19] reset: don't allow "git reset -- $pathspec" in bare repo
  2013-01-10  8:24     ` Martin von Zweigbergk
@ 2013-01-10 18:04       ` Junio C Hamano
  0 siblings, 0 replies; 68+ messages in thread
From: Junio C Hamano @ 2013-01-10 18:04 UTC (permalink / raw)
  To: Martin von Zweigbergk; +Cc: git

Martin von Zweigbergk <martinvonz@gmail.com> writes:

>     ... Fix by moving
>     the branching point after the check.

OK, that is what I missed.  We have an existing check for mixed
reset, which was originally meant to handle case without any
pathspec but can use the same error condition (i.e. type is mixed
and repository is bare) and error message (i.e. no mixed reset in
a bare repository).  "reset with pathspec" was done before that
check kicked in.

Thanks for clarification (and sorry for the noise).

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 03/19] reset.c: pass pathspec around instead of (prefix, argv) pair
  2013-01-10 11:05     ` Duy Nguyen
@ 2013-01-10 23:09       ` Junio C Hamano
  2013-01-11 11:10         ` Duy Nguyen
  0 siblings, 1 reply; 68+ messages in thread
From: Junio C Hamano @ 2013-01-10 23:09 UTC (permalink / raw)
  To: Duy Nguyen; +Cc: Martin von Zweigbergk, git

Duy Nguyen <pclouds@gmail.com> writes:

> On Thu, Jan 10, 2013 at 2:26 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> Martin von Zweigbergk <martinvonz@gmail.com> writes:
>>
>>> We use the path arguments in two places in reset.c: in
>>> interactive_reset() and read_from_tree(). Both of these call
>>> get_pathspec(), so we pass the (prefix, arv) pair to both
>>> functions. Move the call to get_pathspec() out of these methods, for
>>> two reasons: 1) One argument is simpler than two. 2) It lets us use
>>> the (arguably clearer) "if (pathspec)" in place of "if (i < argc)".
>>> ---
>>> If I understand correctly, this should be rebased on top of
>>> nd/parse-pathspec. Please let me know.
>>
>> Yeah, this will conflict with the get_pathspec-to-parse_pathspec
>> conversion Duy has been working on.
>
> Or I could hold off nd/parse-pathspec if this series has a better
> chance of graduation first. Decision?

I am greedy and want to have both ;-)

Before deciding that, I'd appreciate a second set of eyes giving
Martin's series an independent review, to see if it is going in the
right direction.  I think I didn't spot anything questionable in it
myself, but second opinion always helps.

There is no textual conflict between the two topics at the moment,
but because the ultimate goal of your series is to remove all uses
of the pathspec.raw[] field outside the implementation of pathspec
matching, it might help to rename the field to _private_raw (or
remove it), and either make get_pathspec() private or disappear, to
ensure that the compiler will help us catching semantic conflicts
with new users of it at a late stage of your series.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 09/19] reset.c: replace switch by if-else
  2013-01-09 19:53   ` Junio C Hamano
@ 2013-01-11  6:35     ` Martin von Zweigbergk
  2013-01-11 17:12       ` Junio C Hamano
  0 siblings, 1 reply; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-11  6:35 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Wed, Jan 9, 2013 at 11:53 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Martin von Zweigbergk <martinvonz@gmail.com> writes:
>
>> ---
>>  builtin/reset.c | 13 +++----------
>>  1 file changed, 3 insertions(+), 10 deletions(-)
>>
>> diff --git a/builtin/reset.c b/builtin/reset.c
>> index 42d1563..05ccfd4 100644
>> --- a/builtin/reset.c
>> +++ b/builtin/reset.c
>> @@ -351,18 +351,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>>        * saving the previous head in ORIG_HEAD before. */
>>       update_ref_status = update_refs(rev, sha1);
>>
>> -     switch (reset_type) {
>> -     case HARD:
>> -             if (!update_ref_status && !quiet)
>> -                     print_new_head_line(commit);
>> -             break;
>> -     case SOFT: /* Nothing else to do. */
>> -             break;
>> -     case MIXED: /* Report what has not been updated. */
>> +     if (reset_type == HARD && !update_ref_status && !quiet)
>> +             print_new_head_line(commit);
>> +     else if (reset_type == MIXED) /* Report what has not been updated. */
>>               update_index_refresh(0, NULL,
>>                               quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
>> -             break;
>> -     }
>
> Justification?

Clairvoyance -- the HARD case will soon be the only non-empty case.
It's also missing KEEP and MERGE (but the empty SOFT block is there).

I'll update the message. I will also move the patch a little later in
the series, closer to where it will be useful.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 03/19] reset.c: pass pathspec around instead of (prefix, argv) pair
  2013-01-10 23:09       ` Junio C Hamano
@ 2013-01-11 11:10         ` Duy Nguyen
  0 siblings, 0 replies; 68+ messages in thread
From: Duy Nguyen @ 2013-01-11 11:10 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Martin von Zweigbergk, git

On Fri, Jan 11, 2013 at 6:09 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> Or I could hold off nd/parse-pathspec if this series has a better
>> chance of graduation first. Decision?
>
> I am greedy and want to have both ;-)

Apparently I have no problems with your being greedy.

> There is no textual conflict between the two topics at the moment,
> but because the ultimate goal of your series is to remove all uses
> of the pathspec.raw[] field outside the implementation of pathspec
> matching, it might help to rename the field to _private_raw (or
> remove it), and either make get_pathspec() private or disappear, to
> ensure that the compiler will help us catching semantic conflicts
> with new users of it at a late stage of your series.

There are still some uses for get_pathspec() and new call sites won't
cause big problems because they would need init_pathspec() to convert
get_pathspec() results to struct pathspec. I will rename raw[] though.
-- 
Duy

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH 09/19] reset.c: replace switch by if-else
  2013-01-11  6:35     ` Martin von Zweigbergk
@ 2013-01-11 17:12       ` Junio C Hamano
  0 siblings, 0 replies; 68+ messages in thread
From: Junio C Hamano @ 2013-01-11 17:12 UTC (permalink / raw)
  To: Martin von Zweigbergk; +Cc: git

Martin von Zweigbergk <martinvonz@gmail.com> writes:

>> Justification?
>
> Clairvoyance ...

;-)

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v2 00/19] reset improvements
  2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
                   ` (18 preceding siblings ...)
  2013-01-09  8:16 ` [PATCH 19/19] reset [--mixed]: use diff-based reset whether or not pathspec was given Martin von Zweigbergk
@ 2013-01-15  5:47 ` Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 01/19] reset $pathspec: no need to discard index Martin von Zweigbergk
                     ` (18 more replies)
  19 siblings, 19 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

Changes since v1:

 - Spelling fixes.

 - Explained how "git reset -- $pathspec" in bare repo is broken.

 - Provided motivation for replacement of switch by if-else

 - Fixed argv/argc handling by removing use of argc.

 - Replaced "don't refresh index on --quiet" patch by one that just
   inlines update_index_refresh()

 - Incorporated fixes from Junio's repo

 - Provided some motivation for "replace switch by if-else" amd moved
   the patch later in the series.

Thanks for reviewing!


Martin von Zweigbergk (19):
  reset $pathspec: no need to discard index
  reset $pathspec: exit with code 0 if successful
  reset.c: pass pathspec around instead of (prefix, argv) pair
  reset: don't allow "git reset -- $pathspec" in bare repo
  reset.c: extract function for parsing arguments
  reset.c: remove unnecessary variable 'i'
  reset.c: extract function for updating {ORIG_,}HEAD
  reset.c: share call to die_if_unmerged_cache()
  reset --keep: only write index file once
  reset: avoid redundant error message
  reset.c: replace switch by if-else
  reset.c: move update_index_refresh() call out of read_from_tree()
  reset.c: move lock, write and commit out of update_index_refresh()
  reset [--mixed]: only write index file once
  reset.c: finish entire cmd_reset() whether or not pathspec is given
  reset.c: inline update_index_refresh()
  reset $sha1 $pathspec: require $sha1 only to be treeish
  reset: allow reset on unborn branch
  reset [--mixed]: use diff-based reset whether or not pathspec was
    given

 builtin/reset.c                | 283 +++++++++++++++++++----------------------
 t/t2013-checkout-submodule.sh  |   2 +-
 t/t7102-reset.sh               |  26 +++-
 t/t7106-reset-unborn-branch.sh |  52 ++++++++
 4 files changed, 203 insertions(+), 160 deletions(-)
 create mode 100755 t/t7106-reset-unborn-branch.sh

-- 
1.8.1.1.454.gce43f05

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v2 01/19] reset $pathspec: no need to discard index
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 02/19] reset $pathspec: exit with code 0 if successful Martin von Zweigbergk
                     ` (17 subsequent siblings)
  18 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

Since 34110cd (Make 'unpack_trees()' have a separate source and
destination index, 2008-03-06), the index no longer gets clobbered by
do_diff_cache() and we can remove the code for discarding and
re-reading it.

There are two paths to update_index_refresh() from cmd_reset(), but on
both paths, either read_cache() or read_cache_unmerged() will have
been called, so the call to read_cache() in this method is redundant
(although practically free).

This speeds up "git reset -- ." a little on the linux-2.6 repo (best
of five, warm cache):

        Before      After
real    0m0.093s    0m0.080s
user    0m0.040s    0m0.020s
sys     0m0.050s    0m0.050s

Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com>
---
 builtin/reset.c | 16 +---------------
 1 file changed, 1 insertion(+), 15 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 915cc9f..8cc7c72 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -126,9 +126,6 @@ static int update_index_refresh(int fd, struct lock_file *index_lock, int flags)
 		fd = hold_locked_index(index_lock, 1);
 	}
 
-	if (read_cache() < 0)
-		return error(_("Could not read index"));
-
 	result = refresh_index(&the_index, (flags), NULL, NULL,
 			       _("Unstaged changes after reset:")) ? 1 : 0;
 	if (write_cache(fd, active_cache, active_nr) ||
@@ -141,12 +138,6 @@ static void update_index_from_diff(struct diff_queue_struct *q,
 		struct diff_options *opt, void *data)
 {
 	int i;
-	int *discard_flag = data;
-
-	/* do_diff_cache() mangled the index */
-	discard_cache();
-	*discard_flag = 1;
-	read_cache();
 
 	for (i = 0; i < q->nr; i++) {
 		struct diff_filespec *one = q->queue[i]->one;
@@ -179,17 +170,15 @@ static int read_from_tree(const char *prefix, const char **argv,
 		unsigned char *tree_sha1, int refresh_flags)
 {
 	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
-	int index_fd, index_was_discarded = 0;
+	int index_fd;
 	struct diff_options opt;
 
 	memset(&opt, 0, sizeof(opt));
 	diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt);
 	opt.output_format = DIFF_FORMAT_CALLBACK;
 	opt.format_callback = update_index_from_diff;
-	opt.format_callback_data = &index_was_discarded;
 
 	index_fd = hold_locked_index(lock, 1);
-	index_was_discarded = 0;
 	read_cache();
 	if (do_diff_cache(tree_sha1, &opt))
 		return 1;
@@ -197,9 +186,6 @@ static int read_from_tree(const char *prefix, const char **argv,
 	diff_flush(&opt);
 	diff_tree_release_paths(&opt);
 
-	if (!index_was_discarded)
-		/* The index is still clobbered from do_diff_cache() */
-		discard_cache();
 	return update_index_refresh(index_fd, lock, refresh_flags);
 }
 
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 02/19] reset $pathspec: exit with code 0 if successful
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 01/19] reset $pathspec: no need to discard index Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 03/19] reset.c: pass pathspec around instead of (prefix, argv) pair Martin von Zweigbergk
                     ` (16 subsequent siblings)
  18 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

"git reset $pathspec" currently exits with a non-zero exit code if the
worktree is dirty after resetting, which is inconsistent with reset
without pathspec, and it makes it harder to know whether the command
really failed. Change it to exit with code 0 regardless of whether the
worktree is dirty so that non-zero indicates an error.

This makes the 4 "disambiguation" test cases in t7102 clearer since
they all used to "fail", 3 of which "failed" due to changes in the
work tree. Now only the ambiguous one fails.

Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com>
---
 builtin/reset.c               |  8 +++-----
 t/t2013-checkout-submodule.sh |  2 +-
 t/t7102-reset.sh              | 18 ++++++++++++------
 3 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 8cc7c72..65413d0 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -119,19 +119,17 @@ static void print_new_head_line(struct commit *commit)
 
 static int update_index_refresh(int fd, struct lock_file *index_lock, int flags)
 {
-	int result;
-
 	if (!index_lock) {
 		index_lock = xcalloc(1, sizeof(struct lock_file));
 		fd = hold_locked_index(index_lock, 1);
 	}
 
-	result = refresh_index(&the_index, (flags), NULL, NULL,
-			       _("Unstaged changes after reset:")) ? 1 : 0;
+	refresh_index(&the_index, (flags), NULL, NULL,
+		      _("Unstaged changes after reset:"));
 	if (write_cache(fd, active_cache, active_nr) ||
 			commit_locked_index(index_lock))
 		return error ("Could not refresh index");
-	return result;
+	return 0;
 }
 
 static void update_index_from_diff(struct diff_queue_struct *q,
diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh
index 70edbb3..06b18f8 100755
--- a/t/t2013-checkout-submodule.sh
+++ b/t/t2013-checkout-submodule.sh
@@ -23,7 +23,7 @@ test_expect_success '"reset <submodule>" updates the index' '
 	git update-index --refresh &&
 	git diff-files --quiet &&
 	git diff-index --quiet --cached HEAD &&
-	test_must_fail git reset HEAD^ submodule &&
+	git reset HEAD^ submodule &&
 	test_must_fail git diff-files --quiet &&
 	git reset submodule &&
 	git diff-files --quiet
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
index b096dc8..81b2570 100755
--- a/t/t7102-reset.sh
+++ b/t/t7102-reset.sh
@@ -388,7 +388,8 @@ test_expect_success 'test --mixed <paths>' '
 	echo 4 > file4 &&
 	echo 5 > file1 &&
 	git add file1 file3 file4 &&
-	test_must_fail git reset HEAD -- file1 file2 file3 &&
+	git reset HEAD -- file1 file2 file3 &&
+	test_must_fail git diff --quiet &&
 	git diff > output &&
 	test_cmp output expect &&
 	git diff --cached > output &&
@@ -402,7 +403,8 @@ test_expect_success 'test resetting the index at give paths' '
 	>sub/file2 &&
 	git update-index --add sub/file1 sub/file2 &&
 	T=$(git write-tree) &&
-	test_must_fail git reset HEAD sub/file2 &&
+	git reset HEAD sub/file2 &&
+	test_must_fail git diff --quiet &&
 	U=$(git write-tree) &&
 	echo "$T" &&
 	echo "$U" &&
@@ -440,7 +442,8 @@ test_expect_success 'resetting specific path that is unmerged' '
 		echo "100644 $F3 3	file2"
 	} | git update-index --index-info &&
 	git ls-files -u &&
-	test_must_fail git reset HEAD file2 &&
+	git reset HEAD file2 &&
+	test_must_fail git diff --quiet &&
 	git diff-index --exit-code --cached HEAD
 '
 
@@ -449,7 +452,8 @@ test_expect_success 'disambiguation (1)' '
 	git reset --hard &&
 	>secondfile &&
 	git add secondfile &&
-	test_must_fail git reset secondfile &&
+	git reset secondfile &&
+	test_must_fail git diff --quiet -- secondfile &&
 	test -z "$(git diff --cached --name-only)" &&
 	test -f secondfile &&
 	test ! -s secondfile
@@ -474,7 +478,8 @@ test_expect_success 'disambiguation (3)' '
 	>secondfile &&
 	git add secondfile &&
 	rm -f secondfile &&
-	test_must_fail git reset HEAD secondfile &&
+	git reset HEAD secondfile &&
+	test_must_fail git diff --quiet &&
 	test -z "$(git diff --cached --name-only)" &&
 	test ! -f secondfile
 
@@ -486,7 +491,8 @@ test_expect_success 'disambiguation (4)' '
 	>secondfile &&
 	git add secondfile &&
 	rm -f secondfile &&
-	test_must_fail git reset -- secondfile &&
+	git reset -- secondfile &&
+	test_must_fail git diff --quiet &&
 	test -z "$(git diff --cached --name-only)" &&
 	test ! -f secondfile
 '
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 03/19] reset.c: pass pathspec around instead of (prefix, argv) pair
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 01/19] reset $pathspec: no need to discard index Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 02/19] reset $pathspec: exit with code 0 if successful Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 04/19] reset: don't allow "git reset -- $pathspec" in bare repo Martin von Zweigbergk
                     ` (15 subsequent siblings)
  18 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

We use the path arguments in two places in reset.c: in
interactive_reset() and read_from_tree(). Both of these call
get_pathspec(), so we pass the (prefix, argv) pair to both
functions. Move the call to get_pathspec() out of these methods, for
two reasons: 1) One argument is simpler than two. 2) It lets us use
the (arguably clearer) "if (pathspec)" in place of "if (i < argc)".

Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com>
---
 builtin/reset.c | 27 ++++++++++-----------------
 1 file changed, 10 insertions(+), 17 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 65413d0..045c960 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -153,26 +153,15 @@ static void update_index_from_diff(struct diff_queue_struct *q,
 	}
 }
 
-static int interactive_reset(const char *revision, const char **argv,
-			     const char *prefix)
-{
-	const char **pathspec = NULL;
-
-	if (*argv)
-		pathspec = get_pathspec(prefix, argv);
-
-	return run_add_interactive(revision, "--patch=reset", pathspec);
-}
-
-static int read_from_tree(const char *prefix, const char **argv,
-		unsigned char *tree_sha1, int refresh_flags)
+static int read_from_tree(const char **pathspec, unsigned char *tree_sha1,
+			  int refresh_flags)
 {
 	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 	int index_fd;
 	struct diff_options opt;
 
 	memset(&opt, 0, sizeof(opt));
-	diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt);
+	diff_tree_setup_paths(pathspec, &opt);
 	opt.output_format = DIFF_FORMAT_CALLBACK;
 	opt.format_callback = update_index_from_diff;
 
@@ -216,6 +205,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	const char *rev = "HEAD";
 	unsigned char sha1[20], *orig = NULL, sha1_orig[20],
 				*old_orig = NULL, sha1_old_orig[20];
+	const char **pathspec = NULL;
 	struct commit *commit;
 	struct strbuf msg = STRBUF_INIT;
 	const struct option options[] = {
@@ -287,22 +277,25 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		die(_("Could not parse object '%s'."), rev);
 	hashcpy(sha1, commit->object.sha1);
 
+	if (i < argc)
+		pathspec = get_pathspec(prefix, argv + i);
+
 	if (patch_mode) {
 		if (reset_type != NONE)
 			die(_("--patch is incompatible with --{hard,mixed,soft}"));
-		return interactive_reset(rev, argv + i, prefix);
+		return run_add_interactive(rev, "--patch=reset", pathspec);
 	}
 
 	/* git reset tree [--] paths... can be used to
 	 * load chosen paths from the tree into the index without
 	 * affecting the working tree nor HEAD. */
-	if (i < argc) {
+	if (pathspec) {
 		if (reset_type == MIXED)
 			warning(_("--mixed with paths is deprecated; use 'git reset -- <paths>' instead."));
 		else if (reset_type != NONE)
 			die(_("Cannot do %s reset with paths."),
 					_(reset_type_names[reset_type]));
-		return read_from_tree(prefix, argv + i, sha1,
+		return read_from_tree(pathspec, sha1,
 				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
 	}
 	if (reset_type == NONE)
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 04/19] reset: don't allow "git reset -- $pathspec" in bare repo
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
                     ` (2 preceding siblings ...)
  2013-01-15  5:47   ` [PATCH v2 03/19] reset.c: pass pathspec around instead of (prefix, argv) pair Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 05/19] reset.c: extract function for parsing arguments Martin von Zweigbergk
                     ` (14 subsequent siblings)
  18 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

Running e.g. "git reset ." in a bare repo results in an index file
being created from the HEAD commit. The differences compared to the
index are then printed as usual, but since there is no worktree, it
will appear as if all files are deleted. For example, in a bare clone
of git.git:

  Unstaged changes after reset:
  D       .gitattributes
  D       .gitignore
  D       .mailmap
  ...

This happens because the check for is_bare_repository() happens after
we branch off into read_from_tree() to reset with paths. Fix by moving
the branching point after the check.

Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com>
---
 builtin/reset.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 045c960..664fad9 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -295,8 +295,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		else if (reset_type != NONE)
 			die(_("Cannot do %s reset with paths."),
 					_(reset_type_names[reset_type]));
-		return read_from_tree(pathspec, sha1,
-				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
 	}
 	if (reset_type == NONE)
 		reset_type = MIXED; /* by default */
@@ -308,6 +306,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		die(_("%s reset is not allowed in a bare repository"),
 		    _(reset_type_names[reset_type]));
 
+	if (pathspec)
+		return read_from_tree(pathspec, sha1,
+				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+
 	/* Soft reset does not touch the index file nor the working tree
 	 * at all, but requires them in a good order.  Other resets reset
 	 * the index file to the tree object we are switching to. */
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 05/19] reset.c: extract function for parsing arguments
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
                     ` (3 preceding siblings ...)
  2013-01-15  5:47   ` [PATCH v2 04/19] reset: don't allow "git reset -- $pathspec" in bare repo Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 06/19] reset.c: remove unnecessary variable 'i' Martin von Zweigbergk
                     ` (13 subsequent siblings)
  18 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

Declutter cmd_reset() a bit by moving out the argument parsing to its
own function.

Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com>
---
 builtin/reset.c | 70 +++++++++++++++++++++++++++++++--------------------------
 1 file changed, 38 insertions(+), 32 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 664fad9..58f0f61 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -198,36 +198,11 @@ static void die_if_unmerged_cache(int reset_type)
 
 }
 
-int cmd_reset(int argc, const char **argv, const char *prefix)
+static const char **parse_args(int argc, const char **argv, const char *prefix, const char **rev_ret)
 {
-	int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
-	int patch_mode = 0;
+	int i = 0;
 	const char *rev = "HEAD";
-	unsigned char sha1[20], *orig = NULL, sha1_orig[20],
-				*old_orig = NULL, sha1_old_orig[20];
-	const char **pathspec = NULL;
-	struct commit *commit;
-	struct strbuf msg = STRBUF_INIT;
-	const struct option options[] = {
-		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
-		OPT_SET_INT(0, "mixed", &reset_type,
-						N_("reset HEAD and index"), MIXED),
-		OPT_SET_INT(0, "soft", &reset_type, N_("reset only HEAD"), SOFT),
-		OPT_SET_INT(0, "hard", &reset_type,
-				N_("reset HEAD, index and working tree"), HARD),
-		OPT_SET_INT(0, "merge", &reset_type,
-				N_("reset HEAD, index and working tree"), MERGE),
-		OPT_SET_INT(0, "keep", &reset_type,
-				N_("reset HEAD but keep local changes"), KEEP),
-		OPT_BOOLEAN('p', "patch", &patch_mode, N_("select hunks interactively")),
-		OPT_END()
-	};
-
-	git_config(git_default_config, NULL);
-
-	argc = parse_options(argc, argv, prefix, options, git_reset_usage,
-						PARSE_OPT_KEEP_DASHDASH);
-
+	unsigned char unused[20];
 	/*
 	 * Possible arguments are:
 	 *
@@ -250,7 +225,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		 * Otherwise, argv[i] could be either <rev> or <paths> and
 		 * has to be unambiguous.
 		 */
-		else if (!get_sha1_committish(argv[i], sha1)) {
+		else if (!get_sha1_committish(argv[i], unused)) {
 			/*
 			 * Ok, argv[i] looks like a rev; it should not
 			 * be a filename.
@@ -262,6 +237,40 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 			verify_filename(prefix, argv[i], 1);
 		}
 	}
+	*rev_ret = rev;
+	return i < argc ? get_pathspec(prefix, argv + i) : NULL;
+}
+
+int cmd_reset(int argc, const char **argv, const char *prefix)
+{
+	int reset_type = NONE, update_ref_status = 0, quiet = 0;
+	int patch_mode = 0;
+	const char *rev;
+	unsigned char sha1[20], *orig = NULL, sha1_orig[20],
+				*old_orig = NULL, sha1_old_orig[20];
+	const char **pathspec = NULL;
+	struct commit *commit;
+	struct strbuf msg = STRBUF_INIT;
+	const struct option options[] = {
+		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
+		OPT_SET_INT(0, "mixed", &reset_type,
+						N_("reset HEAD and index"), MIXED),
+		OPT_SET_INT(0, "soft", &reset_type, N_("reset only HEAD"), SOFT),
+		OPT_SET_INT(0, "hard", &reset_type,
+				N_("reset HEAD, index and working tree"), HARD),
+		OPT_SET_INT(0, "merge", &reset_type,
+				N_("reset HEAD, index and working tree"), MERGE),
+		OPT_SET_INT(0, "keep", &reset_type,
+				N_("reset HEAD but keep local changes"), KEEP),
+		OPT_BOOLEAN('p', "patch", &patch_mode, N_("select hunks interactively")),
+		OPT_END()
+	};
+
+	git_config(git_default_config, NULL);
+
+	argc = parse_options(argc, argv, prefix, options, git_reset_usage,
+						PARSE_OPT_KEEP_DASHDASH);
+	pathspec = parse_args(argc, argv, prefix, &rev);
 
 	if (get_sha1_committish(rev, sha1))
 		die(_("Failed to resolve '%s' as a valid ref."), rev);
@@ -277,9 +286,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		die(_("Could not parse object '%s'."), rev);
 	hashcpy(sha1, commit->object.sha1);
 
-	if (i < argc)
-		pathspec = get_pathspec(prefix, argv + i);
-
 	if (patch_mode) {
 		if (reset_type != NONE)
 			die(_("--patch is incompatible with --{hard,mixed,soft}"));
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 06/19] reset.c: remove unnecessary variable 'i'
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
                     ` (4 preceding siblings ...)
  2013-01-15  5:47   ` [PATCH v2 05/19] reset.c: extract function for parsing arguments Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
       [not found]     ` <A5E8E180685CEF45AB9E737A010799805E00DD@cdnz-ex1.corp.cubic.cub>
  2013-01-15  5:47   ` [PATCH v2 07/19] reset.c: extract function for updating {ORIG_,}HEAD Martin von Zweigbergk
                     ` (12 subsequent siblings)
  18 siblings, 1 reply; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

Throughout most of parse_args(), the variable 'i' remains at 0. Many
references are still made to the variable even when it could only have
the value 0. This made at least me, who has relatively little
experience with C programming styles, think that parts of the function
was meant to be part of a loop. To avoid such confusion, remove the
variable and also the 'argc' parameter and check for NULL trailing
argv instead.

Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com>
---
I explained a bit more why I was confused by the current style, but
I'm also perfectly happy if you just drop the patch (there would be
some minor conflicts in a later patch, though).

 builtin/reset.c | 33 ++++++++++++++++-----------------
 1 file changed, 16 insertions(+), 17 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 58f0f61..d89cf4d 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -198,9 +198,8 @@ static void die_if_unmerged_cache(int reset_type)
 
 }
 
-static const char **parse_args(int argc, const char **argv, const char *prefix, const char **rev_ret)
+static const char **parse_args(const char **argv, const char *prefix, const char **rev_ret)
 {
-	int i = 0;
 	const char *rev = "HEAD";
 	unsigned char unused[20];
 	/*
@@ -211,34 +210,34 @@ static const char **parse_args(int argc, const char **argv, const char *prefix,
 	 * git reset [-opts] -- <paths>...
 	 * git reset [-opts] <paths>...
 	 *
-	 * At this point, argv[i] points immediately after [-opts].
+	 * At this point, argv 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;
+	if (argv[0]) {
+		if (!strcmp(argv[0], "--")) {
+			argv++; /* reset to HEAD, possibly with paths */
+		} else if (argv[1] && !strcmp(argv[1], "--")) {
+			rev = argv[0];
+			argv += 2;
 		}
 		/*
-		 * Otherwise, argv[i] could be either <rev> or <paths> and
+		 * Otherwise, argv[0] could be either <rev> or <paths> and
 		 * has to be unambiguous.
 		 */
-		else if (!get_sha1_committish(argv[i], unused)) {
+		else if (!get_sha1_committish(argv[0], unused)) {
 			/*
-			 * Ok, argv[i] looks like a rev; it should not
+			 * Ok, argv[0] looks like a rev; it should not
 			 * be a filename.
 			 */
-			verify_non_filename(prefix, argv[i]);
-			rev = argv[i++];
+			verify_non_filename(prefix, argv[0]);
+			rev = *argv++;
 		} else {
 			/* Otherwise we treat this as a filename */
-			verify_filename(prefix, argv[i], 1);
+			verify_filename(prefix, argv[0], 1);
 		}
 	}
 	*rev_ret = rev;
-	return i < argc ? get_pathspec(prefix, argv + i) : NULL;
+	return argv[0] ? get_pathspec(prefix, argv) : NULL;
 }
 
 int cmd_reset(int argc, const char **argv, const char *prefix)
@@ -270,7 +269,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
 	argc = parse_options(argc, argv, prefix, options, git_reset_usage,
 						PARSE_OPT_KEEP_DASHDASH);
-	pathspec = parse_args(argc, argv, prefix, &rev);
+	pathspec = parse_args(argv, prefix, &rev);
 
 	if (get_sha1_committish(rev, sha1))
 		die(_("Failed to resolve '%s' as a valid ref."), rev);
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 07/19] reset.c: extract function for updating {ORIG_,}HEAD
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
                     ` (5 preceding siblings ...)
  2013-01-15  5:47   ` [PATCH v2 06/19] reset.c: remove unnecessary variable 'i' Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 08/19] reset.c: share call to die_if_unmerged_cache() Martin von Zweigbergk
                     ` (11 subsequent siblings)
  18 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

By extracting the code for updating the HEAD and ORIG_HEAD symbolic
references to a separate function, we declutter cmd_reset() a bit and
we make it clear that e.g. the four variables {,sha1_}{,old_}orig are
only used by this code.

Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com>
---
 builtin/reset.c | 39 +++++++++++++++++++++++----------------
 1 file changed, 23 insertions(+), 16 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index d89cf4d..2187d64 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -240,16 +240,35 @@ static const char **parse_args(const char **argv, const char *prefix, const char
 	return argv[0] ? get_pathspec(prefix, argv) : NULL;
 }
 
+static int update_refs(const char *rev, const unsigned char *sha1)
+{
+	int update_ref_status;
+	struct strbuf msg = STRBUF_INIT;
+	unsigned char *orig = NULL, sha1_orig[20],
+		*old_orig = NULL, sha1_old_orig[20];
+
+	if (!get_sha1("ORIG_HEAD", sha1_old_orig))
+		old_orig = sha1_old_orig;
+	if (!get_sha1("HEAD", sha1_orig)) {
+		orig = sha1_orig;
+		set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
+		update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
+	} else if (old_orig)
+		delete_ref("ORIG_HEAD", old_orig, 0);
+	set_reflog_message(&msg, "updating HEAD", rev);
+	update_ref_status = update_ref(msg.buf, "HEAD", sha1, orig, 0, MSG_ON_ERR);
+	strbuf_release(&msg);
+	return update_ref_status;
+}
+
 int cmd_reset(int argc, const char **argv, const char *prefix)
 {
 	int reset_type = NONE, update_ref_status = 0, quiet = 0;
 	int patch_mode = 0;
 	const char *rev;
-	unsigned char sha1[20], *orig = NULL, sha1_orig[20],
-				*old_orig = NULL, sha1_old_orig[20];
+	unsigned char sha1[20];
 	const char **pathspec = NULL;
 	struct commit *commit;
-	struct strbuf msg = STRBUF_INIT;
 	const struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_SET_INT(0, "mixed", &reset_type,
@@ -333,17 +352,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
 	/* Any resets update HEAD to the head being switched to,
 	 * saving the previous head in ORIG_HEAD before. */
-	if (!get_sha1("ORIG_HEAD", sha1_old_orig))
-		old_orig = sha1_old_orig;
-	if (!get_sha1("HEAD", sha1_orig)) {
-		orig = sha1_orig;
-		set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
-		update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
-	}
-	else if (old_orig)
-		delete_ref("ORIG_HEAD", old_orig, 0);
-	set_reflog_message(&msg, "updating HEAD", rev);
-	update_ref_status = update_ref(msg.buf, "HEAD", sha1, orig, 0, MSG_ON_ERR);
+	update_ref_status = update_refs(rev, sha1);
 
 	switch (reset_type) {
 	case HARD:
@@ -360,7 +369,5 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
 	remove_branch_state();
 
-	strbuf_release(&msg);
-
 	return update_ref_status;
 }
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 08/19] reset.c: share call to die_if_unmerged_cache()
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
                     ` (6 preceding siblings ...)
  2013-01-15  5:47   ` [PATCH v2 07/19] reset.c: extract function for updating {ORIG_,}HEAD Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 09/19] reset --keep: only write index file once Martin von Zweigbergk
                     ` (10 subsequent siblings)
  18 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

Use a single condition to guard the call to die_if_unmerged_cache for
both --soft and --keep. This avoids the small distraction of the
precondition check from the logic following it.

Also change an instance of

  if (e)
    err = err || f();

to the almost as short, but clearer

  if (e && !err)
    err = f();

(which is equivalent since we only care whether exit code is 0)

Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com>
---
 builtin/reset.c | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 2187d64..4e34195 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -337,15 +337,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	/* Soft reset does not touch the index file nor the working tree
 	 * at all, but requires them in a good order.  Other resets reset
 	 * the index file to the tree object we are switching to. */
-	if (reset_type == SOFT)
+	if (reset_type == SOFT || reset_type == KEEP)
 		die_if_unmerged_cache(reset_type);
-	else {
-		int err;
-		if (reset_type == KEEP)
-			die_if_unmerged_cache(reset_type);
-		err = reset_index_file(sha1, reset_type, quiet);
-		if (reset_type == KEEP)
-			err = err || reset_index_file(sha1, MIXED, quiet);
+
+	if (reset_type != SOFT) {
+		int err = reset_index_file(sha1, reset_type, quiet);
+		if (reset_type == KEEP && !err)
+			err = reset_index_file(sha1, MIXED, quiet);
 		if (err)
 			die(_("Could not reset index file to revision '%s'."), rev);
 	}
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 09/19] reset --keep: only write index file once
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
                     ` (7 preceding siblings ...)
  2013-01-15  5:47   ` [PATCH v2 08/19] reset.c: share call to die_if_unmerged_cache() Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 10/19] reset: avoid redundant error message Martin von Zweigbergk
                     ` (9 subsequent siblings)
  18 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

"git reset --keep" calls reset_index_file() twice, first doing a
two-way merge to the target revision, updating the index and worktree,
and then resetting the index. After each call, we write the index
file.

In the unlikely event that the second call to reset_index_file()
fails, the index will have been merged to the target revision, but
HEAD will not be updated, leaving the user with a dirty index.

By moving the locking, writing and committing out of
reset_index_file() and into the caller, we can avoid writing the index
twice, thereby making the sure we don't end up in the half-way reset
state. As a bonus, we speed up "git reset --keep" a little on the
linux-2.6 repo (best of five, warm cache):

        Before      After
real    0m0.315s    0m0.296s
user    0m0.290s    0m0.280s
sys     0m0.020s    0m0.010s

Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com>
---
 builtin/reset.c | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 4e34195..7c440ad 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -38,14 +38,12 @@ static inline int is_merge(void)
 	return !access(git_path("MERGE_HEAD"), F_OK);
 }
 
-static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet)
+static int reset_index(const unsigned char *sha1, int reset_type, int quiet)
 {
 	int nr = 1;
-	int newfd;
 	struct tree_desc desc[2];
 	struct tree *tree;
 	struct unpack_trees_options opts;
-	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 
 	memset(&opts, 0, sizeof(opts));
 	opts.head_idx = 1;
@@ -67,8 +65,6 @@ static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet
 		opts.reset = 1;
 	}
 
-	newfd = hold_locked_index(lock, 1);
-
 	read_cache_unmerged();
 
 	if (reset_type == KEEP) {
@@ -91,10 +87,6 @@ static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet
 		prime_cache_tree(&active_cache_tree, tree);
 	}
 
-	if (write_cache(newfd, active_cache, active_nr) ||
-	    commit_locked_index(lock))
-		return error(_("Could not write new index file."));
-
 	return 0;
 }
 
@@ -341,9 +333,16 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		die_if_unmerged_cache(reset_type);
 
 	if (reset_type != SOFT) {
-		int err = reset_index_file(sha1, reset_type, quiet);
+		struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+		int newfd = hold_locked_index(lock, 1);
+		int err = reset_index(sha1, reset_type, quiet);
 		if (reset_type == KEEP && !err)
-			err = reset_index_file(sha1, MIXED, quiet);
+			err = reset_index(sha1, MIXED, quiet);
+		if (!err &&
+		    (write_cache(newfd, active_cache, active_nr) ||
+		     commit_locked_index(lock))) {
+			err = error(_("Could not write new index file."));
+		}
 		if (err)
 			die(_("Could not reset index file to revision '%s'."), rev);
 	}
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 10/19] reset: avoid redundant error message
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
                     ` (8 preceding siblings ...)
  2013-01-15  5:47   ` [PATCH v2 09/19] reset --keep: only write index file once Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 11/19] reset.c: replace switch by if-else Martin von Zweigbergk
                     ` (8 subsequent siblings)
  18 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

If writing or committing the new index file fails, we print "Could not
write new index file." followed by "Could not reset index file to
revision $rev.". The first message seems to imply the second, so print
only the first message.

Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com>
---
 builtin/reset.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 7c440ad..97fa9f7 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -338,13 +338,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		int err = reset_index(sha1, reset_type, quiet);
 		if (reset_type == KEEP && !err)
 			err = reset_index(sha1, MIXED, quiet);
-		if (!err &&
-		    (write_cache(newfd, active_cache, active_nr) ||
-		     commit_locked_index(lock))) {
-			err = error(_("Could not write new index file."));
-		}
 		if (err)
 			die(_("Could not reset index file to revision '%s'."), rev);
+		if (write_cache(newfd, active_cache, active_nr) ||
+		    commit_locked_index(lock))
+			die(_("Could not write new index file."));
 	}
 
 	/* Any resets update HEAD to the head being switched to,
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 11/19] reset.c: replace switch by if-else
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
                     ` (9 preceding siblings ...)
  2013-01-15  5:47   ` [PATCH v2 10/19] reset: avoid redundant error message Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 12/19] reset.c: move update_index_refresh() call out of read_from_tree() Martin von Zweigbergk
                     ` (7 subsequent siblings)
  18 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

The switch statement towards the end of reset.c is missing case arms
for KEEP and MERGE for no obvious reason, and soon the only non-empty
case arm will be the one for HARD. So let's proactively replace it by
if-else, which will let us move one if statement out without leaving
funny-looking left-overs.

Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com>
---
 builtin/reset.c | 13 +++----------
 1 file changed, 3 insertions(+), 10 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 97fa9f7..c3eb2eb 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -349,18 +349,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	 * saving the previous head in ORIG_HEAD before. */
 	update_ref_status = update_refs(rev, sha1);
 
-	switch (reset_type) {
-	case HARD:
-		if (!update_ref_status && !quiet)
-			print_new_head_line(commit);
-		break;
-	case SOFT: /* Nothing else to do. */
-		break;
-	case MIXED: /* Report what has not been updated. */
+	if (reset_type == HARD && !update_ref_status && !quiet)
+		print_new_head_line(commit);
+	else if (reset_type == MIXED) /* Report what has not been updated. */
 		update_index_refresh(0, NULL,
 				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
-		break;
-	}
 
 	remove_branch_state();
 
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 12/19] reset.c: move update_index_refresh() call out of read_from_tree()
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
                     ` (10 preceding siblings ...)
  2013-01-15  5:47   ` [PATCH v2 11/19] reset.c: replace switch by if-else Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 13/19] reset.c: move lock, write and commit out of update_index_refresh() Martin von Zweigbergk
                     ` (6 subsequent siblings)
  18 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

The final part of cmd_reset() essentially looks like:

  if (pathspec) {
    ...
    read_from_tree(...);
  } else {
    ...
    reset_index(...);
    update_index_refresh(...);
    ...
  }

where read_from_tree() internally also calls
update_index_refresh(). Move the call to update_index_refresh() out of
read_from_tree for symmetry with the 'else' block, making
read_from_tree() and reset_index() closer in functionality.

Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com>
---
 builtin/reset.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index c3eb2eb..70733c2 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -145,11 +145,8 @@ static void update_index_from_diff(struct diff_queue_struct *q,
 	}
 }
 
-static int read_from_tree(const char **pathspec, unsigned char *tree_sha1,
-			  int refresh_flags)
+static int read_from_tree(const char **pathspec, unsigned char *tree_sha1)
 {
-	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
-	int index_fd;
 	struct diff_options opt;
 
 	memset(&opt, 0, sizeof(opt));
@@ -157,7 +154,6 @@ static int read_from_tree(const char **pathspec, unsigned char *tree_sha1,
 	opt.output_format = DIFF_FORMAT_CALLBACK;
 	opt.format_callback = update_index_from_diff;
 
-	index_fd = hold_locked_index(lock, 1);
 	read_cache();
 	if (do_diff_cache(tree_sha1, &opt))
 		return 1;
@@ -165,7 +161,7 @@ static int read_from_tree(const char **pathspec, unsigned char *tree_sha1,
 	diff_flush(&opt);
 	diff_tree_release_paths(&opt);
 
-	return update_index_refresh(index_fd, lock, refresh_flags);
+	return 0;
 }
 
 static void set_reflog_message(struct strbuf *sb, const char *action,
@@ -322,9 +318,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		die(_("%s reset is not allowed in a bare repository"),
 		    _(reset_type_names[reset_type]));
 
-	if (pathspec)
-		return read_from_tree(pathspec, sha1,
-				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+	if (pathspec) {
+		struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+		int index_fd = hold_locked_index(lock, 1);
+		return read_from_tree(pathspec, sha1) ||
+			update_index_refresh(index_fd, lock,
+					quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+	}
 
 	/* Soft reset does not touch the index file nor the working tree
 	 * at all, but requires them in a good order.  Other resets reset
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 13/19] reset.c: move lock, write and commit out of update_index_refresh()
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
                     ` (11 preceding siblings ...)
  2013-01-15  5:47   ` [PATCH v2 12/19] reset.c: move update_index_refresh() call out of read_from_tree() Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 14/19] reset [--mixed]: only write index file once Martin von Zweigbergk
                     ` (5 subsequent siblings)
  18 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

In preparation for the/a following patch, move the locking, writing
and committing of the index file out of update_index_refresh(). The
code duplication caused will soon be taken care of. What remains of
update_index_refresh() is just one line, but it is still called from
two places, so let's leave it for now.

In the process, we expose and fix the minor UI bug that makes us print
"Could not refresh index" when we fail to write the index file when
invoked with a pathspec. Copy the error message from the pathspec-less
codepath ("Could not write new index file.").

Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com>
---
 builtin/reset.c | 34 ++++++++++++++++++----------------
 1 file changed, 18 insertions(+), 16 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 70733c2..c1d6ef2 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -109,19 +109,10 @@ static void print_new_head_line(struct commit *commit)
 		printf("\n");
 }
 
-static int update_index_refresh(int fd, struct lock_file *index_lock, int flags)
+static void update_index_refresh(int flags)
 {
-	if (!index_lock) {
-		index_lock = xcalloc(1, sizeof(struct lock_file));
-		fd = hold_locked_index(index_lock, 1);
-	}
-
 	refresh_index(&the_index, (flags), NULL, NULL,
 		      _("Unstaged changes after reset:"));
-	if (write_cache(fd, active_cache, active_nr) ||
-			commit_locked_index(index_lock))
-		return error ("Could not refresh index");
-	return 0;
 }
 
 static void update_index_from_diff(struct diff_queue_struct *q,
@@ -321,9 +312,14 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	if (pathspec) {
 		struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 		int index_fd = hold_locked_index(lock, 1);
-		return read_from_tree(pathspec, sha1) ||
-			update_index_refresh(index_fd, lock,
-					quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+		if (read_from_tree(pathspec, sha1))
+			return 1;
+		update_index_refresh(
+			quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+		if (write_cache(index_fd, active_cache, active_nr) ||
+		    commit_locked_index(lock))
+			return error("Could not write new index file.");
+		return 0;
 	}
 
 	/* Soft reset does not touch the index file nor the working tree
@@ -351,9 +347,15 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
 	if (reset_type == HARD && !update_ref_status && !quiet)
 		print_new_head_line(commit);
-	else if (reset_type == MIXED) /* Report what has not been updated. */
-		update_index_refresh(0, NULL,
-				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+	else if (reset_type == MIXED) { /* Report what has not been updated. */
+		struct lock_file *index_lock = xcalloc(1, sizeof(struct lock_file));
+		int fd = hold_locked_index(index_lock, 1);
+		update_index_refresh(
+			quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+		if (write_cache(fd, active_cache, active_nr) ||
+		    commit_locked_index(index_lock))
+			error("Could not refresh index");
+	}
 
 	remove_branch_state();
 
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 14/19] reset [--mixed]: only write index file once
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
                     ` (12 preceding siblings ...)
  2013-01-15  5:47   ` [PATCH v2 13/19] reset.c: move lock, write and commit out of update_index_refresh() Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 15/19] reset.c: finish entire cmd_reset() whether or not pathspec is given Martin von Zweigbergk
                     ` (4 subsequent siblings)
  18 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

When doing a mixed reset without paths, the index is locked, read,
reset, and written back as part of the actual reset operation (in
reset_index()). Then, when showing the list of worktree modifications,
we lock the index again, refresh it, and write it.

Change this so we only write the index once, making "git reset" a
little faster. It does mean that the index lock will be held a little
longer, but the difference is small compared to the time spent
refreshing the index.

There is one minor functional difference: We used to say "Could not
write new index file." if the first write failed, and "Could not
refresh index" if the second write failed. Now, we will only use the
first message.

This speeds up "git reset" a little on the linux-2.6 repo (best of
five, warm cache):

        Before      After
real    0m0.239s    0m0.214s
user    0m0.160s    0m0.130s
sys     0m0.070s    0m0.080s

Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com>
---
 builtin/reset.c | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index c1d6ef2..e8a3e41 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -336,6 +336,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 			err = reset_index(sha1, MIXED, quiet);
 		if (err)
 			die(_("Could not reset index file to revision '%s'."), rev);
+
+		if (reset_type == MIXED) /* Report what has not been updated. */
+			update_index_refresh(
+				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+
 		if (write_cache(newfd, active_cache, active_nr) ||
 		    commit_locked_index(lock))
 			die(_("Could not write new index file."));
@@ -347,15 +352,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
 	if (reset_type == HARD && !update_ref_status && !quiet)
 		print_new_head_line(commit);
-	else if (reset_type == MIXED) { /* Report what has not been updated. */
-		struct lock_file *index_lock = xcalloc(1, sizeof(struct lock_file));
-		int fd = hold_locked_index(index_lock, 1);
-		update_index_refresh(
-			quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
-		if (write_cache(fd, active_cache, active_nr) ||
-		    commit_locked_index(index_lock))
-			error("Could not refresh index");
-	}
 
 	remove_branch_state();
 
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 15/19] reset.c: finish entire cmd_reset() whether or not pathspec is given
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
                     ` (13 preceding siblings ...)
  2013-01-15  5:47   ` [PATCH v2 14/19] reset [--mixed]: only write index file once Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 16/19] reset.c: inline update_index_refresh() Martin von Zweigbergk
                     ` (3 subsequent siblings)
  18 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

By not returning from inside the "if (pathspec)" block, we can let the
pathspec-aware and pathspec-less code share a bit more, making it
easier to make future changes that should affect both cases. This also
highlights the similarity between read_from_tree() and reset_index().

Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com>
---
 builtin/reset.c | 42 ++++++++++++++++++------------------------
 1 file changed, 18 insertions(+), 24 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index e8a3e41..c316d9b 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -309,19 +309,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		die(_("%s reset is not allowed in a bare repository"),
 		    _(reset_type_names[reset_type]));
 
-	if (pathspec) {
-		struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
-		int index_fd = hold_locked_index(lock, 1);
-		if (read_from_tree(pathspec, sha1))
-			return 1;
-		update_index_refresh(
-			quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
-		if (write_cache(index_fd, active_cache, active_nr) ||
-		    commit_locked_index(lock))
-			return error("Could not write new index file.");
-		return 0;
-	}
-
 	/* Soft reset does not touch the index file nor the working tree
 	 * at all, but requires them in a good order.  Other resets reset
 	 * the index file to the tree object we are switching to. */
@@ -331,11 +318,16 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	if (reset_type != SOFT) {
 		struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 		int newfd = hold_locked_index(lock, 1);
-		int err = reset_index(sha1, reset_type, quiet);
-		if (reset_type == KEEP && !err)
-			err = reset_index(sha1, MIXED, quiet);
-		if (err)
-			die(_("Could not reset index file to revision '%s'."), rev);
+		if (pathspec) {
+			if (read_from_tree(pathspec, sha1))
+				return 1;
+		} else {
+			int err = reset_index(sha1, reset_type, quiet);
+			if (reset_type == KEEP && !err)
+				err = reset_index(sha1, MIXED, quiet);
+			if (err)
+				die(_("Could not reset index file to revision '%s'."), rev);
+		}
 
 		if (reset_type == MIXED) /* Report what has not been updated. */
 			update_index_refresh(
@@ -346,14 +338,16 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 			die(_("Could not write new index file."));
 	}
 
-	/* Any resets update HEAD to the head being switched to,
-	 * saving the previous head in ORIG_HEAD before. */
-	update_ref_status = update_refs(rev, sha1);
+	if (!pathspec) {
+		/* Any resets without paths update HEAD to the head being
+		 * switched to, saving the previous head in ORIG_HEAD before. */
+		update_ref_status = update_refs(rev, sha1);
 
-	if (reset_type == HARD && !update_ref_status && !quiet)
-		print_new_head_line(commit);
+		if (reset_type == HARD && !update_ref_status && !quiet)
+			print_new_head_line(commit);
 
-	remove_branch_state();
+		remove_branch_state();
+	}
 
 	return update_ref_status;
 }
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 16/19] reset.c: inline update_index_refresh()
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
                     ` (14 preceding siblings ...)
  2013-01-15  5:47   ` [PATCH v2 15/19] reset.c: finish entire cmd_reset() whether or not pathspec is given Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 17/19] reset $sha1 $pathspec: require $sha1 only to be treeish Martin von Zweigbergk
                     ` (2 subsequent siblings)
  18 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

Now that there is only one caller left to the single-line method
update_index_refresh(), inline it.

Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com>
---
 builtin/reset.c | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index c316d9b..520c1a5 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -109,12 +109,6 @@ static void print_new_head_line(struct commit *commit)
 		printf("\n");
 }
 
-static void update_index_refresh(int flags)
-{
-	refresh_index(&the_index, (flags), NULL, NULL,
-		      _("Unstaged changes after reset:"));
-}
-
 static void update_index_from_diff(struct diff_queue_struct *q,
 		struct diff_options *opt, void *data)
 {
@@ -329,9 +323,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 				die(_("Could not reset index file to revision '%s'."), rev);
 		}
 
-		if (reset_type == MIXED) /* Report what has not been updated. */
-			update_index_refresh(
-				quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
+		if (reset_type == MIXED) { /* Report what has not been updated. */
+			int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
+			refresh_index(&the_index, flags, NULL, NULL,
+				      _("Unstaged changes after reset:"));
+		}
 
 		if (write_cache(newfd, active_cache, active_nr) ||
 		    commit_locked_index(lock))
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 17/19] reset $sha1 $pathspec: require $sha1 only to be treeish
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
                     ` (15 preceding siblings ...)
  2013-01-15  5:47   ` [PATCH v2 16/19] reset.c: inline update_index_refresh() Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
  2013-01-16 18:00     ` [PATCH v2 17/19] fixup! " Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 18/19] reset: allow reset on unborn branch Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 19/19] reset [--mixed]: use diff-based reset whether or not pathspec was given Martin von Zweigbergk
  18 siblings, 1 reply; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

Resetting with paths does not update HEAD and there is nothing else
that a commit should be needed for. Relax the argument parsing so only
a tree is required.

The sha1 is only passed to read_from_tree(), which already only
requires a tree.

The "rev" variable we pass to run_add_interactive() will resolve to a
tree. This is fine since interactive_reset only needs the parameter to
be a treeish and doesn't use it for display purposes.

Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com>
---
 builtin/reset.c  | 48 +++++++++++++++++++++++++++---------------------
 t/t7102-reset.sh |  8 ++++++++
 2 files changed, 35 insertions(+), 21 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 520c1a5..b776867 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -178,9 +178,10 @@ static const char **parse_args(const char **argv, const char *prefix, const char
 	/*
 	 * Possible arguments are:
 	 *
-	 * git reset [-opts] <rev> <paths>...
-	 * git reset [-opts] <rev> -- <paths>...
-	 * git reset [-opts] -- <paths>...
+	 * git reset [-opts] [<rev>]
+	 * git reset [-opts] <tree> [<paths>...]
+	 * git reset [-opts] <tree> -- [<paths>...]
+	 * git reset [-opts] -- [<paths>...]
 	 * git reset [-opts] <paths>...
 	 *
 	 * At this point, argv points immediately after [-opts].
@@ -195,11 +196,13 @@ static const char **parse_args(const char **argv, const char *prefix, const char
 		}
 		/*
 		 * Otherwise, argv[0] could be either <rev> or <paths> and
-		 * has to be unambiguous.
+		 * has to be unambiguous. If there is a single argument, it
+		 * can not be a tree
 		 */
-		else if (!get_sha1_committish(argv[0], unused)) {
+		else if ((!argv[1] && !get_sha1_committish(argv[0], unused)) ||
+			 (argv[1] && !get_sha1_treeish(argv[0], unused))) {
 			/*
-			 * Ok, argv[0] looks like a rev; it should not
+			 * Ok, argv[0] looks like a commit/tree; it should not
 			 * be a filename.
 			 */
 			verify_non_filename(prefix, argv[0]);
@@ -241,7 +244,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	const char *rev;
 	unsigned char sha1[20];
 	const char **pathspec = NULL;
-	struct commit *commit;
 	const struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_SET_INT(0, "mixed", &reset_type,
@@ -263,19 +265,23 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 						PARSE_OPT_KEEP_DASHDASH);
 	pathspec = parse_args(argv, prefix, &rev);
 
-	if (get_sha1_committish(rev, sha1))
-		die(_("Failed to resolve '%s' as a valid ref."), rev);
-
-	/*
-	 * NOTE: As "git reset $treeish -- $path" should be usable on
-	 * any tree-ish, this is not strictly correct. We are not
-	 * moving the HEAD to any commit; we are merely resetting the
-	 * entries in the index to that of a treeish.
-	 */
-	commit = lookup_commit_reference(sha1);
-	if (!commit)
-		die(_("Could not parse object '%s'."), rev);
-	hashcpy(sha1, commit->object.sha1);
+	if (!pathspec) {
+		struct commit *commit;
+		if (get_sha1_committish(rev, sha1))
+			die(_("Failed to resolve '%s' as a valid revision."), rev);
+		commit = lookup_commit_reference(sha1);
+		if (!commit)
+			die(_("Could not parse object '%s'."), rev);
+		hashcpy(sha1, commit->object.sha1);
+	} else {
+		struct tree *tree;
+		if (get_sha1_treeish(rev, sha1))
+			die(_("Failed to resolve '%s' as a valid tree."), rev);
+		tree = parse_tree_indirect(sha1);
+		if (!tree)
+			die(_("Could not parse object '%s'."), rev);
+		hashcpy(sha1, tree->object.sha1);
+	}
 
 	if (patch_mode) {
 		if (reset_type != NONE)
@@ -340,7 +346,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		update_ref_status = update_refs(rev, sha1);
 
 		if (reset_type == HARD && !update_ref_status && !quiet)
-			print_new_head_line(commit);
+			print_new_head_line(lookup_commit_reference(sha1));
 
 		remove_branch_state();
 	}
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
index 81b2570..1fa2a5f 100755
--- a/t/t7102-reset.sh
+++ b/t/t7102-reset.sh
@@ -497,4 +497,12 @@ test_expect_success 'disambiguation (4)' '
 	test ! -f secondfile
 '
 
+test_expect_success 'reset with paths accepts tree' '
+	# for simpler tests, drop last commit containing added files
+	git reset --hard HEAD^ &&
+	git reset HEAD^^{tree} -- . &&
+	git diff --cached HEAD^ --exit-code &&
+	git diff HEAD --exit-code
+'
+
 test_done
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 18/19] reset: allow reset on unborn branch
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
                     ` (16 preceding siblings ...)
  2013-01-15  5:47   ` [PATCH v2 17/19] reset $sha1 $pathspec: require $sha1 only to be treeish Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
  2013-01-15  5:47   ` [PATCH v2 19/19] reset [--mixed]: use diff-based reset whether or not pathspec was given Martin von Zweigbergk
  18 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

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>
---
 builtin/reset.c                | 16 ++++++++-----
 t/t7106-reset-unborn-branch.sh | 52 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 62 insertions(+), 6 deletions(-)
 create mode 100755 t/t7106-reset-unborn-branch.sh

diff --git a/builtin/reset.c b/builtin/reset.c
index b776867..45b01eb 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -240,7 +240,7 @@ static int update_refs(const char *rev, const unsigned char *sha1)
 int cmd_reset(int argc, const char **argv, const char *prefix)
 {
 	int reset_type = NONE, update_ref_status = 0, quiet = 0;
-	int patch_mode = 0;
+	int patch_mode = 0, unborn;
 	const char *rev;
 	unsigned char sha1[20];
 	const char **pathspec = NULL;
@@ -265,7 +265,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 						PARSE_OPT_KEEP_DASHDASH);
 	pathspec = parse_args(argv, prefix, &rev);
 
-	if (!pathspec) {
+	unborn = !strcmp(rev, "HEAD") && get_sha1("HEAD", sha1);
+	if (unborn) {
+		/* reset on unborn branch: treat as reset to empty tree */
+		hashcpy(sha1, EMPTY_TREE_SHA1_BIN);
+	} else if (!pathspec) {
 		struct commit *commit;
 		if (get_sha1_committish(rev, sha1))
 			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)
 	if (patch_mode) {
 		if (reset_type != NONE)
 			die(_("--patch is incompatible with --{hard,mixed,soft}"));
-		return run_add_interactive(rev, "--patch=reset", pathspec);
+		return run_add_interactive(sha1_to_hex(sha1), "--patch=reset", pathspec);
 	}
 
 	/* git reset tree [--] paths... can be used to
@@ -340,16 +344,16 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 			die(_("Could not write new index file."));
 	}
 
-	if (!pathspec) {
+	if (!pathspec && !unborn) {
 		/* Any resets without paths update HEAD to the head being
 		 * switched to, saving the previous head in ORIG_HEAD before. */
 		update_ref_status = update_refs(rev, sha1);
 
 		if (reset_type == HARD && !update_ref_status && !quiet)
 			print_new_head_line(lookup_commit_reference(sha1));
-
-		remove_branch_state();
 	}
+	if (!pathspec)
+		remove_branch_state();
 
 	return update_ref_status;
 }
diff --git a/t/t7106-reset-unborn-branch.sh b/t/t7106-reset-unborn-branch.sh
new file mode 100755
index 0000000..8062cf5
--- /dev/null
+++ b/t/t7106-reset-unborn-branch.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+test_description='git reset should work on unborn branch'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+	echo a >a &&
+	echo b >b
+'
+
+test_expect_success 'reset' '
+	git add a b &&
+	git reset &&
+	test "$(git ls-files)" = ""
+'
+
+test_expect_success 'reset HEAD' '
+	rm .git/index &&
+	git add a b &&
+	test_must_fail git reset HEAD
+'
+
+test_expect_success 'reset $file' '
+	rm .git/index &&
+	git add a b &&
+	git reset a &&
+	test "$(git ls-files)" = "b"
+'
+
+test_expect_success 'reset -p' '
+	rm .git/index &&
+	git add a &&
+	echo y | git reset -p &&
+	test "$(git ls-files)" = ""
+'
+
+test_expect_success 'reset --soft is a no-op' '
+	rm .git/index &&
+	git add a &&
+	git reset --soft
+	test "$(git ls-files)" = "a"
+'
+
+test_expect_success 'reset --hard' '
+	rm .git/index &&
+	git add a &&
+	git reset --hard &&
+	test "$(git ls-files)" = "" &&
+	test_path_is_missing a
+'
+
+test_done
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH v2 19/19] reset [--mixed]: use diff-based reset whether or not pathspec was given
  2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
                     ` (17 preceding siblings ...)
  2013-01-15  5:47   ` [PATCH v2 18/19] reset: allow reset on unborn branch Martin von Zweigbergk
@ 2013-01-15  5:47   ` Martin von Zweigbergk
  18 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15  5:47 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

Thanks to b65982b (Optimize "diff-index --cached" using cache-tree,
2009-05-20), resetting with paths is much faster than resetting
without paths. Some timings for the linux-2.6 repo to illustrate this
(best of five, warm cache):

        reset       reset .
real    0m0.219s    0m0.080s
user    0m0.140s    0m0.040s
sys     0m0.070s    0m0.030s

These two commands should do the same thing, so instead of having the
user type the trailing " ." to get the faster do_diff_cache()-based
implementation, always use it when doing a mixed reset, with or
without paths (so "git reset $rev" would also be faster).

Timing "git reset" shows that it indeed becomes as fast as
"git reset ." after this patch.

Signed-off-by: Martin von Zweigbergk <martinvonz@gmail.com>
---
It seems like a better solution would be for unpack_trees() learn the
same tricks as do_diff_cache(). I'm leaving that a challange for the
reader :-). I did have a look a unpack_trees(), but it looked rather
overwhelming.

 builtin/reset.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 45b01eb..921afbe 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -322,7 +322,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 	if (reset_type != SOFT) {
 		struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 		int newfd = hold_locked_index(lock, 1);
-		if (pathspec) {
+		if (reset_type == MIXED) {
 			if (read_from_tree(pathspec, sha1))
 				return 1;
 		} else {
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 06/19] reset.c: remove unnecessary variable 'i'
       [not found]     ` <A5E8E180685CEF45AB9E737A010799805E00DD@cdnz-ex1.corp.cubic.cub>
@ 2013-01-15 18:36       ` Martin von Zweigbergk
  0 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-15 18:36 UTC (permalink / raw)
  To: Holding, Lawrence (NZ)
  Cc: git, Junio C Hamano, Matt Kraai, Ramsay Jones, Duy Nguyen,
	Jeff King

I suppose this was meant for everyone. Adding back the others.

On Tue, Jan 15, 2013 at 10:27 AM, Holding, Lawrence (NZ)
<Lawrence.Holding@cubic.com> wrote:
> Maybe use *argv instead of argv[0]?

Sure. Everywhere? Also in the lines added in patch 17/19 that refer to
both argv[0] and argv[1], such as "argv[1] &&
!get_sha1_treeish(argv[0], unused)"? Or is this just a sign that I'm
making the code _more_ confusing to those who are more familiar with
C?

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH v2 17/19] fixup! reset $sha1 $pathspec: require $sha1 only to be treeish
  2013-01-15  5:47   ` [PATCH v2 17/19] reset $sha1 $pathspec: require $sha1 only to be treeish Martin von Zweigbergk
@ 2013-01-16 18:00     ` Martin von Zweigbergk
  2013-01-16 18:08       ` Martin von Zweigbergk
  0 siblings, 1 reply; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-16 18:00 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

---

Sorry, I forgot the documentation updates. I hope this looks ok. Can
you squash this in, Junio? Thanks.

I don't think any documentation update is necessary for the "reset on
unborn branch" patch. Let me know if you think differently.


 Documentation/git-reset.txt | 18 +++++++++---------
 builtin/reset.c             |  4 ++--
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 978d8da..a404b47 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -8,20 +8,20 @@ git-reset - Reset current HEAD to the specified state
 SYNOPSIS
 --------
 [verse]
-'git reset' [-q] [<commit>] [--] <paths>...
-'git reset' (--patch | -p) [<commit>] [--] [<paths>...]
+'git reset' [-q] [<tree-ish>] [--] <paths>...
+'git reset' (--patch | -p) [<tree-sh>] [--] [<paths>...]
 'git reset' [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]
 
 DESCRIPTION
 -----------
-In the first and second form, copy entries from <commit> to the index.
+In the first and second form, copy entries from <tree-ish> to the index.
 In the third form, set the current branch head (HEAD) to <commit>, optionally
-modifying index and working tree to match.  The <commit> defaults to HEAD
-in all forms.
+modifying index and working tree to match.  The <tree-ish>/<commit> defaults
+to HEAD in all forms.
 
-'git reset' [-q] [<commit>] [--] <paths>...::
+'git reset' [-q] [<tree-ish>] [--] <paths>...::
 	This form resets the index entries for all <paths> to their
-	state at <commit>.  (It does not affect the working tree, nor
+	state at <tree-ish>.  (It does not affect the working tree, nor
 	the current branch.)
 +
 This means that `git reset <paths>` is the opposite of `git add
@@ -34,9 +34,9 @@ Alternatively, using linkgit:git-checkout[1] and specifying a commit, you
 can copy the contents of a path out of a commit to the index and to the
 working tree in one go.
 
-'git reset' (--patch | -p) [<commit>] [--] [<paths>...]::
+'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]::
 	Interactively select hunks in the difference between the index
-	and <commit> (defaults to HEAD).  The chosen hunks are applied
+	and <tree-ish> (defaults to HEAD).  The chosen hunks are applied
 	in reverse to the index.
 +
 This means that `git reset -p` is the opposite of `git add -p`, i.e.
diff --git a/builtin/reset.c b/builtin/reset.c
index b776867..cb84f1b 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -23,8 +23,8 @@
 
 static const char * const git_reset_usage[] = {
 	N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
-	N_("git reset [-q] <commit> [--] <paths>..."),
-	N_("git reset --patch [<commit>] [--] [<paths>...]"),
+	N_("git reset [-q] <tree-ish> [--] <paths>..."),
+	N_("git reset --patch [<tree-ish>] [--] [<paths>...]"),
 	NULL
 };
 
-- 
1.8.1.1.454.gce43f05

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* Re: [PATCH v2 17/19] fixup! reset $sha1 $pathspec: require $sha1 only to be treeish
  2013-01-16 18:00     ` [PATCH v2 17/19] fixup! " Martin von Zweigbergk
@ 2013-01-16 18:08       ` Martin von Zweigbergk
  0 siblings, 0 replies; 68+ messages in thread
From: Martin von Zweigbergk @ 2013-01-16 18:08 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Matt Kraai, Ramsay Jones, Duy Nguyen, Jeff King,
	Martin von Zweigbergk

On Wed, Jan 16, 2013 at 10:00 AM, Martin von Zweigbergk
<martinvonz@gmail.com> wrote:
> ---
>
> Sorry, I forgot the documentation updates. I hope this looks ok. Can
> you squash this in, Junio? Thanks.

I see the series just entered 'next', so I guess it would have to go
on top then. Perhaps with a commit message like as simple as the
following. Let me know if you prefer it to be resent as a proper
patch. Sorry about the noise.

reset: update documentation to require only tree-ish with paths

When resetting with paths, we no longer require a commit argument, but
only a tree-ish. Update the documentation and synopsis accordingly.

^ permalink raw reply	[flat|nested] 68+ messages in thread

end of thread, other threads:[~2013-01-16 18:09 UTC | newest]

Thread overview: 68+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-01-09  8:15 [PATCH 00/19] reset improvements Martin von Zweigbergk
2013-01-09  8:15 ` [PATCH 01/19] reset $pathspec: no need to discard index Martin von Zweigbergk
2013-01-09  8:15 ` [PATCH 02/19] reset $pathspec: exit with code 0 if successful Martin von Zweigbergk
2013-01-09  8:16 ` [PATCH 03/19] reset.c: pass pathspec around instead of (prefix, argv) pair Martin von Zweigbergk
2013-01-09 11:42   ` Matt Kraai
2013-01-09 19:26   ` Junio C Hamano
2013-01-10 11:05     ` Duy Nguyen
2013-01-10 23:09       ` Junio C Hamano
2013-01-11 11:10         ` Duy Nguyen
2013-01-09  8:16 ` [PATCH 04/19] reset: don't allow "git reset -- $pathspec" in bare repo Martin von Zweigbergk
2013-01-09 19:32   ` Junio C Hamano
2013-01-10  8:24     ` Martin von Zweigbergk
2013-01-10 18:04       ` Junio C Hamano
2013-01-09  8:16 ` [PATCH 05/19] reset.c: extract function for parsing arguments Martin von Zweigbergk
2013-01-09  8:16 ` [PATCH 06/19] reset.c: remove unnecessary variable 'i' Martin von Zweigbergk
2013-01-09 19:39   ` Junio C Hamano
2013-01-10  8:41     ` Martin von Zweigbergk
2013-01-09  8:16 ` [PATCH 07/19] reset.c: extract function for updating {ORIG,}HEAD Martin von Zweigbergk
2013-01-09 11:54   ` Matt Kraai
2013-01-09  8:16 ` [PATCH 08/19] reset.c: share call to die_if_unmerged_cache() Martin von Zweigbergk
2013-01-09 19:48   ` Junio C Hamano
2013-01-10  8:51     ` Martin von Zweigbergk
2013-01-09  8:16 ` [PATCH 09/19] reset.c: replace switch by if-else Martin von Zweigbergk
2013-01-09 19:53   ` Junio C Hamano
2013-01-11  6:35     ` Martin von Zweigbergk
2013-01-11 17:12       ` Junio C Hamano
2013-01-09  8:16 ` [PATCH 10/19] reset --keep: only write index file once Martin von Zweigbergk
2013-01-09 19:55   ` Junio C Hamano
2013-01-09  8:16 ` [PATCH 11/19] reset: avoid redundant error message Martin von Zweigbergk
2013-01-09  8:16 ` [PATCH 12/19] reset.c: move update_index_refresh() call out of read_from_tree() Martin von Zweigbergk
2013-01-09  8:16 ` [PATCH 13/19] reset.c: move lock, write and commit out of update_index_refresh() Martin von Zweigbergk
2013-01-09  8:16 ` [PATCH 14/19] reset [--mixed]: don't write index file twice Martin von Zweigbergk
2013-01-09  8:16 ` [PATCH 15/19] reset.c: finish entire cmd_reset() whether or not pathspec is given Martin von Zweigbergk
2013-01-09 19:59   ` Junio C Hamano
2013-01-09  8:16 ` [PATCH 16/19] reset [--mixed] --quiet: don't refresh index Martin von Zweigbergk
2013-01-09 17:01   ` Jeff King
2013-01-09 18:43     ` Martin von Zweigbergk
2013-01-09 19:12       ` Junio C Hamano
2013-01-09 19:38         ` Martin von Zweigbergk
2013-01-09 20:05   ` Junio C Hamano
2013-01-09  8:16 ` [PATCH 17/19] reset $sha1 $pathspec: require $sha1 only to be treeish Martin von Zweigbergk
2013-01-09 20:23   ` Junio C Hamano
2013-01-09  8:16 ` [PATCH 18/19] reset: allow reset on unborn branch Martin von Zweigbergk
2013-01-09  8:16 ` [PATCH 19/19] reset [--mixed]: use diff-based reset whether or not pathspec was given Martin von Zweigbergk
2013-01-09 20:27   ` Junio C Hamano
2013-01-15  5:47 ` [PATCH v2 00/19] reset improvements Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 01/19] reset $pathspec: no need to discard index Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 02/19] reset $pathspec: exit with code 0 if successful Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 03/19] reset.c: pass pathspec around instead of (prefix, argv) pair Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 04/19] reset: don't allow "git reset -- $pathspec" in bare repo Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 05/19] reset.c: extract function for parsing arguments Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 06/19] reset.c: remove unnecessary variable 'i' Martin von Zweigbergk
     [not found]     ` <A5E8E180685CEF45AB9E737A010799805E00DD@cdnz-ex1.corp.cubic.cub>
2013-01-15 18:36       ` Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 07/19] reset.c: extract function for updating {ORIG_,}HEAD Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 08/19] reset.c: share call to die_if_unmerged_cache() Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 09/19] reset --keep: only write index file once Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 10/19] reset: avoid redundant error message Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 11/19] reset.c: replace switch by if-else Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 12/19] reset.c: move update_index_refresh() call out of read_from_tree() Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 13/19] reset.c: move lock, write and commit out of update_index_refresh() Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 14/19] reset [--mixed]: only write index file once Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 15/19] reset.c: finish entire cmd_reset() whether or not pathspec is given Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 16/19] reset.c: inline update_index_refresh() Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 17/19] reset $sha1 $pathspec: require $sha1 only to be treeish Martin von Zweigbergk
2013-01-16 18:00     ` [PATCH v2 17/19] fixup! " Martin von Zweigbergk
2013-01-16 18:08       ` Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 18/19] reset: allow reset on unborn branch Martin von Zweigbergk
2013-01-15  5:47   ` [PATCH v2 19/19] reset [--mixed]: use diff-based reset whether or not pathspec was given Martin von Zweigbergk

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).