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

[PATCH 2/2] rebase --exec: make it work with --rebase-merges

From
Johannes Schindelin via GitGitGadget <gitgitgadget@gmail.com>
Date
Aug 3, 2018, 17:42 UTC
Message-ID
<b29c4d979f147f38bc0a4765a953a748eec0cd6d.1533318123.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.13.git.gitgitgadget@gmail.com>
From: Johannes Schindelin <johannes.schindelin@gmx.de>
The idea of `--exec` is to append an `exec` call after each `pick`.

Since the introduction of fixup!/squash! commits, this idea was extended to apply to "pick, possibly followed by a fixup/squash chain", i.e. an exec would not be inserted between a `pick` and any of its corresponding `fixup` or `squash` lines.

The current implementation uses a dirty trick to achieve that: it assumes that there are only pick/fixup/squash commands, and then *inserts* the `exec` lines before any `pick` but the first, and appends a final one.

With the todo lists generated by `git rebase --rebase-merges`, this simple implementation shows its problems: it produces the exact wrong thing when there are `label`, `reset` and `merge` commands.

Let's change the implementation to do exactly what we want: look for `pick` lines, skip any fixup/squash chains, and then insert the `exec` line. Lather, rinse, repeat.

While at it, also add `exec` lines after `merge` commands, because they are similar in spirit to `pick` commands: they add new commits.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 sequencer.c              | 59 ++++++++++++++++++++++++++++++++--------
 t/t3430-rebase-merges.sh |  2 +-
 2 files changed, 49 insertions(+), 12 deletions(-)
diff --git a/sequencer.c b/sequencer.c
index 31038472f..dda5cdbba 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4244,10 +4244,9 @@ int sequencer_add_exec_commands(const char *commands)
 {
 	const char *todo_file = rebase_path_todo();
 	struct todo_list todo_list = TODO_LIST_INIT;
-	struct todo_item *item;
 	struct strbuf *buf = &todo_list.buf;
 	size_t offset = 0, commands_len = strlen(commands);
-	int i, first;
+	int i, insert_final_commands;
 
 	if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
 		return error(_("could not read '%s'."), todo_file);
@@ -4257,19 +4256,57 @@ int sequencer_add_exec_commands(const char *commands)
 		return error(_("unusable todo list: '%s'"), todo_file);
 	}
 
-	first = 1;
-	/* insert <commands> before every pick except the first one */
-	for (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {
-		if (item->command == TODO_PICK && !first) {
-			strbuf_insert(buf, item->offset_in_buf + offset,
-				      commands, commands_len);
-			offset += commands_len;
+	/*
+	 * Insert <commands> after every pick. Here, fixup/squash chains
+	 * are considered part of the pick, so we insert the commands *after*
+	 * those chains if there are any.
+	 */
+	insert_final_commands = 1;
+	for (i = 0; i < todo_list.nr; ) {
+		enum todo_command command = todo_list.items[i].command;
+		int j = 0;
+
+		if (command != TODO_PICK && command != TODO_MERGE) {
+			i++;
+			continue;
+		}
+
+		/* skip fixup/squash chain, if any */
+		for (i++; i < todo_list.nr; i++, j = 0) {
+			command = todo_list.items[i].command;
+
+			if (is_fixup(command))
+				continue;
+
+			if (command != TODO_COMMENT)
+				break;
+
+			/* skip comment if followed by any fixup/squash */
+			for (j = i + 1; j < todo_list.nr; j++)
+				if (todo_list.items[j].command != TODO_COMMENT)
+					break;
+			if (j < todo_list.nr &&
+			    is_fixup(todo_list.items[j].command)) {
+				i = j;
+				continue;
+			}
+			break;
 		}
-		first = 0;
+
+		if (i >= todo_list.nr) {
+			insert_final_commands = 1;
+			break;
+		}
+
+		strbuf_insert(buf, todo_list.items[i].offset_in_buf + offset,
+			      commands, commands_len);
+		offset += commands_len;
+		insert_final_commands = 0;
 	}
 
 	/* append final <commands> */
-	strbuf_add(buf, commands, commands_len);
+	if (insert_final_commands)
+		strbuf_add(buf, commands, commands_len);
 
 	i = write_message(buf->buf, buf->len, todo_file, 0);
 	todo_list_release(&todo_list);
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index 0bf5eaa37..90ae613e2 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -363,7 +363,7 @@ test_expect_success 'octopus merges' '
 	EOF
 '
 
-test_expect_failure 'with --autosquash and --exec' '
+test_expect_success 'with --autosquash and --exec' '
 	git checkout -b with-exec H &&
 	echo Booh >B.t &&
 	test_tick &&
-- 
gitgitgadget
Previous: Johannes Schindelin via GitGitGadgetNext: Junio C Hamano
Message 3 of 21 in “Make git rebase work with --rebase-merges and --exec”
  1. 0/2 Make git rebase work with --rebase-merges and --execJohannes Schindelin via GitGitGadget, Aug 3, 2018
  2. 1/2 t3430: demonstrate what -r, --autosquash & --exec should doJohannes Schindelin via GitGitGadget, Aug 3, 2018
  3. 2/2 rebase --exec: make it work with --rebase-mergesJohannes Schindelin via GitGitGadget, Aug 3, 2018
  4. Junio C HamanoAug 3, 2018
  5. Johannes SchindelinAug 6, 2018
  6. Johannes SchindelinAug 6, 2018
  7. Junio C HamanoAug 6, 2018
  8. Junio C HamanoAug 3, 2018
  9. Johannes SchindelinAug 6, 2018
  10. 0/2 Make git rebase work with --rebase-merges and --execJohannes Schindelin via GitGitGadget, Aug 6, 2018
  11. 1/2 t3430: demonstrate what -r, --autosquash & --exec should doJohannes Schindelin via GitGitGadget, Aug 6, 2018
  12. 2/2 rebase --exec: make it work with --rebase-mergesJohannes Schindelin via GitGitGadget, Aug 6, 2018
  13. Phillip WoodAug 6, 2018
  14. Phillip WoodAug 6, 2018
  15. Johannes SchindelinAug 9, 2018
  16. Phillip WoodAug 9, 2018
  17. Johannes SchindelinAug 9, 2018
  18. Johannes SchindelinAug 6, 2018
  19. 0/2 Make git rebase work with --rebase-merges and --execJohannes Schindelin via GitGitGadget, Aug 9, 2018
  20. 1/2 t3430: demonstrate what -r, --autosquash & --exec should doJohannes Schindelin via GitGitGadget, Aug 9, 2018
  21. 2/2 rebase --exec: make it work with --rebase-mergesJohannes Schindelin via GitGitGadget, Aug 9, 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.