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

[PATCH 17/17] revert: Introduce --continue to continue the operation

From
Ramkumar Ramachandra <artagnon@gmail.com>
Date
Jul 11, 2011, 14:54 UTC
Message-ID
<1310396048-24925-18-git-send-email-artagnon@gmail.com>
In-Reply-To
<1310396048-24925-1-git-send-email-artagnon@gmail.com>

Using the information in ".git/sequencer", it is now possible to continue a cherry-pick or continue after a conflict. To do this, we have to parse the information in ".git/sequencer/opts" into the replay_opts structure and build a commit_list from the information in ".git/sequencer/todo" before calling into the cherry-picking machinery. Introduce a new subcommand called '--continue' to facilitate this. It is imperative to note that this subcommand will blindly drop the first instruction and attempt to perform the rest of the instructions in ".git/sequencer/todo" to affect the current HEAD provided that the index matches HEAD. The resulting end-user workflow is as follows:

$ git cherry-pick foo..bar ... conflict in commit moo ... $ git cherry-pick --continue error: 'cherry-pick' is not possible because you have unmerged files. ... $ echo "resolved" >conflictingfile $ git commit # this is a replacement for "moo" $ git cherry-pick --continue # drops "moo" and replays everything else

During the "git commit" stage, CHERRY_PICK_HEAD will aid by providing the commit message from the conflicting "moo" commit. Note that the cherry-pick mechanism has no control at this stage, so the user is free to violate anything that was specified during the first cherry-pick invocation. For example, if "-x" was specified during the first cherry-pick invocation, the user is free to edit out the message during commit time. One glitch to note is that the "--signoff" option specified at cherry-pick invocation time is not reflected in the commit message provided by CHERRY_PICK_HEAD; the user must take care to add "--signoff" during the "git commit" invocation.

Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 Documentation/git-cherry-pick.txt |    1 +
 Documentation/git-revert.txt      |    1 +
 Documentation/sequencer.txt       |    5 ++
 builtin/revert.c                  |   60 ++++++++++++++++++++++--
 t/t3510-cherry-pick-sequence.sh   |   93 +++++++++++++++++++++++++++++++++++++
 5 files changed, 156 insertions(+), 4 deletions(-)
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index 138a292..663186b 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -9,6 +9,7 @@ SYNOPSIS
 --------
 'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] <commit>...
 'git cherry-pick' --reset
+'git cherry-pick' --continue
 
 DESCRIPTION
 -----------
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index 783ffb4..9be2fe2 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -9,6 +9,7 @@ SYNOPSIS
 --------
 'git revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>...
 'git revert' --reset
+'git revert' --continue
 
 DESCRIPTION
 -----------
diff --git a/Documentation/sequencer.txt b/Documentation/sequencer.txt
index 16ce88c..3e6df33 100644
--- a/Documentation/sequencer.txt
+++ b/Documentation/sequencer.txt
@@ -2,3 +2,8 @@
 	Forget about the current operation in progress.  Can be used
 	to clear the sequencer state after a failed cherry-pick or
 	revert.
+
+--continue::
+	Continue the operation in progress using the information in
+	'.git/sequencer'.  Can be used to continue after resolving
+	conflicts in a failed cherry-pick or revert.
diff --git a/builtin/revert.c b/builtin/revert.c
index 409d88f..f974ee0 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -41,7 +41,7 @@ static const char * const cherry_pick_usage[] = {
 
 static const char *me;
 enum replay_action { REVERT, CHERRY_PICK };
-enum replay_subcommand { REPLAY_NONE, REPLAY_RESET };
+enum replay_subcommand { REPLAY_NONE, REPLAY_RESET, REPLAY_CONTINUE };
 
 struct replay_opts {
 	enum replay_action action;
@@ -104,13 +104,39 @@ static void verify_opt_compatible(const char *me, const char *base_opt, ...)
 	va_end(ap);
 }
 
+static void verify_opt_mutually_compatible(const char *me, ...)
+{
+       const char *opt1, *opt2;
+       va_list ap;
+       int set;
+
+       va_start(ap, me);
+       while ((opt1 = va_arg(ap, const char *))) {
+	       set = va_arg(ap, int);
+	       if (set)
+		       break;
+       }
+       if (!opt1)
+	       goto ok;
+       while ((opt2 = va_arg(ap, const char *))) {
+	       set = va_arg(ap, int);
+	       if (set)
+		       die(_("%s: %s cannot be used with %s"),
+			       me, opt1, opt2);
+       }
+ok:
+       va_end(ap);
+}
+
 static void parse_args(int argc, const char **argv, struct replay_opts *opts)
 {
 	const char * const * usage_str = revert_or_cherry_pick_usage(opts);
 	int noop;
 	int reset = 0;
+	int contin = 0;
 	struct option options[] = {
 		OPT_BOOLEAN(0, "reset", &reset, "forget the current operation"),
+		OPT_BOOLEAN(0, "continue", &contin, "continue the current operation"),
 		OPT_BOOLEAN('n', "no-commit", &opts->no_commit, "don't automatically commit"),
 		OPT_BOOLEAN('e', "edit", &opts->edit, "edit the commit message"),
 		{ OPTION_BOOLEAN, 'r', NULL, &noop, NULL, "no-op (backward compatibility)",
@@ -140,15 +166,29 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
 					PARSE_OPT_KEEP_ARGV0 |
 					PARSE_OPT_KEEP_UNKNOWN);
 
+	/* Check for incompatible subcommands */
+	verify_opt_mutually_compatible(me,
+				"--reset", reset,
+				"--continue", contin,
+				NULL);
+
 	/* Set the subcommand */
 	if (reset)
 		opts->subcommand = REPLAY_RESET;
+	else if (contin)
+		opts->subcommand = REPLAY_CONTINUE;
 	else
 		opts->subcommand = REPLAY_NONE;
 
 	/* Check for incompatible command line arguments */
-	if (opts->subcommand == REPLAY_RESET) {
-		verify_opt_compatible(me, "--reset",
+	if (opts->subcommand != REPLAY_NONE) {
+		char *this_operation;
+		if (opts->subcommand == REPLAY_RESET)
+			this_operation = "--reset";
+		else
+			this_operation = "--continue";
+
+		verify_opt_compatible(me, this_operation,
 				"--no-commit", opts->no_commit,
 				"--signoff", opts->signoff,
 				"--mainline", opts->mainline,
@@ -1051,6 +1091,15 @@ static int process_subcommand(struct replay_opts *opts)
 	if (opts->subcommand == REPLAY_RESET) {
 		remove_sequencer_state(1);
 		return 0;
+	} else if (opts->subcommand == REPLAY_CONTINUE) {
+		if (!file_exists(git_path(SEQ_TODO_FILE)))
+			goto error;
+		read_populate_opts(&opts);
+		read_populate_todo(&todo_list, opts);
+
+		/* Verify that the conflict has been resolved */
+		if (!index_differs_from("HEAD", 0))
+			todo_list = todo_list->next;
 	} else {
 		/*
 		 * Start a new cherry-pick/ revert sequence; but
@@ -1059,7 +1108,8 @@ static int process_subcommand(struct replay_opts *opts)
 		 */
 		if (file_exists(git_path(SEQ_TODO_FILE))) {
 			error(_("A %s is already in progress"), me);
-			advise(_("Use %s --reset to forget about it"), me);
+			advise(_("Use %s --continue to continue the operation"), me);
+			advise(_("or use %s --reset to forget about it"), me);
 			return -1;
 		}
 
@@ -1071,6 +1121,8 @@ static int process_subcommand(struct replay_opts *opts)
 		save_todo(todo_list, opts);
 	}
 	return pick_commits(todo_list, opts);
+error:
+	return error(_("No %s in progress"), me);
 }
 
 int cmd_revert(int argc, const char **argv, const char *prefix)
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index c21b345..635a45e 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -101,4 +101,97 @@ test_expect_success '--reset cleans up sequencer state' '
 	test_path_is_missing .git/sequencer
 '
 
+test_expect_success '--continue complains when no cherry-pick is in progress' '
+	pristine_detach initial &&
+	test_must_fail git cherry-pick --continue >actual 2>&1 &&
+	test_i18ngrep "error" actual
+'
+
+test_expect_success '--continue complains when there are unresolved conflicts' '
+	pristine_detach initial &&
+	head=$(git rev-parse HEAD) &&
+	test_must_fail git cherry-pick base..picked &&
+	test_must_fail git cherry-pick --continue &&
+	git cherry-pick --reset
+'
+
+test_expect_success '--continue continues after conflicts are resolved' '
+	pristine_detach initial &&
+	head=$(git rev-parse HEAD) &&
+	test_must_fail git cherry-pick base..anotherpick &&
+	echo "c" >foo &&
+	git add foo &&
+	git commit &&
+	git cherry-pick --continue &&
+	test_path_is_missing .git/sequencer &&
+	{
+		git rev-list HEAD |
+		git diff-tree --root --stdin |
+		sed "s/[0-9a-f]\{40\}/OBJID/g"
+	} >actual &&
+	cat >expect <<-\EOF &&
+	OBJID
+	:100644 100644 OBJID OBJID M	foo
+	OBJID
+	:100644 100644 OBJID OBJID M	foo
+	OBJID
+	:100644 100644 OBJID OBJID M	unrelated
+	OBJID
+	:000000 100644 OBJID OBJID A	foo
+	:000000 100644 OBJID OBJID A	unrelated
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success '--continue respects opts' '
+	pristine_detach initial &&
+	head=$(git rev-parse HEAD) &&
+	test_must_fail git cherry-pick -s -x base..anotherpick &&
+	echo "c" >foo &&
+	git add foo &&
+	git commit -s &&
+	git cherry-pick --continue &&
+	test_path_is_missing .git/sequencer &&
+	git cat-file commit HEAD >anotherpick_msg &&
+	git cat-file commit HEAD~1 >picked_msg &&
+	git cat-file commit HEAD~2 >unrelatedpick_msg &&
+	git cat-file commit HEAD~3 >initial_msg &&
+	test_must_fail test_i18ngrep "Signed-off-by:" initial_msg &&
+	test_i18ngrep "Signed-off-by:" unrelatedpick_msg &&
+	test_i18ngrep "Signed-off-by:" picked_msg &&
+	test_i18ngrep "Signed-off-by:" anotherpick_msg &&
+	test_must_fail test_i18ngrep "cherry picked from" initial_msg &&
+	test_i18ngrep "cherry picked from" unrelatedpick_msg &&
+	test_i18ngrep "cherry picked from" picked_msg &&
+	test_i18ngrep "cherry picked from" anotherpick_msg
+'
+
+test_expect_success 'malformed instruction sheet 1' '
+	pristine_detach initial &&
+	head=$(git rev-parse HEAD) &&
+	test_must_fail git cherry-pick base..picked &&
+	echo "resolved" >foo &&
+	git add foo &&
+	git commit &&
+	sed "s/pick /pick/" .git/sequencer/todo >new_sheet
+	cp new_sheet .git/sequencer/todo
+	test_must_fail git cherry-pick --continue >actual 2>&1 &&
+	git cherry-pick --reset &&
+	test_i18ngrep "fatal" actual
+'
+
+test_expect_success 'malformed instruction sheet 2' '
+	pristine_detach initial &&
+	head=$(git rev-parse HEAD) &&
+	test_must_fail git cherry-pick base..picked &&
+	echo "resolved" >foo &&
+	git add foo &&
+	git commit &&
+	sed "s/pick/revert/" .git/sequencer/todo >new_sheet
+	cp new_sheet .git/sequencer/todo
+	test_must_fail git cherry-pick --continue >actual 2>&1 &&
+	git cherry-pick --reset &&
+	test_i18ngrep "fatal" actual
+'
+
 test_done
-- 
1.7.5.GIT
Previous: Jonathan NiederNext: Jonathan Nieder
Message 78 of 99 in “[GSoC update] Sequencer for inclusion”
  1. Ramkumar RamachandraJul 11, 2011
  2. 01/17 advice: Introduce error_resolve_conflictRamkumar Ramachandra, Jul 11, 2011
  3. 02/17 revert: Inline add_message_to_msg functionRamkumar Ramachandra, Jul 11, 2011
  4. Jonathan NiederJul 12, 2011
  5. Ramkumar RamachandraJul 13, 2011
  6. Jonathan NiederJul 13, 2011
  7. Ramkumar RamachandraJul 19, 2011
  8. Junio C HamanoJul 19, 2011
  9. Ramkumar RamachandraJul 19, 2011
  10. Jonathan NiederJul 19, 2011
  11. Ramkumar RamachandraJul 20, 2011
  12. 03/17 revert: Don't check lone argument in get_encodingRamkumar Ramachandra, Jul 11, 2011
  13. Jonathan NiederJul 12, 2011
  14. Ramkumar RamachandraJul 13, 2011
  15. Jonathan NiederJul 13, 2011
  16. 04/17 revert: Rename no_replay to record_originRamkumar Ramachandra, Jul 11, 2011
  17. Jonathan NiederJul 12, 2011
  18. Ramkumar RamachandraJul 13, 2011
  19. Jonathan NiederJul 17, 2011
  20. 05/17 revert: Propogate errors upwards from do_pick_commitRamkumar Ramachandra, Jul 11, 2011
  21. Jonathan NiederJul 12, 2011
  22. Ramkumar RamachandraJul 17, 2011
  23. Jonathan NiederJul 17, 2011
  24. Ramkumar RamachandraJul 19, 2011
  25. 06/17 revert: Eliminate global "commit" variableRamkumar Ramachandra, Jul 11, 2011
  26. Jonathan NiederJul 12, 2011
  27. Ramkumar RamachandraJul 13, 2011
  28. Jonathan NiederJul 13, 2011
  29. Ramkumar RamachandraJul 13, 2011
  30. Jonathan NiederJul 13, 2011
  31. 07/17 revert: Introduce struct to keep command-line optionsRamkumar Ramachandra, Jul 11, 2011
  32. Jonathan NiederJul 12, 2011
  33. Ramkumar RamachandraJul 13, 2011
  34. 08/17 revert: Separate cmdline parsing from functional codeRamkumar Ramachandra, Jul 11, 2011
  35. Jonathan NiederJul 12, 2011
  36. Ramkumar RamachandraJul 18, 2011
  37. Jonathan NiederJul 18, 2011
  38. 09/17 revert: Don't create invalid replay_opts in parse_argsRamkumar Ramachandra, Jul 11, 2011
  39. Junio C HamanoJul 11, 2011
  40. Ramkumar RamachandraJul 12, 2011
  41. Jonathan NiederJul 12, 2011
  42. Ramkumar RamachandraJul 17, 2011
  43. Jonathan NiederJul 17, 2011
  44. 10/17 sequencer: Announce sequencer state locationRamkumar Ramachandra, Jul 11, 2011
  45. Jonathan NiederJul 12, 2011
  46. Sverre RabbelierJul 13, 2011
  47. Ramkumar RamachandraJul 17, 2011
  48. Jonathan NiederJul 17, 2011
  49. Ramkumar RamachandraJul 18, 2011
  50. 11/17 revert: Save data for continuing after conflict resolutionRamkumar Ramachandra, Jul 11, 2011
  51. Junio C HamanoJul 11, 2011
  52. Junio C HamanoJul 11, 2011
  53. Ramkumar RamachandraJul 12, 2011
  54. Jonathan NiederJul 12, 2011
  55. Ramkumar RamachandraJul 17, 2011
  56. Jonathan NiederJul 17, 2011
  57. Ramkumar RamachandraJul 18, 2011
  58. Jonathan NiederJul 19, 2011
  59. Ramkumar RamachandraJul 19, 2011
  60. 12/17 revert: Save command-line options for continuing operationRamkumar Ramachandra, Jul 11, 2011
  61. Junio C HamanoJul 11, 2011
  62. Ramkumar RamachandraJul 12, 2011
  63. Jonathan NiederJul 12, 2011
  64. Ramkumar RamachandraJul 18, 2011
  65. 13/17 revert: Introduce a layer of indirection over pick_commitsRamkumar Ramachandra, Jul 11, 2011
  66. Jonathan NiederJul 12, 2011
  67. Ramkumar RamachandraJul 18, 2011
  68. 14/17 reset: Make hard reset remove the sequencer stateRamkumar Ramachandra, Jul 11, 2011
  69. Jonathan NiederJul 12, 2011
  70. Ramkumar RamachandraJul 17, 2011
  71. 15/17 revert: Remove sequencer state when no commits are pendingRamkumar Ramachandra, Jul 11, 2011
  72. Junio C HamanoJul 11, 2011
  73. Ramkumar RamachandraJul 12, 2011
  74. 16/17 revert: Introduce --reset to remove sequencer stateRamkumar Ramachandra, Jul 11, 2011
  75. Jonathan NiederJul 12, 2011
  76. Ramkumar RamachandraJul 17, 2011
  77. Jonathan NiederJul 17, 2011
  78. 17/17 revert: Introduce --continue to continue the operationRamkumar Ramachandra, Jul 11, 2011
  79. Jonathan NiederJul 12, 2011
  80. Ramkumar RamachandraJul 17, 2011
  81. Jonathan NiederJul 17, 2011
  82. Ramkumar RamachandraJul 18, 2011
  83. Jonathan NiederJul 18, 2011
  84. Jonathan NiederJul 11, 2011
  85. Ramkumar RamachandraJul 11, 2011
  86. Ramkumar RamachandraJul 11, 2011
  87. Jonathan NiederJul 11, 2011
  88. Ramkumar RamachandraJul 12, 2011
  89. Junio C HamanoJul 11, 2011
  90. Jeff KingJul 11, 2011
  91. Ramkumar RamachandraJul 12, 2011
  92. Jeff KingJul 12, 2011
  93. Ramkumar RamachandraJul 13, 2011
  94. Jeff KingJul 13, 2011
  95. config: Introduce functions to write non-standard fileRamkumar Ramachandra, Jul 18, 2011
  96. Junio C HamanoJul 18, 2011
  97. Ramkumar RamachandraJul 19, 2011
  98. Jonathan NiederJul 12, 2011
  99. Miles BaderJul 12, 2011

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.