git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2 3/3] built-in rebase: reinstate `checkout -q` behavior where appropriate

From
Johannes Schindelin via GitGitGadget <gitgitgadget@gmail.com>
Date
Nov 12, 2018, 11:44 UTC
Message-ID
<a7360b856f05169bad094c60e92571035e9d96a7.1542023067.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.72.v2.git.gitgitgadget@gmail.com>
From: Johannes Schindelin <johannes.schindelin@gmx.de>

When we converted a `git checkout -q $onto^0` call to use `reset_head()`, we inadvertently incurred a change from a twoway_merge to a oneway_merge, as if we wanted a `git reset --hard` instead.

This has performance ramifications under certain, though, as the oneway_merge needs to lstat() every single index entry whereas twoway_merge does not.

So let's go back to the old behavior.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 builtin/rebase.c | 40 +++++++++++++++++++++++++---------------
 1 file changed, 25 insertions(+), 15 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 074594cf10..dc78c1497d 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -523,14 +523,16 @@ finished_rebase:
 #define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
 
 #define RESET_HEAD_DETACH (1<<0)
+#define RESET_HEAD_HARD (1<<1)
 
 static int reset_head(struct object_id *oid, const char *action,
 		      const char *switch_to_branch, unsigned flags,
 		      const char *reflog_orig_head, const char *reflog_head)
 {
 	unsigned detach_head = flags & RESET_HEAD_DETACH;
+	unsigned reset_hard = flags & RESET_HEAD_HARD;
 	struct object_id head_oid;
-	struct tree_desc desc;
+	struct tree_desc desc[2] = { { NULL }, { NULL } };
 	struct lock_file lock = LOCK_INIT;
 	struct unpack_trees_options unpack_tree_opts;
 	struct tree *tree;
@@ -539,7 +541,7 @@ static int reset_head(struct object_id *oid, const char *action,
 	size_t prefix_len;
 	struct object_id *orig = NULL, oid_orig,
 		*old_orig = NULL, oid_old_orig;
-	int ret = 0;
+	int ret = 0, nr = 0;
 
 	if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
 		BUG("Not a fully qualified branch: '%s'", switch_to_branch);
@@ -549,20 +551,20 @@ static int reset_head(struct object_id *oid, const char *action,
 		goto leave_reset_head;
 	}
 
-	if (!oid) {
-		if (get_oid("HEAD", &head_oid)) {
-			ret = error(_("could not determine HEAD revision"));
-			goto leave_reset_head;
-		}
-		oid = &head_oid;
+	if ((!oid || !reset_hard) && get_oid("HEAD", &head_oid)) {
+		ret = error(_("could not determine HEAD revision"));
+		goto leave_reset_head;
 	}
 
+	if (!oid)
+		oid = &head_oid;
+
 	memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
 	setup_unpack_trees_porcelain(&unpack_tree_opts, action);
 	unpack_tree_opts.head_idx = 1;
 	unpack_tree_opts.src_index = the_repository->index;
 	unpack_tree_opts.dst_index = the_repository->index;
-	unpack_tree_opts.fn = oneway_merge;
+	unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;
 	unpack_tree_opts.update = 1;
 	unpack_tree_opts.merge = 1;
 	if (!detach_head)
@@ -573,12 +575,17 @@ static int reset_head(struct object_id *oid, const char *action,
 		goto leave_reset_head;
 	}
 
-	if (!fill_tree_descriptor(&desc, oid)) {
+	if (!reset_hard && !fill_tree_descriptor(&desc[nr++], &head_oid)) {
+		ret = error(_("failed to find tree of %s"), oid_to_hex(oid));
+		goto leave_reset_head;
+	}
+
+	if (!fill_tree_descriptor(&desc[nr++], oid)) {
 		ret = error(_("failed to find tree of %s"), oid_to_hex(oid));
 		goto leave_reset_head;
 	}
 
-	if (unpack_trees(1, &desc, &unpack_tree_opts)) {
+	if (unpack_trees(nr, desc, &unpack_tree_opts)) {
 		ret = -1;
 		goto leave_reset_head;
 	}
@@ -625,7 +632,8 @@ static int reset_head(struct object_id *oid, const char *action,
 leave_reset_head:
 	strbuf_release(&msg);
 	rollback_lock_file(&lock);
-	free((void *)desc.buffer);
+	while (nr)
+		free((void *)desc[--nr].buffer);
 	return ret;
 }
 
@@ -1003,7 +1011,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		rerere_clear(&merge_rr);
 		string_list_clear(&merge_rr, 1);
 
-		if (reset_head(NULL, "reset", NULL, 0, NULL, NULL) < 0)
+		if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD,
+			       NULL, NULL) < 0)
 			die(_("could not discard worktree changes"));
 		if (read_basic_state(&options))
 			exit(1);
@@ -1019,7 +1028,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		if (read_basic_state(&options))
 			exit(1);
 		if (reset_head(&options.orig_head, "reset",
-			       options.head_name, 0, NULL, NULL) < 0)
+			       options.head_name, RESET_HEAD_HARD,
+			       NULL, NULL) < 0)
 			die(_("could not move back to %s"),
 			    oid_to_hex(&options.orig_head));
 		ret = finish_rebase(&options);
@@ -1383,7 +1393,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			write_file(autostash, "%s", oid_to_hex(&oid));
 			printf(_("Created autostash: %s\n"), buf.buf);
 			if (reset_head(&head->object.oid, "reset --hard",
-				       NULL, 0, NULL, NULL) < 0)
+				       NULL, RESET_HEAD_HARD, NULL, NULL) < 0)
 				die(_("could not reset --hard"));
 			printf(_("HEAD is now at %s"),
 			       find_unique_abbrev(&head->object.oid,
-- 
gitgitgadget
Previous: Johannes Schindelin via GitGitGadgetNext: Johannes Schindelin
Message 17 of 18 in “Fix built-in rebase perf regression”
  1. 0/2 Fix built-in rebase perf regressionJohannes Schindelin via GitGitGadget, Nov 9, 2018
  2. 1/2 rebase: consolidate clean-up code before leaving reset_head()Johannes Schindelin via GitGitGadget, Nov 9, 2018
  3. Jeff KingNov 9, 2018
  4. Johannes SchindelinNov 9, 2018
  5. 2/2 built-in rebase: reinstate `checkout -q` behavior where appropriateJohannes Schindelin via GitGitGadget, Nov 9, 2018
  6. Jeff KingNov 9, 2018
  7. Johannes SchindelinNov 9, 2018
  8. Jeff KingNov 9, 2018
  9. Johannes SchindelinNov 12, 2018
  10. Junio C HamanoNov 12, 2018
  11. Johannes SchindelinNov 12, 2018
  12. Junio C HamanoNov 13, 2018
  13. Junio C HamanoNov 13, 2018
  14. 0/3 Fix built-in rebase perf regressionJohannes Schindelin via GitGitGadget, Nov 12, 2018
  15. 1/3 rebase: consolidate clean-up code before leaving reset_head()Johannes Schindelin via GitGitGadget, Nov 12, 2018
  16. 2/3 rebase: prepare reset_head() for more flagsJohannes Schindelin via GitGitGadget, Nov 12, 2018
  17. 3/3 built-in rebase: reinstate `checkout -q` behavior where appropriateJohannes Schindelin via GitGitGadget, Nov 12, 2018
  18. Johannes SchindelinNov 12, 2018

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.