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

[PATCH v3 6/9] rebase -i: add fixup [-C | -c] command

From
Charvi Mendiratta <charvi077@gmail.com>
Date
Jan 24, 2021, 17:04 UTC
Message-ID
<20210124170405.30583-7-charvi077@gmail.com>
In-Reply-To
<20210124170405.30583-1-charvi077@gmail.com>

Add options to `fixup` command to fixup both the commit contents and message. `fixup -C` command is used to replace the original commit message and `fixup -c`, additionally allows to edit the commit message.

Original-patch-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Charvi Mendiratta <charvi077@gmail.com>
---
 rebase-interactive.c |   4 +-
 sequencer.c          | 213 +++++++++++++++++++++++++++++++++++++++----
 2 files changed, 197 insertions(+), 20 deletions(-)
diff --git a/rebase-interactive.c b/rebase-interactive.c
index 762853bc7e..c3bd02adee 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -44,7 +44,9 @@ void append_todo_help(int command_count,
 "r, reword <commit> = use commit, but edit the commit message\n"
 "e, edit <commit> = use commit, but stop for amending\n"
 "s, squash <commit> = use commit, but meld into previous commit\n"
-"f, fixup <commit> = like \"squash\", but discard this commit's log message\n"
+"f, fixup [-C | -c] <commit> = like \"squash\", but discard this\n"
+"                   commit's log message. Use -C to replace with this\n"
+"                   commit message or -c to edit the commit message\n"
 "x, exec <command> = run command (the rest of the line) using shell\n"
 "b, break = stop here (continue rebase later with 'git rebase --continue')\n"
 "d, drop <commit> = remove commit\n"
diff --git a/sequencer.c b/sequencer.c
index 6d9a10afcf..46e11d20e8 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1718,6 +1718,12 @@ static int is_pick_or_similar(enum todo_command command)
 	}
 }
 
+enum todo_item_flags {
+	TODO_EDIT_MERGE_MSG    = (1 << 0),
+	TODO_REPLACE_FIXUP_MSG = (1 << 1),
+	TODO_EDIT_FIXUP_MSG    = (1 << 2),
+};
+
 static size_t subject_length(const char *body)
 {
 	const char *p = body;
@@ -1734,32 +1740,176 @@ static size_t subject_length(const char *body)
 
 static const char first_commit_msg_str[] = N_("This is the 1st commit message:");
 static const char nth_commit_msg_fmt[] = N_("This is the commit message #%d:");
+static const char skip_first_commit_msg_str[] = N_("The 1st commit message will be skipped:");
 static const char skip_nth_commit_msg_fmt[] = N_("The commit message #%d will be skipped:");
 static const char combined_commit_msg_fmt[] = N_("This is a combination of %d commits.");
 
-static void append_squash_message(struct strbuf *buf, const char *body,
-				  struct replay_opts *opts)
+static int check_fixup_flag(enum todo_command command,
+			    enum todo_item_flags flag)
+{
+	return command == TODO_FIXUP && ((flag & TODO_REPLACE_FIXUP_MSG) ||
+					 (flag & TODO_EDIT_FIXUP_MSG));
+}
+
+/*
+ * Wrapper around strbuf_add_commented_lines() which avoids double
+ * commenting commit subjects.
+ */
+static void add_commented_lines(struct strbuf *buf, const void *str, size_t len)
+{
+	const char *s = str;
+	while (len > 0 && s[0] == comment_line_char) {
+		size_t count;
+		const char *n = memchr(s, '\n', len);
+		if (!n)
+			count = len;
+		else
+			count = n - s + 1;
+		strbuf_add(buf, s, count);
+		s += count;
+		len -= count;
+	}
+	strbuf_add_commented_lines(buf, s, len);
+}
+
+/* Does the current fixup chain contain a squash command? */
+static int seen_squash(struct replay_opts *opts)
+{
+	return starts_with(opts->current_fixups.buf, "squash") ||
+		strstr(opts->current_fixups.buf, "\nsquash");
+}
+
+static void update_comment_bufs(struct strbuf *buf1, struct strbuf *buf2, int n)
 {
-	size_t commented_len = 0;
+	strbuf_setlen(buf1, 2);
+	strbuf_addf(buf1, _(nth_commit_msg_fmt), n);
+	strbuf_addch(buf1, '\n');
+	strbuf_setlen(buf2, 2);
+	strbuf_addf(buf2, _(skip_nth_commit_msg_fmt), n);
+	strbuf_addch(buf2, '\n');
+}
 
-	unlink(rebase_path_fixup_msg());
-	if (starts_with(body, "squash!") || starts_with(body, "fixup!"))
+/*
+ * Comment out any un-commented commit messages, updating the message comments
+ * to say they will be skipped but do not comment out the empty lines that
+ * surround commit messages and their comments.
+ */
+static void update_squash_message_for_fixup(struct strbuf *msg)
+{
+	void (*copy_lines)(struct strbuf *, const void *, size_t) = strbuf_add;
+	struct strbuf buf1 = STRBUF_INIT, buf2 = STRBUF_INIT;
+	const char *s, *start;
+	char *orig_msg;
+	size_t orig_msg_len;
+	int i = 1;
+
+	strbuf_addf(&buf1, "# %s\n", _(first_commit_msg_str));
+	strbuf_addf(&buf2, "# %s\n", _(skip_first_commit_msg_str));
+	s = start = orig_msg = strbuf_detach(msg, &orig_msg_len);
+	while (s) {
+		const char *next;
+		size_t off;
+		if (skip_prefix(s, buf1.buf, &next)) {
+			/*
+			 * Copy the last message, preserving the blank line
+			 * preceding the current line
+			 */
+			off = (s > start + 1 && s[-2] == '\n') ? 1 : 0;
+			copy_lines(msg, start, s - start - off);
+			if (off)
+				strbuf_addch(msg, '\n');
+			/*
+			 * The next message needs to be commented out but the
+			 * message header is already commented out so just copy
+			 * it and the blank line that follows it.
+			 */
+			strbuf_addbuf(msg, &buf2);
+			if (*next == '\n')
+				strbuf_addch(msg, *next++);
+			start = s = next;
+			copy_lines = add_commented_lines;
+			update_comment_bufs(&buf1, &buf2, ++i);
+		} else if (skip_prefix(s, buf2.buf, &next)) {
+			off = (s > start + 1 && s[-2] == '\n') ? 1 : 0;
+			copy_lines(msg, start, s - start - off);
+			start = s - off;
+			s = next;
+			copy_lines = strbuf_add;
+			update_comment_bufs(&buf1, &buf2, ++i);
+		} else {
+			s = strchr(s, '\n');
+			if (s)
+				s++;
+		}
+	}
+	copy_lines(msg, start, orig_msg_len - (start - orig_msg));
+	free(orig_msg);
+	strbuf_release(&buf1);
+	strbuf_release(&buf2);
+}
+
+static int append_squash_message(struct strbuf *buf, const char *body,
+			 enum todo_command command, struct replay_opts *opts,
+			 enum todo_item_flags flag)
+{
+	const char *fixup_msg;
+	size_t commented_len = 0, fixup_off;
+	/*
+	 * amend is non-interactive and not normally used with fixup!
+	 * or squash! commits, so only comment out those subjects when
+	 * squashing commit messages.
+	 */
+	if (starts_with(body, "amend!") ||
+	    ((command == TODO_SQUASH || seen_squash(opts)) &&
+	     (starts_with(body, "squash!") || starts_with(body, "fixup!"))))
 		commented_len = subject_length(body);
+
 	strbuf_addf(buf, "\n%c ", comment_line_char);
 	strbuf_addf(buf, _(nth_commit_msg_fmt),
 		    ++opts->current_fixup_count + 1);
 	strbuf_addstr(buf, "\n\n");
 	strbuf_add_commented_lines(buf, body, commented_len);
+	/* buf->buf may be reallocated so store an offset into the buffer */
+	fixup_off = buf->len;
 	strbuf_addstr(buf, body + commented_len);
+
+	/* fixup -C after squash behaves like squash */
+	if (check_fixup_flag(command, flag) && !seen_squash(opts)) {
+		/*
+		 * We're replacing the commit message so we need to
+		 * append the Signed-off-by: trailer if the user
+		 * requested '--signoff'.
+		 */
+		if (opts->signoff)
+			append_signoff(buf, 0, 0);
+
+		if ((command == TODO_FIXUP) &&
+		    (flag & TODO_REPLACE_FIXUP_MSG) &&
+		    (file_exists(rebase_path_fixup_msg()) ||
+		     !file_exists(rebase_path_squash_msg()))) {
+			fixup_msg = skip_blank_lines(buf->buf + fixup_off);
+			if (write_message(fixup_msg, strlen(fixup_msg),
+					rebase_path_fixup_msg(), 0) < 0)
+				return error(_("cannot write '%s'"),
+					rebase_path_fixup_msg());
+		} else {
+			unlink(rebase_path_fixup_msg());
+		}
+	} else  {
+		unlink(rebase_path_fixup_msg());
+	}
+
+	return 0;
 }
 
 static int update_squash_messages(struct repository *r,
 				  enum todo_command command,
 				  struct commit *commit,
-				  struct replay_opts *opts)
+				  struct replay_opts *opts,
+				  enum todo_item_flags flag)
 {
 	struct strbuf buf = STRBUF_INIT;
-	int res;
+	int res = 0;
 	const char *message, *body;
 	const char *encoding = get_commit_output_encoding();
 
@@ -1779,6 +1929,8 @@ static int update_squash_messages(struct repository *r,
 			    opts->current_fixup_count + 2);
 		strbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len);
 		strbuf_release(&header);
+		if (check_fixup_flag(command, flag) && !seen_squash(opts))
+			update_squash_message_for_fixup(&buf);
 	} else {
 		struct object_id head;
 		struct commit *head_commit;
@@ -1792,18 +1944,22 @@ static int update_squash_messages(struct repository *r,
 			return error(_("could not read HEAD's commit message"));
 
 		find_commit_subject(head_message, &body);
-		if (command == TODO_FIXUP && write_message(body, strlen(body),
+		if (command == TODO_FIXUP && !flag && write_message(body, strlen(body),
 							rebase_path_fixup_msg(), 0) < 0) {
 			unuse_commit_buffer(head_commit, head_message);
 			return error(_("cannot write '%s'"), rebase_path_fixup_msg());
 		}
-
 		strbuf_addf(&buf, "%c ", comment_line_char);
 		strbuf_addf(&buf, _(combined_commit_msg_fmt), 2);
 		strbuf_addf(&buf, "\n%c ", comment_line_char);
-		strbuf_addstr(&buf, _(first_commit_msg_str));
+		strbuf_addstr(&buf, check_fixup_flag(command, flag) ?
+			      _(skip_first_commit_msg_str) :
+			      _(first_commit_msg_str));
 		strbuf_addstr(&buf, "\n\n");
-		strbuf_addstr(&buf, body);
+		if (check_fixup_flag(command, flag))
+			strbuf_add_commented_lines(&buf, body, strlen(body));
+		else
+			strbuf_addstr(&buf, body);
 
 		unuse_commit_buffer(head_commit, head_message);
 	}
@@ -1813,8 +1969,8 @@ static int update_squash_messages(struct repository *r,
 			     oid_to_hex(&commit->object.oid));
 	find_commit_subject(message, &body);
 
-	if (command == TODO_SQUASH) {
-		append_squash_message(&buf, body, opts);
+	if (command == TODO_SQUASH || check_fixup_flag(command, flag)) {
+		res = append_squash_message(&buf, body, command, opts, flag);
 	} else if (command == TODO_FIXUP) {
 		strbuf_addf(&buf, "\n%c ", comment_line_char);
 		strbuf_addf(&buf, _(skip_nth_commit_msg_fmt),
@@ -1825,7 +1981,9 @@ static int update_squash_messages(struct repository *r,
 		return error(_("unknown command: %d"), command);
 	unuse_commit_buffer(commit, message);
 
-	res = write_message(buf.buf, buf.len, rebase_path_squash_msg(), 0);
+	if (!res)
+		res = write_message(buf.buf, buf.len, rebase_path_squash_msg(),
+				    0);
 	strbuf_release(&buf);
 
 	if (!res) {
@@ -2026,7 +2184,8 @@ static int do_pick_commit(struct repository *r,
 	if (command == TODO_REWORD)
 		reword = 1;
 	else if (is_fixup(command)) {
-		if (update_squash_messages(r, command, commit, opts))
+		if (update_squash_messages(r, command, commit,
+					   opts, item->flags))
 			return -1;
 		flags |= AMEND_MSG;
 		if (!final_fixup)
@@ -2191,10 +2350,6 @@ static int read_and_refresh_cache(struct repository *r,
 	return 0;
 }
 
-enum todo_item_flags {
-	TODO_EDIT_MERGE_MSG = 1
-};
-
 void todo_list_release(struct todo_list *todo_list)
 {
 	strbuf_release(&todo_list->buf);
@@ -2281,6 +2436,18 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
 		return 0;
 	}
 
+	if (item->command == TODO_FIXUP) {
+		if (skip_prefix(bol, "-C", &bol) &&
+		   (*bol == ' ' || *bol == '\t')) {
+			bol += strspn(bol, " \t");
+			item->flags |= TODO_REPLACE_FIXUP_MSG;
+		} else if (skip_prefix(bol, "-c", &bol) &&
+				  (*bol == ' ' || *bol == '\t')) {
+			bol += strspn(bol, " \t");
+			item->flags |= TODO_EDIT_FIXUP_MSG;
+		}
+	}
+
 	if (item->command == TODO_MERGE) {
 		if (skip_prefix(bol, "-C", &bol))
 			bol += strspn(bol, " \t");
@@ -5287,6 +5454,14 @@ static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_lis
 					  short_commit_name(item->commit) :
 					  oid_to_hex(&item->commit->object.oid);
 
+			if (item->command == TODO_FIXUP) {
+				if (item->flags & TODO_EDIT_FIXUP_MSG)
+					strbuf_addstr(buf, " -c");
+				else if (item->flags & TODO_REPLACE_FIXUP_MSG) {
+					strbuf_addstr(buf, " -C");
+				}
+			}
+
 			if (item->command == TODO_MERGE) {
 				if (item->flags & TODO_EDIT_MERGE_MSG)
 					strbuf_addstr(buf, " -c");
-- 
2.29.0.rc1
Previous: Charvi MendirattaNext: Charvi Mendiratta
Message 39 of 110 in “[Outreachy] rebase -i: add options to fixup command”
  1. 0/9 [Outreachy] rebase -i: add options to fixup commandCharvi Mendiratta, Jan 8, 2021
  2. 1/9 rebase -i: only write fixup-message when it's neededCharvi Mendiratta, Jan 8, 2021
  3. Taylor BlauJan 13, 2021
  4. Charvi MendirattaJan 14, 2021
  5. Phillip WoodJan 14, 2021
  6. Charvi MendirattaJan 15, 2021
  7. Junio C HamanoJan 15, 2021
  8. Charvi MendirattaJan 16, 2021
  9. 2/9 sequencer: factor out code to append squash messageCharvi Mendiratta, Jan 8, 2021
  10. 3/9 rebase -i: comment out squash!/fixup! subjects from squash messageCharvi Mendiratta, Jan 8, 2021
  11. Taylor BlauJan 13, 2021
  12. Charvi MendirattaJan 14, 2021
  13. Phillip WoodJan 14, 2021
  14. Charvi MendirattaJan 15, 2021
  15. Christian CouderJan 15, 2021
  16. Charvi MendirattaJan 15, 2021
  17. Charvi MendirattaJan 17, 2021
  18. Phillip WoodJan 18, 2021
  19. Charvi MendirattaJan 19, 2021
  20. 4/9 sequencer: pass todo_item to do_pick_commit()Charvi Mendiratta, Jan 8, 2021
  21. 5/9 sequencer: use const variable for commit message commentsCharvi Mendiratta, Jan 8, 2021
  22. Taylor BlauJan 13, 2021
  23. Junio C HamanoJan 13, 2021
  24. Christian CouderJan 14, 2021
  25. Charvi MendirattaJan 14, 2021
  26. 6/9 rebase -i: add fixup [-C | -c] commandCharvi Mendiratta, Jan 8, 2021
  27. Christian CouderJan 14, 2021
  28. Charvi MendirattaJan 14, 2021
  29. 8/9 rebase -i: teach --autosquash to work with amend!Charvi Mendiratta, Jan 8, 2021
  30. 7/9 t3437: test script for fixup [-C|-c] options in interactive rebaseCharvi Mendiratta, Jan 8, 2021
  31. 9/9 doc/git-rebase: add documentation for fixup [-C|-c] optionsCharvi Mendiratta, Jan 8, 2021
  32. 0/9 [Outreachy] rebase -i: add options to fixup commandCharvi Mendiratta, Jan 19, 2021
  33. 0/9 [Outreachy] rebase -i: add options to fixup commandCharvi Mendiratta, Jan 24, 2021
  34. 1/9 rebase -i: only write fixup-message when it's neededCharvi Mendiratta, Jan 24, 2021
  35. 2/9 sequencer: factor out code to append squash messageCharvi Mendiratta, Jan 24, 2021
  36. 3/9 rebase -i: comment out squash!/fixup! subjects from squash messageCharvi Mendiratta, Jan 24, 2021
  37. 4/9 sequencer: pass todo_item to do_pick_commit()Charvi Mendiratta, Jan 24, 2021
  38. 5/9 sequencer: use const variable for commit message commentsCharvi Mendiratta, Jan 24, 2021
  39. 6/9 rebase -i: add fixup [-C | -c] commandCharvi Mendiratta, Jan 24, 2021
  40. 7/9 t3437: test script for fixup [-C|-c] options in interactive rebaseCharvi Mendiratta, Jan 24, 2021
  41. 8/9 rebase -i: teach --autosquash to work with amend!Charvi Mendiratta, Jan 24, 2021
  42. 9/9 doc/git-rebase: add documentation for fixup [-C|-c] optionsCharvi Mendiratta, Jan 24, 2021
  43. 0/9 [Outreachy] rebase -i: add options to fixup commandCharvi Mendiratta, Jan 29, 2021
  44. 1/9 rebase -i: only write fixup-message when it's neededCharvi Mendiratta, Jan 29, 2021
  45. 2/9 sequencer: factor out code to append squash messageCharvi Mendiratta, Jan 29, 2021
  46. 3/9 rebase -i: comment out squash!/fixup! subjects from squash messageCharvi Mendiratta, Jan 29, 2021
  47. 6/9 rebase -i: add fixup [-C | -c] commandCharvi Mendiratta, Jan 29, 2021
  48. Eric SunshineFeb 2, 2021
  49. Charvi MendirattaFeb 2, 2021
  50. Eric SunshineFeb 3, 2021
  51. Charvi MendirattaFeb 4, 2021
  52. Eric SunshineFeb 4, 2021
  53. 9/9 doc/git-rebase: add documentation for fixup [-C|-c] optionsCharvi Mendiratta, Jan 29, 2021
  54. Eric SunshineFeb 2, 2021
  55. Charvi MendirattaFeb 2, 2021
  56. Marc BranchaudFeb 2, 2021
  57. 7/9 t3437: test script for fixup [-C|-c] options in interactive rebaseCharvi Mendiratta, Jan 29, 2021
  58. Eric SunshineFeb 2, 2021
  59. Christian CouderFeb 2, 2021
  60. Charvi MendirattaFeb 2, 2021
  61. Eric SunshineFeb 3, 2021
  62. Charvi MendirattaFeb 4, 2021
  63. Phillip WoodFeb 4, 2021
  64. Eric SunshineFeb 4, 2021
  65. Charvi MendirattaFeb 4, 2021
  66. 4/9 sequencer: pass todo_item to do_pick_commit()Charvi Mendiratta, Jan 29, 2021
  67. 8/9 rebase -i: teach --autosquash to work with amend!Charvi Mendiratta, Jan 29, 2021
  68. Eric SunshineFeb 2, 2021
  69. Charvi MendirattaFeb 2, 2021
  70. 5/9 sequencer: use const variable for commit message commentsCharvi Mendiratta, Jan 29, 2021
  71. 0/8 [Outreachy] rebase -i: add options to fixup commandCharvi Mendiratta, Feb 4, 2021
  72. 1/8 rebase -i: only write fixup-message when it's neededCharvi Mendiratta, Feb 4, 2021
  73. 3/8 rebase -i: comment out squash!/fixup! subjects from squash messageCharvi Mendiratta, Feb 4, 2021
  74. 7/8 t3437: test script for fixup [-C|-c] options in interactive rebaseCharvi Mendiratta, Feb 4, 2021
  75. 6/8 rebase -i: add fixup [-C | -c] commandCharvi Mendiratta, Feb 4, 2021
  76. 2/8 sequencer: factor out code to append squash messageCharvi Mendiratta, Feb 4, 2021
  77. 5/8 sequencer: use const variable for commit message commentsCharvi Mendiratta, Feb 4, 2021
  78. 8/8 doc/git-rebase: add documentation for fixup [-C|-c] optionsCharvi Mendiratta, Feb 4, 2021
  79. 4/8 sequencer: pass todo_item to do_pick_commit()Charvi Mendiratta, Feb 4, 2021
  80. Eric SunshineFeb 5, 2021
  81. Charvi MendirattaFeb 5, 2021
  82. Christian CouderFeb 5, 2021
  83. Eric SunshineFeb 5, 2021
  84. Charvi MendirattaFeb 6, 2021
  85. Junio C HamanoFeb 5, 2021
  86. Charvi MendirattaFeb 6, 2021
  87. 1/9 rebase -i: only write fixup-message when it's neededCharvi Mendiratta, Jan 19, 2021
  88. 2/9 sequencer: factor out code to append squash messageCharvi Mendiratta, Jan 19, 2021
  89. 5/9 sequencer: use const variable for commit message commentsCharvi Mendiratta, Jan 19, 2021
  90. 3/9 rebase -i: comment out squash!/fixup! subjects from squash messageCharvi Mendiratta, Jan 19, 2021
  91. Junio C HamanoJan 21, 2021
  92. Charvi MendirattaJan 21, 2021
  93. Christian CouderJan 21, 2021
  94. Phillip WoodJan 21, 2021
  95. Junio C HamanoJan 21, 2021
  96. Charvi MendirattaJan 22, 2021
  97. Charvi MendirattaJan 22, 2021
  98. 4/9 sequencer: pass todo_item to do_pick_commit()Charvi Mendiratta, Jan 19, 2021
  99. 7/9 t3437: test script for fixup [-C|-c] options in interactive rebaseCharvi Mendiratta, Jan 19, 2021
  100. 8/9 rebase -i: teach --autosquash to work with amend!Charvi Mendiratta, Jan 19, 2021
  101. 9/9 doc/git-rebase: add documentation for fixup [-C|-c] optionsCharvi Mendiratta, Jan 19, 2021
  102. Marc BranchaudJan 19, 2021
  103. Charvi MendirattaJan 19, 2021
  104. Marc BranchaudJan 19, 2021
  105. Charvi MendirattaJan 20, 2021
  106. Phillip WoodJan 20, 2021
  107. Charvi MendirattaJan 20, 2021
  108. Phillip WoodJan 20, 2021
  109. Charvi MendirattaJan 20, 2021
  110. 6/9 rebase -i: add fixup [-C | -c] commandCharvi Mendiratta, Jan 19, 2021

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.