All of lore.kernel.org
 help / color / mirror / Atom feed
* [GSoC update] Sequencer for inclusion v2
@ 2011-07-19 17:17 Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 01/18] advice: Introduce error_resolve_conflict Ramkumar Ramachandra
                   ` (19 more replies)
  0 siblings, 20 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

Hi,

Mid-term is over, and I'm disappointed that I'm behind schedule -- I
haven't been able to get the Sequencer merged yet.  The reviews have
been incessant as ever, and I spend most of my time chipping
everything to prefection.  On a positive note, the series looks like a
beautiful work of art now; I never imagined that it could be this
beautiful :)

So, I've worked harder on this iteration to fix all the issues with
the last one.  Many of the commit messages are new, I've thrown away
my custom parser in favor of the gitconfig one.  There is however, one
pending issue: how "revert: Remove sequencer state when no commits are
pending" will be compatible with a future "--abort" feature.  I've
done nothing about it; here's an elaborate justification:

In article <7vvcv8bjmf.fsf@alter.siamese.dyndns.org>, Junio C Hamano wrote:
> Ramkumar Ramachandra <artagnon@gmail.com> writes:
>> diff --git a/builtin/revert.c b/builtin/revert.c
>> index f9f5e3a..3936516 100644
>> --- a/builtin/revert.c
>> +++ b/builtin/revert.c
>> @@ -990,8 +990,18 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
>>       for (cur = todo_list; cur; cur = cur->next) {
>>               save_todo(cur, opts);
>>               res = do_pick_commit(cur->item, opts);
>> -             if (res)
>> +             if (res) {
>> +                     if (!cur->next)
>> +                             /*
>> +                              * An error was encountered while
>> +                              * picking the last commit; the
>> +                              * sequencer state is useless now --
>> +                              * the user simply needs to resolve
>> +                              * the conflict and commit
>> +                              */
>> +                             remove_sequencer_state();
>>                       return res;
>> +             }
>>       }
>
> It may be useless for --continue, but wouldn't --abort need some clue on
> what you were doing?

In article <20110706195610.GA23984@elie>, Jonathan Nieder wrote:
> Doh, I'm not thinking straight.  Would this break "git cherry-pick
> --abort", or is there some hack layered on top to avoid that?  Making
> "git commit" remove the .git/sequencer for the last commit of the
> sequence seems a little saner.

Okay, allow me to revisit this discussion with some new arguments -- I
spent some more time thinking about this.

First, on the issue of making "git commit" remove the sequencer state
- why I don't like this. Okay, let's say I "touch
.git/remove-sequencer-state-after-commit" instead of this hunk.  What
does this mean? "git commit must remove the sequencer state before it
exits", right? Well, not exactly -- we can restrict it to say that
"git commit must remove the sequencer state before it exits, provided
that it's committing a conflict resolution (using information in
CHERRY_PICK_HEAD)". Now, the problem:
having.git/remove-sequencer-state-after-commit around is very
dangerous.  It is only cleaned up after the next git commit commits a
conflict resolution and exits cleanly.  Consider what would happen if
it were lying around, even by mistake:

$ git cherry-pick moo..foo
... conflict somewhere in the middle ...
$ echo "foo" >problematicfile
$ git add problematicfile
$ git commit # Resolving the conflict
$ git cherry-pick --continue
fatal: No cherry-pick in progress!

My sequencer state was blown away just because of a stray
.git/remove-sequencer-state-after-commit from a previous operation!
This is horrible.  One can then argue: the file should only ever abort
*that* sequencer operation, and now we run into the problem of
assigning a UID for each sequencer operation.  Unnatural and ugly.
Conclusion: Making "git commit" remove the sequencer state is WRONG.

One alternative I've explored: the sequencer should remove the
sequencer state.  But we have to keep the head around somewhere else
so that "--abort" can work.  What about .git/SEQUENCER_HEAD instead of
.git/sequencer/head? The only inelegance I can see is in the cleanup
-- a cleanup routine has to remove two seemingly unrelated paths.
Then again -- I don't like the idea of unconditional "--abort" either
the way "rebase -i" aborts.  I know it looks odd because I was the one
putting "--abort" in my patches in the first place :p Anyway, what is
the problem with the unconditional "--abort"?  I just returned to my
room after a long vacation, and I want everything to be up to date, so
I do:

$ git reset --hard ram/sequencer
$ git ci --amend
... Some other changes ..
$ git rebase -i master
fatal: An existing rebase is already in progress
# Surprise!
$ git rebase --abort # Immediate reaction
$ git log
# Gah! Where did all my work go?

I propose something like: only "reset --hard" when .git/SEQUENCER_HEAD
is reachable from HEAD.  We can put even more safeguards in place
using a ref name like Junio suggested earlier to protect users from
shooting themselves in the face.  Anyway, this is all a little
long-term, and "--reset" was a really good idea for now: I'm glad we
implemented it.  Users can abort by hard-resetting by hand.

Okay, so far so good.  What about this iteration? I haven't changed
anything.  Making the sequencer remove the sequencer state is not
wrong; we just need to do some additional thing to ensure that a
future "--abort" will work.  I'll let some reviews decide whether that
additional thing should be .git/SEQUENCER_HEAD or something else, and
whether we need to think about it now.

Thanks.

-- Ram

Ramkumar Ramachandra (18):
  advice: Introduce error_resolve_conflict
  config: Introduce functions to write non-standard file
  revert: Simplify and inline add_message_to_msg
  revert: Don't check lone argument in get_encoding
  revert: Rename no_replay to record_origin
  revert: Propogate errors upwards from do_pick_commit
  revert: Eliminate global "commit" variable
  revert: Introduce struct to keep command-line options
  revert: Separate cmdline parsing from functional code
  revert: Don't create invalid replay_opts in parse_args
  revert: Save data for continuing after conflict resolution
  revert: Save command-line options for continuing operation
  revert: Make pick_commits functionally act on a commit list
  revert: Introduce --reset to remove sequencer state
  reset: Make reset remove the sequencer state
  revert: Remove sequencer state when no commits are pending
  revert: Don't implictly stomp pending sequencer operation
  revert: Introduce --continue to continue the operation

 Documentation/git-cherry-pick.txt |    6 +
 Documentation/git-revert.txt      |    6 +
 Documentation/sequencer.txt       |    9 +
 Makefile                          |    2 +
 advice.c                          |   31 ++-
 advice.h                          |    3 +-
 branch.c                          |    2 +
 builtin/revert.c                  |  743 +++++++++++++++++++++++++++++--------
 cache.h                           |    2 +
 config.c                          |   25 ++-
 sequencer.c                       |   19 +
 sequencer.h                       |   20 +
 t/7106-reset-sequence.sh          |   43 +++
 t/t3510-cherry-pick-sequence.sh   |  219 +++++++++++
 14 files changed, 964 insertions(+), 166 deletions(-)
 create mode 100644 Documentation/sequencer.txt
 create mode 100644 sequencer.c
 create mode 100644 sequencer.h
 create mode 100755 t/7106-reset-sequence.sh
 create mode 100755 t/t3510-cherry-pick-sequence.sh

-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 01/18] advice: Introduce error_resolve_conflict
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
@ 2011-07-19 17:17 ` Ramkumar Ramachandra
  2011-07-21 15:35   ` Phil Hord
  2011-07-19 17:17 ` [PATCH 02/18] config: Introduce functions to write non-standard file Ramkumar Ramachandra
                   ` (18 subsequent siblings)
  19 siblings, 1 reply; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

Enable future callers to report a conflict and not die immediately by
introducing a new function called error_resolve_conflict.
Re-implement die_resolve_conflict as a call to error_resolve_conflict
followed by a call to die.  Consequently, the message printed by
die_resolve_conflict changes from

fatal: 'commit' is not possible because you have unmerged files.
       Please, fix them up in the work tree ...
       ...

to

error: 'commit' is not possible because you have unmerged files.
hint: Please, fix them up in the work tree ...
hint: ...
fatal: Exiting because of an unresolved conflict.

Hints are printed using the same advise function introduced in
v1.7.3-rc0~26^2~3 (Introduce advise() to print hints, 2010-08-11).

Inspired-by: Christian Couder <chistian.couder@gmail.com>
Helped-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 advice.c         |   31 ++++++++++++++++++++++++-------
 advice.h         |    3 ++-
 builtin/revert.c |    9 ---------
 3 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/advice.c b/advice.c
index 0be4b5f..a6b3fc2 100644
--- a/advice.c
+++ b/advice.c
@@ -19,6 +19,15 @@ static struct {
 	{ "detachedhead", &advice_detached_head },
 };
 
+void advise(const char *advice, ...)
+{
+	va_list params;
+
+	va_start(params, advice);
+	vreportf("hint: ", advice, params);
+	va_end(params);
+}
+
 int git_default_advice_config(const char *var, const char *value)
 {
 	const char *k = skip_prefix(var, "advice.");
@@ -34,16 +43,24 @@ int git_default_advice_config(const char *var, const char *value)
 	return 0;
 }
 
-void NORETURN die_resolve_conflict(const char *me)
+int error_resolve_conflict(const char *me)
 {
-	if (advice_resolve_conflict)
+	error("'%s' is not possible because you have unmerged files.", me);
+	if (advice_resolve_conflict) {
 		/*
 		 * Message used both when 'git commit' fails and when
 		 * other commands doing a merge do.
 		 */
-		die("'%s' is not possible because you have unmerged files.\n"
-		    "Please, fix them up in the work tree, and then use 'git add/rm <file>' as\n"
-		    "appropriate to mark resolution and make a commit, or use 'git commit -a'.", me);
-	else
-		die("'%s' is not possible because you have unmerged files.", me);
+		advise("Please, fix them up in the work tree,");
+		advise("and then use 'git add/rm <file>' as");
+		advise("appropriate to mark resolution and make a commit,");
+		advise("or use 'git commit -a'.");
+	}
+	return -1;
+}
+
+void NORETURN die_resolve_conflict(const char *me)
+{
+	error_resolve_conflict(me);
+	die("Exiting because of an unresolved conflict.");
 }
diff --git a/advice.h b/advice.h
index 3244ebb..e5d0af7 100644
--- a/advice.h
+++ b/advice.h
@@ -11,7 +11,8 @@ extern int advice_implicit_identity;
 extern int advice_detached_head;
 
 int git_default_advice_config(const char *var, const char *value);
-
+void advise(const char *advice, ...);
+int error_resolve_conflict(const char *me);
 extern void NORETURN die_resolve_conflict(const char *me);
 
 #endif /* ADVICE_H */
diff --git a/builtin/revert.c b/builtin/revert.c
index 1f27c63..2df3f3b 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -214,15 +214,6 @@ static void write_cherry_pick_head(void)
 	strbuf_release(&buf);
 }
 
-static void advise(const char *advice, ...)
-{
-	va_list params;
-
-	va_start(params, advice);
-	vreportf("hint: ", advice, params);
-	va_end(params);
-}
-
 static void print_advice(void)
 {
 	char *msg = getenv("GIT_CHERRY_PICK_HELP");
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 02/18] config: Introduce functions to write non-standard file
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 01/18] advice: Introduce error_resolve_conflict Ramkumar Ramachandra
@ 2011-07-19 17:17 ` Ramkumar Ramachandra
  2011-07-19 19:55   ` Jeff King
  2011-07-19 17:17 ` [PATCH 03/18] revert: Simplify and inline add_message_to_msg Ramkumar Ramachandra
                   ` (17 subsequent siblings)
  19 siblings, 1 reply; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

Introduce two new functions corresponding to "git_config_set" and
"git_config_set_multivar" to write a non-standard configuration file.
Expose thse new functions in cache.h for other git programs to use.

Suggested-by: Jeff King <peff@peff.net>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 cache.h  |    2 ++
 config.c |   25 ++++++++++++++++++++++---
 2 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/cache.h b/cache.h
index bc9e5eb..833ee75 100644
--- a/cache.h
+++ b/cache.h
@@ -1056,9 +1056,11 @@ extern int git_config_bool(const char *, const char *);
 extern int git_config_maybe_bool(const char *, const char *);
 extern int git_config_string(const char **, const char *, const char *);
 extern int git_config_pathname(const char **, const char *, const char *);
+extern int git_config_set_in_file(const char *, const char *, const char *);
 extern int git_config_set(const char *, const char *);
 extern int git_config_parse_key(const char *, char **, int *);
 extern int git_config_set_multivar(const char *, const char *, const char *, int);
+extern int git_config_set_multivar_in_file(const char *, const char *, const char *, int, const char *);
 extern int git_config_rename_section(const char *, const char *);
 extern const char *git_etc_gitconfig(void);
 extern int check_repository_format_version(const char *var, const char *value, void *cb);
diff --git a/config.c b/config.c
index 1fc063b..443324e 100644
--- a/config.c
+++ b/config.c
@@ -1092,9 +1092,15 @@ contline:
 	return offset;
 }
 
+int git_config_set_in_file(const char *key, const char *value,
+			const char *filename)
+{
+	return git_config_set_multivar_in_file(key, value, NULL, 0, filename);
+}
+
 int git_config_set(const char *key, const char *value)
 {
-	return git_config_set_multivar(key, value, NULL, 0);
+	return git_config_set_in_file(key, value, config_exclusive_filename);
 }
 
 /*
@@ -1189,14 +1195,19 @@ out_free_ret_1:
  * - the config file is removed and the lock file rename()d to it.
  *
  */
-int git_config_set_multivar(const char *key, const char *value,
-	const char *value_regex, int multi_replace)
+int git_config_set_multivar_in_file(const char *key, const char *value,
+				const char *value_regex, int multi_replace,
+				const char *filename)
 {
 	int fd = -1, in_fd;
 	int ret;
 	char *config_filename;
+	const char *saved_config_filename;
 	struct lock_file *lock = NULL;
 
+	saved_config_filename = config_exclusive_filename;
+	config_exclusive_filename = filename;
+
 	if (config_exclusive_filename)
 		config_filename = xstrdup(config_exclusive_filename);
 	else
@@ -1379,6 +1390,7 @@ out_free:
 	if (lock)
 		rollback_lock_file(lock);
 	free(config_filename);
+	config_exclusive_filename = saved_config_filename;
 	return ret;
 
 write_err_out:
@@ -1387,6 +1399,13 @@ write_err_out:
 
 }
 
+int git_config_set_multivar(const char *key, const char *value,
+			const char *value_regex, int multi_replace)
+{
+	return git_config_set_multivar_in_file(key, value, value_regex,
+					multi_replace, config_exclusive_filename);
+}
+
 static int section_name_match (const char *buf, const char *name)
 {
 	int i = 0, j = 0, dot = 0;
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 03/18] revert: Simplify and inline add_message_to_msg
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 01/18] advice: Introduce error_resolve_conflict Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 02/18] config: Introduce functions to write non-standard file Ramkumar Ramachandra
@ 2011-07-19 17:17 ` Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 04/18] revert: Don't check lone argument in get_encoding Ramkumar Ramachandra
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

The add_message_to_msg function has some dead code, an unclear API,
only one callsite.  While it originally intended fill up an empty
commit message with the commit object name while picking, it really
doesn't do this -- a bug introduced in 9509af6 (Make git-revert &
git-cherry-pick a builtin, 2007-03-01).  Today, tests in
t3505-cherry-pick-empty.sh indicate that not filling up an empty
commit message is the desired behavior.  Re-implement and inline the
function accordingly.

Helped-by: Junio C Hamano <gitster@pobox.com>
Mentored-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 builtin/revert.c |   28 ++++++++++++++--------------
 1 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/builtin/revert.c b/builtin/revert.c
index 2df3f3b..7dfe295 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -185,19 +185,6 @@ static char *get_encoding(const char *message)
 	return NULL;
 }
 
-static void add_message_to_msg(struct strbuf *msgbuf, const char *message)
-{
-	const char *p = message;
-	while (*p && (*p != '\n' || p[1] != '\n'))
-		p++;
-
-	if (!*p)
-		strbuf_addstr(msgbuf, sha1_to_hex(commit->object.sha1));
-
-	p += 2;
-	strbuf_addstr(msgbuf, p);
-}
-
 static void write_cherry_pick_head(void)
 {
 	int fd;
@@ -462,11 +449,24 @@ static int do_pick_commit(void)
 		}
 		strbuf_addstr(&msgbuf, ".\n");
 	} else {
+		const char *p;
+
 		base = parent;
 		base_label = msg.parent_label;
 		next = commit;
 		next_label = msg.label;
-		add_message_to_msg(&msgbuf, msg.message);
+
+		/*
+		 * Append the commit log message to msgbuf; it starts
+		 * after the tree, parent, author, committer
+		 * information followed by "\n\n".
+		 */
+		p = strstr(msg.message, "\n\n");
+		if (p) {
+			p += 2;
+			strbuf_addstr(&msgbuf, p);
+		}
+
 		if (no_replay) {
 			strbuf_addstr(&msgbuf, "(cherry picked from commit ");
 			strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 04/18] revert: Don't check lone argument in get_encoding
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
                   ` (2 preceding siblings ...)
  2011-07-19 17:17 ` [PATCH 03/18] revert: Simplify and inline add_message_to_msg Ramkumar Ramachandra
@ 2011-07-19 17:17 ` Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 05/18] revert: Rename no_replay to record_origin Ramkumar Ramachandra
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

The get_encoding function has only one callsite, and its caller makes
sure that a NULL argument isn't passed.  Remove a string marked for
translation that will never be shown by not unnecessarily
double-checking the argument.

Suggested-by: Jonathan Nieder <jrnieder@gmail.com>
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 builtin/revert.c |    3 ---
 1 files changed, 0 insertions(+), 3 deletions(-)

diff --git a/builtin/revert.c b/builtin/revert.c
index 7dfe295..30b39c0 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -167,9 +167,6 @@ static char *get_encoding(const char *message)
 {
 	const char *p = message, *eol;
 
-	if (!p)
-		die (_("Could not read commit message of %s"),
-				sha1_to_hex(commit->object.sha1));
 	while (*p && *p != '\n') {
 		for (eol = p + 1; *eol && *eol != '\n'; eol++)
 			; /* do nothing */
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 05/18] revert: Rename no_replay to record_origin
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
                   ` (3 preceding siblings ...)
  2011-07-19 17:17 ` [PATCH 04/18] revert: Don't check lone argument in get_encoding Ramkumar Ramachandra
@ 2011-07-19 17:17 ` Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 06/18] revert: Propogate errors upwards from do_pick_commit Ramkumar Ramachandra
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

The "-x" command-line option is used to record the name of the
original commits being picked in the commit message.  The variable
corresponding to this option is named "no_replay" for historical
reasons; the name is especially confusing because the term "replay" is
used to describe what cherry-pick does (for example, in the
documentation of the "--mainline" option).  So, give the variable
corresponding to the "-x" command-line option a better name:
"record_origin".

Mentored-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 builtin/revert.c |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/builtin/revert.c b/builtin/revert.c
index 30b39c0..794c050 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -35,7 +35,7 @@ static const char * const cherry_pick_usage[] = {
 	NULL
 };
 
-static int edit, no_replay, no_commit, mainline, signoff, allow_ff;
+static int edit, record_origin, no_commit, mainline, signoff, allow_ff;
 static enum { REVERT, CHERRY_PICK } action;
 static struct commit *commit;
 static int commit_argc;
@@ -91,7 +91,7 @@ static void parse_args(int argc, const char **argv)
 
 	if (action == CHERRY_PICK) {
 		struct option cp_extra[] = {
-			OPT_BOOLEAN('x', NULL, &no_replay, "append commit name"),
+			OPT_BOOLEAN('x', NULL, &record_origin, "append commit name"),
 			OPT_BOOLEAN(0, "ff", &allow_ff, "allow fast-forward"),
 			OPT_END(),
 		};
@@ -464,7 +464,7 @@ static int do_pick_commit(void)
 			strbuf_addstr(&msgbuf, p);
 		}
 
-		if (no_replay) {
+		if (record_origin) {
 			strbuf_addstr(&msgbuf, "(cherry picked from commit ");
 			strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
 			strbuf_addstr(&msgbuf, ")\n");
@@ -559,7 +559,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
 			die(_("cherry-pick --ff cannot be used with --signoff"));
 		if (no_commit)
 			die(_("cherry-pick --ff cannot be used with --no-commit"));
-		if (no_replay)
+		if (record_origin)
 			die(_("cherry-pick --ff cannot be used with -x"));
 		if (edit)
 			die(_("cherry-pick --ff cannot be used with --edit"));
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 06/18] revert: Propogate errors upwards from do_pick_commit
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
                   ` (4 preceding siblings ...)
  2011-07-19 17:17 ` [PATCH 05/18] revert: Rename no_replay to record_origin Ramkumar Ramachandra
@ 2011-07-19 17:17 ` Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 07/18] revert: Eliminate global "commit" variable Ramkumar Ramachandra
                   ` (13 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

Currently, revert_or_cherry_pick can fail in two ways.  If it
encounters a conflict, it returns a positive number indicating the
intended exit status for the git wrapper to pass on; for all other
errors, it calls die().  The latter behavior is inconsiderate towards
callers, as it denies them the opportunity to recover from errors and
do other things.

After this patch, revert_or_cherry_pick will still return a positive
return value to indicate an exit status for conflicts as before, while
for some other errors, it will print an error message and return -1
instead of die()-ing.  The cmd_revert and cmd_cherry_pick are adjusted
to handle the fatal errors by die()-ing themselves.

While the full benefits of this patch will only be seen once all the
"die" calls are replaced with calls to "error", its immediate impact
is to change some "fatal:" messages to say "error:" and to add a new
"fatal: cherry-pick failed" message at the end when the operation
fails.

Inspired-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Jonathan Nieder <jrnieder@gmail.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 builtin/revert.c |   67 ++++++++++++++++++++++++++++-------------------------
 1 files changed, 35 insertions(+), 32 deletions(-)

diff --git a/builtin/revert.c b/builtin/revert.c
index 794c050..5dbea40 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -241,25 +241,20 @@ static struct tree *empty_tree(void)
 	return tree;
 }
 
-static NORETURN void die_dirty_index(const char *me)
+static int error_dirty_index(const char *me)
 {
-	if (read_cache_unmerged()) {
-		die_resolve_conflict(me);
-	} else {
-		if (advice_commit_before_merge) {
-			if (action == REVERT)
-				die(_("Your local changes would be overwritten by revert.\n"
-					  "Please, commit your changes or stash them to proceed."));
-			else
-				die(_("Your local changes would be overwritten by cherry-pick.\n"
-					  "Please, commit your changes or stash them to proceed."));
-		} else {
-			if (action == REVERT)
-				die(_("Your local changes would be overwritten by revert.\n"));
-			else
-				die(_("Your local changes would be overwritten by cherry-pick.\n"));
-		}
-	}
+	if (read_cache_unmerged())
+		return error_resolve_conflict(me);
+
+	/* Different translation strings for cherry-pick and revert */
+	if (action == CHERRY_PICK)
+		error(_("Your local changes would be overwritten by cherry-pick."));
+	else
+		error(_("Your local changes would be overwritten by revert."));
+
+	if (advice_commit_before_merge)
+		advise(_("Please, commit your changes or stash them to proceed."));
+	return -1;
 }
 
 static int fast_forward_to(const unsigned char *to, const unsigned char *from)
@@ -376,9 +371,9 @@ static int do_pick_commit(void)
 			die (_("Your index file is unmerged."));
 	} else {
 		if (get_sha1("HEAD", head))
-			die (_("You do not have a valid HEAD"));
+			return error(_("You do not have a valid HEAD"));
 		if (index_differs_from("HEAD", 0))
-			die_dirty_index(me);
+			return error_dirty_index(me);
 	}
 	discard_cache();
 
@@ -391,20 +386,20 @@ static int do_pick_commit(void)
 		struct commit_list *p;
 
 		if (!mainline)
-			die(_("Commit %s is a merge but no -m option was given."),
-			    sha1_to_hex(commit->object.sha1));
+			return error(_("Commit %s is a merge but no -m option was given."),
+				sha1_to_hex(commit->object.sha1));
 
 		for (cnt = 1, p = commit->parents;
 		     cnt != mainline && p;
 		     cnt++)
 			p = p->next;
 		if (cnt != mainline || !p)
-			die(_("Commit %s does not have parent %d"),
-			    sha1_to_hex(commit->object.sha1), mainline);
+			return error(_("Commit %s does not have parent %d"),
+				sha1_to_hex(commit->object.sha1), mainline);
 		parent = p->item;
 	} else if (0 < mainline)
-		die(_("Mainline was specified but commit %s is not a merge."),
-		    sha1_to_hex(commit->object.sha1));
+		return error(_("Mainline was specified but commit %s is not a merge."),
+			sha1_to_hex(commit->object.sha1));
 	else
 		parent = commit->parents->item;
 
@@ -414,12 +409,12 @@ static int do_pick_commit(void)
 	if (parent && parse_commit(parent) < 0)
 		/* TRANSLATORS: The first %s will be "revert" or
 		   "cherry-pick", the second %s a SHA1 */
-		die(_("%s: cannot parse parent commit %s"),
-		    me, sha1_to_hex(parent->object.sha1));
+		return error(_("%s: cannot parse parent commit %s"),
+			me, sha1_to_hex(parent->object.sha1));
 
 	if (get_message(commit->buffer, &msg) != 0)
-		die(_("Cannot get commit message for %s"),
-				sha1_to_hex(commit->object.sha1));
+		return error(_("Cannot get commit message for %s"),
+			sha1_to_hex(commit->object.sha1));
 
 	/*
 	 * "commit" is an existing commit.  We would want to apply
@@ -580,14 +575,22 @@ static int revert_or_cherry_pick(int argc, const char **argv)
 
 int cmd_revert(int argc, const char **argv, const char *prefix)
 {
+	int res;
 	if (isatty(0))
 		edit = 1;
 	action = REVERT;
-	return revert_or_cherry_pick(argc, argv);
+	res = revert_or_cherry_pick(argc, argv);
+	if (res < 0)
+		die(_("revert failed"));
+	return res;
 }
 
 int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
 {
+	int res;
 	action = CHERRY_PICK;
-	return revert_or_cherry_pick(argc, argv);
+	res = revert_or_cherry_pick(argc, argv);
+	if (res < 0)
+		die(_("cherry-pick failed"));
+	return res;
 }
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 07/18] revert: Eliminate global "commit" variable
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
                   ` (5 preceding siblings ...)
  2011-07-19 17:17 ` [PATCH 06/18] revert: Propogate errors upwards from do_pick_commit Ramkumar Ramachandra
@ 2011-07-19 17:17 ` Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 08/18] revert: Introduce struct to keep command-line options Ramkumar Ramachandra
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

Functions which act on commits currently rely on a file-scope static
variable to be set before they're called.  Consequently, the API and
corresponding callsites are ugly and unclear.  Remove this variable
and change their API to accept the commit to act on as additional
argument so that the callsites change from looking like

commit = prepare_a_commit();
act_on_commit();

to looking like

commit = prepare_a_commit();
act_on_commit(commit);

This change is also in line with our long-term goal of exposing some
of these functions through a public API.

Inspired-by: Christian Couder <chriscool@tuxfamily.org>
Helped-by: Jonathan Nieder <jrnieder@gmail.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 builtin/revert.c |   22 +++++++++++-----------
 1 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/builtin/revert.c b/builtin/revert.c
index 5dbea40..ab8dcd3 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -37,7 +37,6 @@ static const char * const cherry_pick_usage[] = {
 
 static int edit, record_origin, no_commit, mainline, signoff, allow_ff;
 static enum { REVERT, CHERRY_PICK } action;
-static struct commit *commit;
 static int commit_argc;
 static const char **commit_argv;
 static int allow_rerere_auto;
@@ -116,25 +115,25 @@ struct commit_message {
 	const char *message;
 };
 
-static int get_message(const char *raw_message, struct commit_message *out)
+static int get_message(struct commit *commit, struct commit_message *out)
 {
 	const char *encoding;
 	const char *abbrev, *subject;
 	int abbrev_len, subject_len;
 	char *q;
 
-	if (!raw_message)
+	if (!commit->buffer)
 		return -1;
-	encoding = get_encoding(raw_message);
+	encoding = get_encoding(commit->buffer);
 	if (!encoding)
 		encoding = "UTF-8";
 	if (!git_commit_encoding)
 		git_commit_encoding = "UTF-8";
 
 	out->reencoded_message = NULL;
-	out->message = raw_message;
+	out->message = commit->buffer;
 	if (strcmp(encoding, git_commit_encoding))
-		out->reencoded_message = reencode_string(raw_message,
+		out->reencoded_message = reencode_string(commit->buffer,
 					git_commit_encoding, encoding);
 	if (out->reencoded_message)
 		out->message = out->reencoded_message;
@@ -182,7 +181,7 @@ static char *get_encoding(const char *message)
 	return NULL;
 }
 
-static void write_cherry_pick_head(void)
+static void write_cherry_pick_head(struct commit *commit)
 {
 	int fd;
 	struct strbuf buf = STRBUF_INIT;
@@ -350,7 +349,7 @@ static int run_git_commit(const char *defmsg)
 	return run_command_v_opt(args, RUN_GIT_CMD);
 }
 
-static int do_pick_commit(void)
+static int do_pick_commit(struct commit *commit)
 {
 	unsigned char head[20];
 	struct commit *base, *next, *parent;
@@ -412,7 +411,7 @@ static int do_pick_commit(void)
 		return error(_("%s: cannot parse parent commit %s"),
 			me, sha1_to_hex(parent->object.sha1));
 
-	if (get_message(commit->buffer, &msg) != 0)
+	if (get_message(commit, &msg) != 0)
 		return error(_("Cannot get commit message for %s"),
 			sha1_to_hex(commit->object.sha1));
 
@@ -465,7 +464,7 @@ static int do_pick_commit(void)
 			strbuf_addstr(&msgbuf, ")\n");
 		}
 		if (!no_commit)
-			write_cherry_pick_head();
+			write_cherry_pick_head(commit);
 	}
 
 	if (!strategy || !strcmp(strategy, "recursive") || action == REVERT) {
@@ -543,6 +542,7 @@ static void read_and_refresh_cache(const char *me)
 static int revert_or_cherry_pick(int argc, const char **argv)
 {
 	struct rev_info revs;
+	struct commit *commit;
 
 	git_config(git_default_config, NULL);
 	me = action == REVERT ? "revert" : "cherry-pick";
@@ -565,7 +565,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
 	prepare_revs(&revs);
 
 	while ((commit = get_revision(&revs))) {
-		int res = do_pick_commit();
+		int res = do_pick_commit(commit);
 		if (res)
 			return res;
 	}
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 08/18] revert: Introduce struct to keep command-line options
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
                   ` (6 preceding siblings ...)
  2011-07-19 17:17 ` [PATCH 07/18] revert: Eliminate global "commit" variable Ramkumar Ramachandra
@ 2011-07-19 17:17 ` Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 09/18] revert: Separate cmdline parsing from functional code Ramkumar Ramachandra
                   ` (11 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

The current code uses a set of file-scope static variables to tell the
cherry-pick/ revert machinery how to replay the changes, and
initializes them by parsing the command-line arguments.  In later
steps in this series, we would like to introduce an API function that
calls into this machinery directly and have a way to tell it what to
do.  Hence, introduce a structure to group these variables, so that
the API can take them as a single replay_options parameter.  The only
exception is the variable "me" -- remove it since it not an
independent option, and can be inferred from the action.

Unfortunately, this patch introduces a minor regression.  Parsing
strategy-option violates a C89 rule: Initializers cannot refer to
variables whose address is not known at compile time.  Currently, this
rule is violated by some other parts of Git as well, and it is
possible to get GCC to report these instances using the "-std=c89
-pedantic" option.

Inspired-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Jonathan Nieder <jrnieder@gmail.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 builtin/revert.c |  199 ++++++++++++++++++++++++++++++------------------------
 1 files changed, 112 insertions(+), 87 deletions(-)

diff --git a/builtin/revert.c b/builtin/revert.c
index ab8dcd3..c118fd3 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -35,76 +35,94 @@ static const char * const cherry_pick_usage[] = {
 	NULL
 };
 
-static int edit, record_origin, no_commit, mainline, signoff, allow_ff;
-static enum { REVERT, CHERRY_PICK } action;
-static int commit_argc;
-static const char **commit_argv;
-static int allow_rerere_auto;
-
-static const char *me;
-
-/* Merge strategy. */
-static const char *strategy;
-static const char **xopts;
-static size_t xopts_nr, xopts_alloc;
+enum replay_action { REVERT, CHERRY_PICK };
+
+struct replay_opts {
+	enum replay_action action;
+
+	/* Boolean options */
+	int edit;
+	int record_origin;
+	int no_commit;
+	int signoff;
+	int allow_ff;
+	int allow_rerere_auto;
+
+	int mainline;
+	int commit_argc;
+	const char **commit_argv;
+
+	/* Merge strategy */
+	const char *strategy;
+	const char **xopts;
+	size_t xopts_nr, xopts_alloc;
+};
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
+static const char *action_name(const struct replay_opts *opts)
+{
+	return opts->action == REVERT ? "revert" : "cherry-pick";
+}
+
 static char *get_encoding(const char *message);
 
-static const char * const *revert_or_cherry_pick_usage(void)
+static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
 {
-	return action == REVERT ? revert_usage : cherry_pick_usage;
+	return opts->action == REVERT ? revert_usage : cherry_pick_usage;
 }
 
 static int option_parse_x(const struct option *opt,
 			  const char *arg, int unset)
 {
+	struct replay_opts **opts_ptr = opt->value;
+	struct replay_opts *opts = *opts_ptr;
+
 	if (unset)
 		return 0;
 
-	ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc);
-	xopts[xopts_nr++] = xstrdup(arg);
+	ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
+	opts->xopts[opts->xopts_nr++] = xstrdup(arg);
 	return 0;
 }
 
-static void parse_args(int argc, const char **argv)
+static void parse_args(int argc, const char **argv, struct replay_opts *opts)
 {
-	const char * const * usage_str = revert_or_cherry_pick_usage();
+	const char * const * usage_str = revert_or_cherry_pick_usage(opts);
 	int noop;
 	struct option options[] = {
-		OPT_BOOLEAN('n', "no-commit", &no_commit, "don't automatically commit"),
-		OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"),
+		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)",
 		  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 0 },
-		OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
-		OPT_INTEGER('m', "mainline", &mainline, "parent number"),
-		OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
-		OPT_STRING(0, "strategy", &strategy, "strategy", "merge strategy"),
-		OPT_CALLBACK('X', "strategy-option", &xopts, "option",
+		OPT_BOOLEAN('s', "signoff", &opts->signoff, "add Signed-off-by:"),
+		OPT_INTEGER('m', "mainline", &opts->mainline, "parent number"),
+		OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
+		OPT_STRING(0, "strategy", &opts->strategy, "strategy", "merge strategy"),
+		OPT_CALLBACK('X', "strategy-option", &opts, "option",
 			"option for merge strategy", option_parse_x),
 		OPT_END(),
 		OPT_END(),
 		OPT_END(),
 	};
 
-	if (action == CHERRY_PICK) {
+	if (opts->action == CHERRY_PICK) {
 		struct option cp_extra[] = {
-			OPT_BOOLEAN('x', NULL, &record_origin, "append commit name"),
-			OPT_BOOLEAN(0, "ff", &allow_ff, "allow fast-forward"),
+			OPT_BOOLEAN('x', NULL, &opts->record_origin, "append commit name"),
+			OPT_BOOLEAN(0, "ff", &opts->allow_ff, "allow fast-forward"),
 			OPT_END(),
 		};
 		if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))
 			die(_("program error"));
 	}
 
-	commit_argc = parse_options(argc, argv, NULL, options, usage_str,
-				    PARSE_OPT_KEEP_ARGV0 |
-				    PARSE_OPT_KEEP_UNKNOWN);
-	if (commit_argc < 2)
+	opts->commit_argc = parse_options(argc, argv, NULL, options, usage_str,
+					PARSE_OPT_KEEP_ARGV0 |
+					PARSE_OPT_KEEP_UNKNOWN);
+	if (opts->commit_argc < 2)
 		usage_with_options(usage_str, options);
 
-	commit_argv = argv;
+	opts->commit_argv = argv;
 }
 
 struct commit_message {
@@ -240,13 +258,13 @@ static struct tree *empty_tree(void)
 	return tree;
 }
 
-static int error_dirty_index(const char *me)
+static int error_dirty_index(struct replay_opts *opts)
 {
 	if (read_cache_unmerged())
-		return error_resolve_conflict(me);
+		return error_resolve_conflict(action_name(opts));
 
 	/* Different translation strings for cherry-pick and revert */
-	if (action == CHERRY_PICK)
+	if (opts->action == CHERRY_PICK)
 		error(_("Your local changes would be overwritten by cherry-pick."));
 	else
 		error(_("Your local changes would be overwritten by revert."));
@@ -269,7 +287,8 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from)
 
 static int do_recursive_merge(struct commit *base, struct commit *next,
 			      const char *base_label, const char *next_label,
-			      unsigned char *head, struct strbuf *msgbuf)
+			      unsigned char *head, struct strbuf *msgbuf,
+			      struct replay_opts *opts)
 {
 	struct merge_options o;
 	struct tree *result, *next_tree, *base_tree, *head_tree;
@@ -290,7 +309,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
 	next_tree = next ? next->tree : empty_tree();
 	base_tree = base ? base->tree : empty_tree();
 
-	for (xopt = xopts; xopt != xopts + xopts_nr; xopt++)
+	for (xopt = opts->xopts; xopt != opts->xopts + opts->xopts_nr; xopt++)
 		parse_merge_opt(&o, *xopt);
 
 	clean = merge_trees(&o,
@@ -301,7 +320,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
 	    (write_cache(index_fd, active_cache, active_nr) ||
 	     commit_locked_index(&index_lock)))
 		/* TRANSLATORS: %s will be "revert" or "cherry-pick" */
-		die(_("%s: Unable to write new index file"), me);
+		die(_("%s: Unable to write new index file"), action_name(opts));
 	rollback_lock_file(&index_lock);
 
 	if (!clean) {
@@ -330,7 +349,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
  * If we are revert, or if our cherry-pick results in a hand merge,
  * we had better say that the current user is responsible for that.
  */
-static int run_git_commit(const char *defmsg)
+static int run_git_commit(const char *defmsg, struct replay_opts *opts)
 {
 	/* 6 is max possible length of our args array including NULL */
 	const char *args[6];
@@ -338,9 +357,9 @@ static int run_git_commit(const char *defmsg)
 
 	args[i++] = "commit";
 	args[i++] = "-n";
-	if (signoff)
+	if (opts->signoff)
 		args[i++] = "-s";
-	if (!edit) {
+	if (!opts->edit) {
 		args[i++] = "-F";
 		args[i++] = defmsg;
 	}
@@ -349,7 +368,7 @@ static int run_git_commit(const char *defmsg)
 	return run_command_v_opt(args, RUN_GIT_CMD);
 }
 
-static int do_pick_commit(struct commit *commit)
+static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
 {
 	unsigned char head[20];
 	struct commit *base, *next, *parent;
@@ -359,7 +378,7 @@ static int do_pick_commit(struct commit *commit)
 	struct strbuf msgbuf = STRBUF_INIT;
 	int res;
 
-	if (no_commit) {
+	if (opts->no_commit) {
 		/*
 		 * We do not intend to commit immediately.  We just want to
 		 * merge the differences in, so let's compute the tree
@@ -372,7 +391,7 @@ static int do_pick_commit(struct commit *commit)
 		if (get_sha1("HEAD", head))
 			return error(_("You do not have a valid HEAD"));
 		if (index_differs_from("HEAD", 0))
-			return error_dirty_index(me);
+			return error_dirty_index(opts);
 	}
 	discard_cache();
 
@@ -384,32 +403,32 @@ static int do_pick_commit(struct commit *commit)
 		int cnt;
 		struct commit_list *p;
 
-		if (!mainline)
+		if (!opts->mainline)
 			return error(_("Commit %s is a merge but no -m option was given."),
 				sha1_to_hex(commit->object.sha1));
 
 		for (cnt = 1, p = commit->parents;
-		     cnt != mainline && p;
+		     cnt != opts->mainline && p;
 		     cnt++)
 			p = p->next;
-		if (cnt != mainline || !p)
+		if (cnt != opts->mainline || !p)
 			return error(_("Commit %s does not have parent %d"),
-				sha1_to_hex(commit->object.sha1), mainline);
+				sha1_to_hex(commit->object.sha1), opts->mainline);
 		parent = p->item;
-	} else if (0 < mainline)
+	} else if (0 < opts->mainline)
 		return error(_("Mainline was specified but commit %s is not a merge."),
 			sha1_to_hex(commit->object.sha1));
 	else
 		parent = commit->parents->item;
 
-	if (allow_ff && parent && !hashcmp(parent->object.sha1, head))
+	if (opts->allow_ff && parent && !hashcmp(parent->object.sha1, head))
 		return fast_forward_to(commit->object.sha1, head);
 
 	if (parent && parse_commit(parent) < 0)
 		/* TRANSLATORS: The first %s will be "revert" or
 		   "cherry-pick", the second %s a SHA1 */
 		return error(_("%s: cannot parse parent commit %s"),
-			me, sha1_to_hex(parent->object.sha1));
+			action_name(opts), sha1_to_hex(parent->object.sha1));
 
 	if (get_message(commit, &msg) != 0)
 		return error(_("Cannot get commit message for %s"),
@@ -424,7 +443,7 @@ static int do_pick_commit(struct commit *commit)
 
 	defmsg = git_pathdup("MERGE_MSG");
 
-	if (action == REVERT) {
+	if (opts->action == REVERT) {
 		base = commit;
 		base_label = msg.label;
 		next = parent;
@@ -458,18 +477,18 @@ static int do_pick_commit(struct commit *commit)
 			strbuf_addstr(&msgbuf, p);
 		}
 
-		if (record_origin) {
+		if (opts->record_origin) {
 			strbuf_addstr(&msgbuf, "(cherry picked from commit ");
 			strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
 			strbuf_addstr(&msgbuf, ")\n");
 		}
-		if (!no_commit)
+		if (!opts->no_commit)
 			write_cherry_pick_head(commit);
 	}
 
-	if (!strategy || !strcmp(strategy, "recursive") || action == REVERT) {
+	if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REVERT) {
 		res = do_recursive_merge(base, next, base_label, next_label,
-					 head, &msgbuf);
+					 head, &msgbuf, opts);
 		write_message(&msgbuf, defmsg);
 	} else {
 		struct commit_list *common = NULL;
@@ -479,23 +498,23 @@ static int do_pick_commit(struct commit *commit)
 
 		commit_list_insert(base, &common);
 		commit_list_insert(next, &remotes);
-		res = try_merge_command(strategy, xopts_nr, xopts, common,
-					sha1_to_hex(head), remotes);
+		res = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts,
+					common, sha1_to_hex(head), remotes);
 		free_commit_list(common);
 		free_commit_list(remotes);
 	}
 
 	if (res) {
-		error(action == REVERT
+		error(opts->action == REVERT
 		      ? _("could not revert %s... %s")
 		      : _("could not apply %s... %s"),
 		      find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),
 		      msg.subject);
 		print_advice();
-		rerere(allow_rerere_auto);
+		rerere(opts->allow_rerere_auto);
 	} else {
-		if (!no_commit)
-			res = run_git_commit(defmsg);
+		if (!opts->no_commit)
+			res = run_git_commit(defmsg, opts);
 	}
 
 	free_message(&msg);
@@ -504,18 +523,18 @@ static int do_pick_commit(struct commit *commit)
 	return res;
 }
 
-static void prepare_revs(struct rev_info *revs)
+static void prepare_revs(struct rev_info *revs, struct replay_opts *opts)
 {
 	int argc;
 
 	init_revisions(revs, NULL);
 	revs->no_walk = 1;
-	if (action != REVERT)
+	if (opts->action != REVERT)
 		revs->reverse = 1;
 
-	argc = setup_revisions(commit_argc, commit_argv, revs, NULL);
+	argc = setup_revisions(opts->commit_argc, opts->commit_argv, revs, NULL);
 	if (argc > 1)
-		usage(*revert_or_cherry_pick_usage());
+		usage(*revert_or_cherry_pick_usage(opts));
 
 	if (prepare_revision_walk(revs))
 		die(_("revision walk setup failed"));
@@ -524,48 +543,48 @@ static void prepare_revs(struct rev_info *revs)
 		die(_("empty commit set passed"));
 }
 
-static void read_and_refresh_cache(const char *me)
+static void read_and_refresh_cache(struct replay_opts *opts)
 {
 	static struct lock_file index_lock;
 	int index_fd = hold_locked_index(&index_lock, 0);
 	if (read_index_preload(&the_index, NULL) < 0)
-		die(_("git %s: failed to read the index"), me);
+		die(_("git %s: failed to read the index"), action_name(opts));
 	refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
 	if (the_index.cache_changed) {
 		if (write_index(&the_index, index_fd) ||
 		    commit_locked_index(&index_lock))
-			die(_("git %s: failed to refresh the index"), me);
+			die(_("git %s: failed to refresh the index"), action_name(opts));
 	}
 	rollback_lock_file(&index_lock);
 }
 
-static int revert_or_cherry_pick(int argc, const char **argv)
+static int revert_or_cherry_pick(int argc, const char **argv,
+				struct replay_opts *opts)
 {
 	struct rev_info revs;
 	struct commit *commit;
 
 	git_config(git_default_config, NULL);
-	me = action == REVERT ? "revert" : "cherry-pick";
-	setenv(GIT_REFLOG_ACTION, me, 0);
-	parse_args(argc, argv);
+	setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
+	parse_args(argc, argv, opts);
 
-	if (allow_ff) {
-		if (signoff)
+	if (opts->allow_ff) {
+		if (opts->signoff)
 			die(_("cherry-pick --ff cannot be used with --signoff"));
-		if (no_commit)
+		if (opts->no_commit)
 			die(_("cherry-pick --ff cannot be used with --no-commit"));
-		if (record_origin)
+		if (opts->record_origin)
 			die(_("cherry-pick --ff cannot be used with -x"));
-		if (edit)
+		if (opts->edit)
 			die(_("cherry-pick --ff cannot be used with --edit"));
 	}
 
-	read_and_refresh_cache(me);
+	read_and_refresh_cache(opts);
 
-	prepare_revs(&revs);
+	prepare_revs(&revs, opts);
 
 	while ((commit = get_revision(&revs))) {
-		int res = do_pick_commit(commit);
+		int res = do_pick_commit(commit, opts);
 		if (res)
 			return res;
 	}
@@ -576,10 +595,13 @@ static int revert_or_cherry_pick(int argc, const char **argv)
 int cmd_revert(int argc, const char **argv, const char *prefix)
 {
 	int res;
+	struct replay_opts opts;
+
+	memset(&opts, 0, sizeof(struct replay_opts));
 	if (isatty(0))
-		edit = 1;
-	action = REVERT;
-	res = revert_or_cherry_pick(argc, argv);
+		opts.edit = 1;
+	opts.action = REVERT;
+	res = revert_or_cherry_pick(argc, argv, &opts);
 	if (res < 0)
 		die(_("revert failed"));
 	return res;
@@ -588,8 +610,11 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
 int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
 {
 	int res;
-	action = CHERRY_PICK;
-	res = revert_or_cherry_pick(argc, argv);
+	struct replay_opts opts;
+
+	memset(&opts, 0, sizeof(struct replay_opts));
+	opts.action = CHERRY_PICK;
+	res = revert_or_cherry_pick(argc, argv, &opts);
 	if (res < 0)
 		die(_("cherry-pick failed"));
 	return res;
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 09/18] revert: Separate cmdline parsing from functional code
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
                   ` (7 preceding siblings ...)
  2011-07-19 17:17 ` [PATCH 08/18] revert: Introduce struct to keep command-line options Ramkumar Ramachandra
@ 2011-07-19 17:17 ` Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 10/18] revert: Don't create invalid replay_opts in parse_args Ramkumar Ramachandra
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

Currently, revert_or_cherry_pick sets up a default git config, parses
command-line arguments, before preparing to pick commits.  This makes
for a bad API as the central worry of callers is to assert whether or
not a conflict occured while cherry picking.  The current API is like:

if (revert_or_cherry_pick(argc, argv, opts) < 0)
   print "Something failed, we're not sure what"

Simplify and rename revert_or_cherry_pick to pick_commits so that it
only has the responsibility of setting up the revision walker and
picking commits in a loop.  Transfer the remaining work to its
callers.  Now, the API is simplified as:

if (parse_args(argc, argv, opts) < 0)
   print "Can't parse arguments"
if (pick_commits(opts) < 0)
   print "Error encountered in picking machinery"

Later in the series, pick_commits will also serve as the starting
point for continuing a cherry-pick or revert.

Inspired-by: Christian Couder <chriscool@tuxfamily.org>
Helped-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 builtin/revert.c |   14 +++++++-------
 1 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/builtin/revert.c b/builtin/revert.c
index c118fd3..047b0aa 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -558,16 +558,12 @@ static void read_and_refresh_cache(struct replay_opts *opts)
 	rollback_lock_file(&index_lock);
 }
 
-static int revert_or_cherry_pick(int argc, const char **argv,
-				struct replay_opts *opts)
+static int pick_commits(struct replay_opts *opts)
 {
 	struct rev_info revs;
 	struct commit *commit;
 
-	git_config(git_default_config, NULL);
 	setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
-	parse_args(argc, argv, opts);
-
 	if (opts->allow_ff) {
 		if (opts->signoff)
 			die(_("cherry-pick --ff cannot be used with --signoff"));
@@ -601,7 +597,9 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
 	if (isatty(0))
 		opts.edit = 1;
 	opts.action = REVERT;
-	res = revert_or_cherry_pick(argc, argv, &opts);
+	git_config(git_default_config, NULL);
+	parse_args(argc, argv, &opts);
+	res = pick_commits(&opts);
 	if (res < 0)
 		die(_("revert failed"));
 	return res;
@@ -614,7 +612,9 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
 
 	memset(&opts, 0, sizeof(struct replay_opts));
 	opts.action = CHERRY_PICK;
-	res = revert_or_cherry_pick(argc, argv, &opts);
+	git_config(git_default_config, NULL);
+	parse_args(argc, argv, &opts);
+	res = pick_commits(&opts);
 	if (res < 0)
 		die(_("cherry-pick failed"));
 	return res;
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 10/18] revert: Don't create invalid replay_opts in parse_args
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
                   ` (8 preceding siblings ...)
  2011-07-19 17:17 ` [PATCH 09/18] revert: Separate cmdline parsing from functional code Ramkumar Ramachandra
@ 2011-07-19 17:17 ` Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 11/18] revert: Save data for continuing after conflict resolution Ramkumar Ramachandra
                   ` (9 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

The "--ff" command-line option cannot be used with some other
command-line options.  However, parse_args still parses these
incompatible options into a replay_opts structure for use by the rest
of the program.  Although pick_commits, the current gatekeeper to the
cherry-pick machinery, checks the validity of the replay_opts
structure before before starting its operation, there will be multiple
entry points to the cherry-pick machinery in future.  To futureproof
the code and catch these errors in one place, make sure that an
invalid replay_opts structure is not created by parse_args in the
first place.  We still check the replay_opts structure for validity in
pick_commits, but this is an assert() now to emphasize that it's the
caller's responsibility to get it right.

Inspired-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Jonathan Nieder <jrnieder@gmail.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 builtin/revert.c |   38 +++++++++++++++++++++++++++-----------
 1 files changed, 27 insertions(+), 11 deletions(-)

diff --git a/builtin/revert.c b/builtin/revert.c
index 047b0aa..bc86cc7 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -86,9 +86,26 @@ static int option_parse_x(const struct option *opt,
 	return 0;
 }
 
+static void verify_opt_compatible(const char *me, const char *base_opt, ...)
+{
+	const char *this_opt;
+	va_list ap;
+	int set;
+
+	va_start(ap, base_opt);
+	while ((this_opt = va_arg(ap, const char *))) {
+		set = va_arg(ap, int);
+		if (set)
+			die(_("%s: %s cannot be used with %s"),
+				me, this_opt, base_opt);
+	}
+	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);
+	const char *me = action_name(opts);
 	int noop;
 	struct option options[] = {
 		OPT_BOOLEAN('n', "no-commit", &opts->no_commit, "don't automatically commit"),
@@ -122,6 +139,13 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
 	if (opts->commit_argc < 2)
 		usage_with_options(usage_str, options);
 
+	if (opts->allow_ff)
+		verify_opt_compatible(me, "--ff",
+				"--signoff", opts->signoff,
+				"--no-commit", opts->no_commit,
+				"-x", opts->record_origin,
+				"--edit", opts->edit,
+				NULL);
 	opts->commit_argv = argv;
 }
 
@@ -564,17 +588,9 @@ static int pick_commits(struct replay_opts *opts)
 	struct commit *commit;
 
 	setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
-	if (opts->allow_ff) {
-		if (opts->signoff)
-			die(_("cherry-pick --ff cannot be used with --signoff"));
-		if (opts->no_commit)
-			die(_("cherry-pick --ff cannot be used with --no-commit"));
-		if (opts->record_origin)
-			die(_("cherry-pick --ff cannot be used with -x"));
-		if (opts->edit)
-			die(_("cherry-pick --ff cannot be used with --edit"));
-	}
-
+	if (opts->allow_ff)
+		assert(!(opts->signoff || opts->no_commit ||
+				opts->record_origin || opts->edit));
 	read_and_refresh_cache(opts);
 
 	prepare_revs(&revs, opts);
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 11/18] revert: Save data for continuing after conflict resolution
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
                   ` (9 preceding siblings ...)
  2011-07-19 17:17 ` [PATCH 10/18] revert: Don't create invalid replay_opts in parse_args Ramkumar Ramachandra
@ 2011-07-19 17:17 ` Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 12/18] revert: Save command-line options for continuing operation Ramkumar Ramachandra
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

Ever since v1.7.2-rc1~4^2~7 (revert: allow cherry-picking more than
one commit, 2010-06-02), a single invocation of "git cherry-pick" or
"git revert" can perform picks of several individual commits.  To
implement features like "--continue" to continue the whole operation,
we will need to store some information about the state and the plan at
the beginning.  Introduce a ".git/sequencer/head" file to store this
state, and ".git/sequencer/todo" file to store the plan.  The head
file contains the SHA-1 of the HEAD before the start of the operation,
and the todo file contains an instruction sheet whose format is
inspired by the format of the "rebase -i" instruction sheet.  As a
result, a typical todo file looks like:

pick 8537f0e submodule add: test failure when url is not configured
pick 4d68932 submodule add: allow relative repository path
pick f22a17e submodule add: clean up duplicated code
pick 59a5775 make copy_ref globally available

Since SHA-1 hex is abbreviated using an find_unique_abbrev(), it is
unambiguous.  This does not guarantee that there will be no ambiguity
when more objects are added to the repository.

That these two files alone are not enough to implement a "--continue"
that remembers the command-line options specified; later patches in
the series save them too.

These new files are unrelated to the existing .git/CHERRY_PICK_HEAD,
which will still be useful while committing after a conflict
resolution.

Inspired-by: Christian Couder <chriscool@tuxfamily.org>
Helped-by: Jonathan Nieder <jrnieder@gmail.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 builtin/revert.c                |  222 +++++++++++++++++++++++++++++++++++++-
 t/t3510-cherry-pick-sequence.sh |   48 +++++++++
 2 files changed, 264 insertions(+), 6 deletions(-)
 create mode 100755 t/t3510-cherry-pick-sequence.sh

diff --git a/builtin/revert.c b/builtin/revert.c
index bc86cc7..cdfa906 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -13,6 +13,7 @@
 #include "rerere.h"
 #include "merge-recursive.h"
 #include "refs.h"
+#include "dir.h"
 
 /*
  * This implements the builtins revert and cherry-pick.
@@ -59,6 +60,11 @@ struct replay_opts {
 };
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
+#define MAYBE_UNUSED __attribute__((__unused__))
+
+#define SEQ_DIR         "sequencer"
+#define SEQ_HEAD_FILE   "sequencer/head"
+#define SEQ_TODO_FILE   "sequencer/todo"
 
 static const char *action_name(const struct replay_opts *opts)
 {
@@ -269,7 +275,7 @@ static void write_message(struct strbuf *msgbuf, const char *filename)
 		die_errno(_("Could not write to %s."), filename);
 	strbuf_release(msgbuf);
 	if (commit_lock_file(&msg_file) < 0)
-		die(_("Error wrapping up %s"), filename);
+		die(_("Error wrapping up %s."), filename);
 }
 
 static struct tree *empty_tree(void)
@@ -582,10 +588,199 @@ static void read_and_refresh_cache(struct replay_opts *opts)
 	rollback_lock_file(&index_lock);
 }
 
-static int pick_commits(struct replay_opts *opts)
+/*
+ * Append a commit to the end of the commit_list.
+ *
+ * next starts by pointing to the variable that holds the head of an
+ * empty commit_list, and is updated to point to the "next" field of
+ * the last item on the list as new commits are appended.
+ *
+ * Usage example:
+ *
+ *     struct commit_list *list;
+ *     struct commit_list **next = &list;
+ *
+ *     next = commit_list_append(c1, next);
+ *     next = commit_list_append(c2, next);
+ *     assert(commit_list_count(list) == 2);
+ *     return list;
+ */
+struct commit_list **commit_list_append(struct commit *commit,
+					struct commit_list **next)
+{
+	struct commit_list *new = xmalloc(sizeof(struct commit_list));
+	new->item = commit;
+	*next = new;
+	new->next = NULL;
+	return &new->next;
+}
+
+static int format_todo(struct strbuf *buf, struct commit_list *todo_list,
+		struct replay_opts *opts)
+{
+	struct commit_list *cur = NULL;
+	struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
+	const char *sha1_abbrev = NULL;
+	const char *action_str = opts->action == REVERT ? "revert" : "pick";
+
+	for (cur = todo_list; cur; cur = cur->next) {
+		sha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);
+		if (get_message(cur->item, &msg))
+			return error(_("Cannot get commit message for %s"), sha1_abbrev);
+		strbuf_addf(buf, "%s %s %s\n", action_str, sha1_abbrev, msg.subject);
+	}
+	return 0;
+}
+
+static struct commit *parse_insn_line(char *start, struct replay_opts *opts)
+{
+	unsigned char commit_sha1[20];
+	char sha1_abbrev[40];
+	struct commit *commit;
+	enum replay_action action;
+	int insn_len = 0;
+	char *p, *q;
+
+	if (!prefixcmp(start, "pick ")) {
+		action = CHERRY_PICK;
+		insn_len = strlen("pick");
+		p = start + insn_len + 1;
+	}
+	else if (!prefixcmp(start, "revert ")) {
+		action = REVERT;
+		insn_len = strlen("revert");
+		p = start + insn_len + 1;
+	}
+	else
+		return NULL;
+
+	q = strchr(p, ' ');
+	if (!q)
+		return NULL;
+	q++;
+
+	strlcpy(sha1_abbrev, p, q - p);
+
+	/*
+	 * Verify that the action matches up with the one in
+	 * opts; we don't support arbitrary instructions
+	 */
+	if (action != opts->action) {
+		const char *action_str;
+		action_str = action == REVERT ? "revert" : "cherry-pick";
+		error(_("Cannot %s during a %s"), action_str, action_name(opts));
+		return NULL;
+	}
+
+	if ((get_sha1(sha1_abbrev, commit_sha1) < 0)
+		|| !(commit = lookup_commit_reference(commit_sha1)))
+		return NULL;
+
+	return commit;
+}
+
+static void MAYBE_UNUSED read_populate_todo(struct commit_list **todo_list,
+					struct replay_opts *opts)
+{
+	const char *todo_file = git_path(SEQ_TODO_FILE);
+	struct strbuf buf = STRBUF_INIT;
+	struct commit_list **next;
+	struct commit *commit;
+	char *p;
+	int fd;
+
+	fd = open(todo_file, O_RDONLY);
+	if (fd < 0) {
+		strbuf_release(&buf);
+		die_errno(_("Could not open %s."), todo_file);
+	}
+	if (strbuf_read(&buf, fd, 0) < buf.len) {
+		close(fd);
+		strbuf_release(&buf);
+		die(_("Could not read %s."), todo_file);
+	}
+	close(fd);
+
+	next = todo_list;
+	for (p = buf.buf; *p; p = strchr(p, '\n') + 1) {
+		if (!(commit = parse_insn_line(p, opts)))
+			goto error;
+		next = commit_list_append(commit, next);
+	}
+	if (!*todo_list)
+		goto error;
+	strbuf_release(&buf);
+	return;
+error:
+	strbuf_release(&buf);
+	die(_("Unusable instruction sheet: %s"), todo_file);
+}
+
+static void walk_revs_populate_todo(struct commit_list **todo_list,
+				struct replay_opts *opts)
 {
 	struct rev_info revs;
 	struct commit *commit;
+	struct commit_list **next;
+
+	prepare_revs(&revs, opts);
+
+	next = todo_list;
+	while ((commit = get_revision(&revs)))
+		next = commit_list_append(commit, next);
+}
+
+static void create_seq_dir(void)
+{
+	const char *seq_dir = git_path(SEQ_DIR);
+
+	if (!file_exists(seq_dir) && mkdir(seq_dir, 0777) < 0)
+		die_errno(_("Could not create sequencer directory '%s'."), seq_dir);
+}
+
+static void save_head(const char *head)
+{
+	const char *head_file = git_path(SEQ_HEAD_FILE);
+	static struct lock_file head_lock;
+	struct strbuf buf = STRBUF_INIT;
+	int fd;
+
+	fd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);
+	strbuf_addf(&buf, "%s\n", head);
+	if (write_in_full(fd, buf.buf, buf.len) < 0)
+		die_errno(_("Could not write to %s."), head_file);
+	if (commit_lock_file(&head_lock) < 0)
+		die(_("Error wrapping up %s."), head_file);
+}
+
+static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)
+{
+	const char *todo_file = git_path(SEQ_TODO_FILE);
+	static struct lock_file todo_lock;
+	struct strbuf buf = STRBUF_INIT;
+	int fd;
+
+	fd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);
+	if (format_todo(&buf, todo_list, opts) < 0)
+		die(_("Could not format %s."), todo_file);
+	if (write_in_full(fd, buf.buf, buf.len) < 0) {
+		strbuf_release(&buf);
+		die_errno(_("Could not write to %s."), todo_file);
+	}
+	if (commit_lock_file(&todo_lock) < 0) {
+		strbuf_release(&buf);
+		die(_("Error wrapping up %s."), todo_file);
+	}
+	strbuf_release(&buf);
+}
+
+static int pick_commits(struct replay_opts *opts)
+{
+	struct commit_list *todo_list = NULL;
+	struct strbuf buf = STRBUF_INIT;
+	unsigned char sha1[20];
+	struct commit_list *cur;
+	int res;
 
 	setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
 	if (opts->allow_ff)
@@ -593,14 +788,29 @@ static int pick_commits(struct replay_opts *opts)
 				opts->record_origin || opts->edit));
 	read_and_refresh_cache(opts);
 
-	prepare_revs(&revs, opts);
-
-	while ((commit = get_revision(&revs))) {
-		int res = do_pick_commit(commit, opts);
+	walk_revs_populate_todo(&todo_list, opts);
+	create_seq_dir();
+	if (get_sha1("HEAD", sha1)) {
+		if (opts->action == REVERT)
+			return error(_("Can't revert as initial commit"));
+		return error(_("Can't cherry-pick into empty head"));
+	} else
+		save_head(sha1_to_hex(sha1));
+	save_todo(todo_list, opts);
+
+	for (cur = todo_list; cur; cur = cur->next) {
+		save_todo(cur, opts);
+		res = do_pick_commit(cur->item, opts);
 		if (res)
 			return res;
 	}
 
+	/*
+	 * Sequence of picks finished successfully; cleanup by
+	 * removing the .git/sequencer directory
+	 */
+	strbuf_addf(&buf, "%s", git_path(SEQ_DIR));
+	remove_dir_recursively(&buf, 0);
 	return 0;
 }
 
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
new file mode 100755
index 0000000..64eaa20
--- /dev/null
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='Test cherry-pick continuation features
+
+  + anotherpick: rewrites foo to d
+  + picked: rewrites foo to c
+  + unrelatedpick: rewrites unrelated to reallyunrelated
+  + base: rewrites foo to b
+  + initial: writes foo as a, unrelated as unrelated
+
+'
+
+. ./test-lib.sh
+
+pristine_detach () {
+	git checkout -f "$1^0" &&
+	git read-tree -u --reset HEAD &&
+	git clean -d -f -f -q -x
+}
+
+test_expect_success setup '
+	echo unrelated >unrelated &&
+	git add unrelated &&
+	test_commit initial foo a &&
+	test_commit base foo b &&
+	test_commit unrelatedpick unrelated reallyunrelated &&
+	test_commit picked foo c &&
+	test_commit anotherpick foo d &&
+	git config advice.detachedhead false
+
+'
+
+test_expect_success 'cherry-pick persists data on failure' '
+	pristine_detach initial &&
+	test_must_fail git cherry-pick base..anotherpick &&
+	test_path_is_dir .git/sequencer &&
+	test_path_is_file .git/sequencer/head &&
+	test_path_is_file .git/sequencer/todo &&
+	rm -rf .git/sequencer
+'
+
+test_expect_success 'cherry-pick cleans up sequencer state upon success' '
+	pristine_detach initial &&
+	git cherry-pick initial..picked &&
+	test_path_is_missing .git/sequencer
+'
+
+test_done
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 12/18] revert: Save command-line options for continuing operation
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
                   ` (10 preceding siblings ...)
  2011-07-19 17:17 ` [PATCH 11/18] revert: Save data for continuing after conflict resolution Ramkumar Ramachandra
@ 2011-07-19 17:17 ` Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 13/18] revert: Make pick_commits functionally act on a commit list Ramkumar Ramachandra
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

In the same spirit as ".git/sequencer/head" and ".git/sequencer/todo",
introduce ".git/sequencer/opts" to persist the replay_opts structure
for continuing after a conflict resolution.  Use the gitconfig format
for this file so that it looks like:

[core]
	signoff = true
	record-origin = true
	mainline = 1
	strategy = recursive
	strategy-option = patience
	strategy-option = ours

Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 builtin/revert.c                |   78 +++++++++++++++++++++++++++++++++++++++
 t/t3510-cherry-pick-sequence.sh |   28 +++++++++++++-
 2 files changed, 105 insertions(+), 1 deletions(-)

diff --git a/builtin/revert.c b/builtin/revert.c
index cdfa906..a8ac5a1 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -65,6 +65,7 @@ struct replay_opts {
 #define SEQ_DIR         "sequencer"
 #define SEQ_HEAD_FILE   "sequencer/head"
 #define SEQ_TODO_FILE   "sequencer/todo"
+#define SEQ_OPTS_FILE   "sequencer/opts"
 
 static const char *action_name(const struct replay_opts *opts)
 {
@@ -716,6 +717,49 @@ error:
 	die(_("Unusable instruction sheet: %s"), todo_file);
 }
 
+static int populate_opts_cb(const char *key, const char *value, void *data)
+{
+	struct replay_opts *opts = data;
+	int error_flag = 1;
+
+	if (!value)
+		error_flag = 0;
+	else if (!strcmp(key, "core.no-commit"))
+		opts->no_commit = git_config_bool_or_int(key, value, &error_flag);
+	else if (!strcmp(key, "core.edit"))
+		opts->edit = git_config_bool_or_int(key, value, &error_flag);
+	else if (!strcmp(key, "core.signoff"))
+		opts->signoff = git_config_bool_or_int(key, value, &error_flag);
+	else if (!strcmp(key, "core.record-origin"))
+		opts->record_origin = git_config_bool_or_int(key, value, &error_flag);
+	else if (!strcmp(key, "core.allow-ff"))
+		opts->allow_ff = git_config_bool_or_int(key, value, &error_flag);
+	else if (!strcmp(key, "core.mainline"))
+		opts->mainline = git_config_int(key, value);
+	else if (!strcmp(key, "core.strategy"))
+		git_config_string(&opts->strategy, key, value);
+	else if (!strcmp(key, "core.strategy-option")) {
+		ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
+		opts->xopts[opts->xopts_nr++] = xstrdup(value);
+	} else
+		return error(_("Invalid key: %s"), key);
+
+	if (!error_flag)
+		return error(_("Invalid value for %s: %s"), key, value);
+
+	return 0;
+}
+
+static void MAYBE_UNUSED read_populate_opts(struct replay_opts **opts_ptr)
+{
+	const char *opts_file = git_path(SEQ_OPTS_FILE);
+
+	if (!file_exists(opts_file))
+		return;
+	if (git_config_from_file(populate_opts_cb, opts_file, *opts_ptr) < 0)
+		die(_("Malformed options sheet: %s"), opts_file);
+}
+
 static void walk_revs_populate_todo(struct commit_list **todo_list,
 				struct replay_opts *opts)
 {
@@ -774,6 +818,39 @@ static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)
 	strbuf_release(&buf);
 }
 
+static void save_opts(struct replay_opts *opts)
+{
+	const char *opts_file = git_path(SEQ_OPTS_FILE);
+	struct strbuf buf = STRBUF_INIT;
+	int i;
+
+	if (opts->no_commit)
+		git_config_set_in_file("core.no-commit", "true", opts_file);
+	if (opts->edit)
+		git_config_set_in_file("core.edit", "true", opts_file);
+	if (opts->signoff)
+		git_config_set_in_file("core.signoff", "true", opts_file);
+	if (opts->record_origin)
+		git_config_set_in_file("core.record-origin", "true", opts_file);
+	if (opts->allow_ff)
+		git_config_set_in_file("core.allow-ff", "true", opts_file);
+	if (opts->mainline) {
+		strbuf_reset(&buf);
+		strbuf_addf(&buf, "%d", opts->mainline);
+		git_config_set_in_file("core.mainline", buf.buf, opts_file);
+	}
+	if (opts->strategy)
+		git_config_set_in_file("core.strategy", opts->strategy, opts_file);
+	if (opts->xopts) {
+		for (i = 0; i < opts->xopts_nr; i ++)
+			git_config_set_multivar_in_file("core.strategy-option",
+							opts->xopts[i], "^$", 0,
+							opts_file);
+	}
+
+	strbuf_release(&buf);
+}
+
 static int pick_commits(struct replay_opts *opts)
 {
 	struct commit_list *todo_list = NULL;
@@ -796,6 +873,7 @@ static int pick_commits(struct replay_opts *opts)
 		return error(_("Can't cherry-pick into empty head"));
 	} else
 		save_head(sha1_to_hex(sha1));
+	save_opts(opts);
 	save_todo(todo_list, opts);
 
 	for (cur = todo_list; cur; cur = cur->next) {
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 64eaa20..79d868f 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -32,10 +32,36 @@ test_expect_success setup '
 
 test_expect_success 'cherry-pick persists data on failure' '
 	pristine_detach initial &&
-	test_must_fail git cherry-pick base..anotherpick &&
+	test_must_fail git cherry-pick -s base..anotherpick &&
 	test_path_is_dir .git/sequencer &&
 	test_path_is_file .git/sequencer/head &&
 	test_path_is_file .git/sequencer/todo &&
+	test_path_is_file .git/sequencer/opts &&
+	rm -rf .git/sequencer
+'
+
+test_expect_success 'cherry-pick persists opts correctly' '
+	pristine_detach initial &&
+	test_must_fail git cherry-pick -s -m 1 --strategy=recursive -X patience -X ours base..anotherpick &&
+	test_path_is_dir .git/sequencer &&
+	test_path_is_file .git/sequencer/head &&
+	test_path_is_file .git/sequencer/todo &&
+	test_path_is_file .git/sequencer/opts &&
+	echo "true" >expect
+	git config --file=.git/sequencer/opts --get-all core.signoff >actual &&
+	test_cmp expect actual &&
+	echo "1" >expect
+	git config --file=.git/sequencer/opts --get-all core.mainline >actual &&
+	test_cmp expect actual &&
+	echo "recursive" >expect
+	git config --file=.git/sequencer/opts --get-all core.strategy >actual &&
+	test_cmp expect actual &&
+	cat >expect <<-\EOF
+	patience
+	ours
+	EOF
+	git config --file=.git/sequencer/opts --get-all core.strategy-option >actual &&
+	test_cmp expect actual &&
 	rm -rf .git/sequencer
 '
 
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 13/18] revert: Make pick_commits functionally act on a commit list
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
                   ` (11 preceding siblings ...)
  2011-07-19 17:17 ` [PATCH 12/18] revert: Save command-line options for continuing operation Ramkumar Ramachandra
@ 2011-07-19 17:17 ` Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 14/18] revert: Introduce --reset to remove sequencer state Ramkumar Ramachandra
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

Apart from its central objective of calling into the picking
mechanism, pick_commits creates a sequencer directory, prepares a todo
list, and even acts upon the "--reset" subcommand.  This makes for a
bad API since the central worry of callers is to figure out whether or
not any conflicts were encountered during the cherry picking.  The
current API is like:

if (pick_commits(opts) < 0)
   print "Something failed, we're not sure what"

So, change pick_commits so that it's only responsible for picking
commits in a loop and reporting any errors, leaving the rest to a new
function called pick_revisions.  Consequently, the API of pick_commits
becomes much clearer:

act_on_subcommand(opts->subcommand);
todo_list = prepare_todo_list();
if (pick_commits(todo_list, opts) < 0)
   print "Error encountered while picking commits"

Now, callers can easily call-in to the cherry-picking machinery by
constructing an arbitrary todo list along with some options.

Helped-by: Jonathan Nieder <jrnider@gmail.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 builtin/revert.c |   46 ++++++++++++++++++++++++++++++----------------
 1 files changed, 30 insertions(+), 16 deletions(-)

diff --git a/builtin/revert.c b/builtin/revert.c
index a8ac5a1..b648448 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -851,11 +851,9 @@ static void save_opts(struct replay_opts *opts)
 	strbuf_release(&buf);
 }
 
-static int pick_commits(struct replay_opts *opts)
+static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
 {
-	struct commit_list *todo_list = NULL;
 	struct strbuf buf = STRBUF_INIT;
-	unsigned char sha1[20];
 	struct commit_list *cur;
 	int res;
 
@@ -865,17 +863,6 @@ static int pick_commits(struct replay_opts *opts)
 				opts->record_origin || opts->edit));
 	read_and_refresh_cache(opts);
 
-	walk_revs_populate_todo(&todo_list, opts);
-	create_seq_dir();
-	if (get_sha1("HEAD", sha1)) {
-		if (opts->action == REVERT)
-			return error(_("Can't revert as initial commit"));
-		return error(_("Can't cherry-pick into empty head"));
-	} else
-		save_head(sha1_to_hex(sha1));
-	save_opts(opts);
-	save_todo(todo_list, opts);
-
 	for (cur = todo_list; cur; cur = cur->next) {
 		save_todo(cur, opts);
 		res = do_pick_commit(cur->item, opts);
@@ -892,6 +879,27 @@ static int pick_commits(struct replay_opts *opts)
 	return 0;
 }
 
+static int pick_revisions(struct replay_opts *opts)
+{
+	struct commit_list *todo_list = NULL;
+	unsigned char sha1[20];
+
+	read_and_refresh_cache(opts);
+
+	walk_revs_populate_todo(&todo_list, opts);
+	create_seq_dir();
+	if (get_sha1("HEAD", sha1)) {
+		if (opts->action == REVERT)
+			return error(_("Can't revert as initial commit"));
+		return error(_("Can't cherry-pick into empty head"));
+	} else
+		save_head(sha1_to_hex(sha1));
+	save_opts(opts);
+	save_todo(todo_list, opts);
+
+	return pick_commits(todo_list, opts);
+}
+
 int cmd_revert(int argc, const char **argv, const char *prefix)
 {
 	int res;
@@ -903,7 +911,13 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
 	opts.action = REVERT;
 	git_config(git_default_config, NULL);
 	parse_args(argc, argv, &opts);
-	res = pick_commits(&opts);
+
+	/*
+	 * Decide what to do depending on the arguments; a fresh
+	 * cherry-pick should be handled differently from an existing
+	 * one that is being continued
+	 */
+	res = pick_revisions(&opts);
 	if (res < 0)
 		die(_("revert failed"));
 	return res;
@@ -918,7 +932,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
 	opts.action = CHERRY_PICK;
 	git_config(git_default_config, NULL);
 	parse_args(argc, argv, &opts);
-	res = pick_commits(&opts);
+	res = pick_revisions(&opts);
 	if (res < 0)
 		die(_("cherry-pick failed"));
 	return res;
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 14/18] revert: Introduce --reset to remove sequencer state
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
                   ` (12 preceding siblings ...)
  2011-07-19 17:17 ` [PATCH 13/18] revert: Make pick_commits functionally act on a commit list Ramkumar Ramachandra
@ 2011-07-19 17:17 ` Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 15/18] reset: Make reset remove the " Ramkumar Ramachandra
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

To explicitly remove the sequencer state for a fresh cherry-pick or
revert invocation, introduce a new subcommand called "--reset" to
remove the sequencer state.

Take the opportunity to publicly expose the sequencer paths, and a
generic function called "remove_sequencer_state" that various git
programs can use to remove the sequencer state in a uniform manner;
"git reset" uses it later in this series.  Introducing this public API
is also in line with our long-term goal of eventually factoring out
functions from revert.c into a generic commit sequencer.

Helped-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 Documentation/git-cherry-pick.txt |    5 +++
 Documentation/git-revert.txt      |    5 +++
 Documentation/sequencer.txt       |    4 ++
 Makefile                          |    2 +
 builtin/revert.c                  |   65 +++++++++++++++++++++++++-----------
 sequencer.c                       |   19 +++++++++++
 sequencer.h                       |   20 +++++++++++
 t/t3510-cherry-pick-sequence.sh   |   16 ++++++++-
 8 files changed, 114 insertions(+), 22 deletions(-)
 create mode 100644 Documentation/sequencer.txt
 create mode 100644 sequencer.c
 create mode 100644 sequencer.h

diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index 9d8fe0d..138a292 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -8,6 +8,7 @@ git-cherry-pick - Apply the changes introduced by some existing commits
 SYNOPSIS
 --------
 'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] <commit>...
+'git cherry-pick' --reset
 
 DESCRIPTION
 -----------
@@ -109,6 +110,10 @@ effect to your index in a row.
 	Pass the merge strategy-specific option through to the
 	merge strategy.  See linkgit:git-merge[1] for details.
 
+SEQUENCER SUBCOMMANDS
+---------------------
+include::sequencer.txt[]
+
 EXAMPLES
 --------
 git cherry-pick master::
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index 6a21b37..b6789be 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -8,6 +8,7 @@ git-revert - Revert some existing commits
 SYNOPSIS
 --------
 'git revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>...
+'git revert' --reset
 
 DESCRIPTION
 -----------
@@ -90,6 +91,10 @@ effect to your index in a row.
 	Pass the merge strategy-specific option through to the
 	merge strategy.  See linkgit:git-merge[1] for details.
 
+SEQUENCER SUBCOMMANDS
+---------------------
+include::sequencer.txt[]
+
 EXAMPLES
 --------
 git revert HEAD~3::
diff --git a/Documentation/sequencer.txt b/Documentation/sequencer.txt
new file mode 100644
index 0000000..16ce88c
--- /dev/null
+++ b/Documentation/sequencer.txt
@@ -0,0 +1,4 @@
+--reset::
+	Forget about the current operation in progress.  Can be used
+	to clear the sequencer state after a failed cherry-pick or
+	revert.
diff --git a/Makefile b/Makefile
index 46793d1..5f9bf26 100644
--- a/Makefile
+++ b/Makefile
@@ -551,6 +551,7 @@ LIB_H += rerere.h
 LIB_H += resolve-undo.h
 LIB_H += revision.h
 LIB_H += run-command.h
+LIB_H += sequencer.h
 LIB_H += sha1-array.h
 LIB_H += sha1-lookup.h
 LIB_H += sideband.h
@@ -655,6 +656,7 @@ LIB_OBJS += revision.o
 LIB_OBJS += run-command.o
 LIB_OBJS += server-info.o
 LIB_OBJS += setup.o
+LIB_OBJS += sequencer.o
 LIB_OBJS += sha1-array.o
 LIB_OBJS += sha1-lookup.o
 LIB_OBJS += sha1_file.o
diff --git a/builtin/revert.c b/builtin/revert.c
index b648448..bb82aa3 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -14,6 +14,7 @@
 #include "merge-recursive.h"
 #include "refs.h"
 #include "dir.h"
+#include "sequencer.h"
 
 /*
  * This implements the builtins revert and cherry-pick.
@@ -28,18 +29,22 @@
 
 static const char * const revert_usage[] = {
 	"git revert [options] <commit-ish>",
+	"git revert <subcommand>",
 	NULL
 };
 
 static const char * const cherry_pick_usage[] = {
 	"git cherry-pick [options] <commit-ish>",
+	"git cherry-pick <subcommand>",
 	NULL
 };
 
 enum replay_action { REVERT, CHERRY_PICK };
+enum replay_subcommand { REPLAY_NONE, REPLAY_RESET };
 
 struct replay_opts {
 	enum replay_action action;
+	enum replay_subcommand subcommand;
 
 	/* Boolean options */
 	int edit;
@@ -62,11 +67,6 @@ struct replay_opts {
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 #define MAYBE_UNUSED __attribute__((__unused__))
 
-#define SEQ_DIR         "sequencer"
-#define SEQ_HEAD_FILE   "sequencer/head"
-#define SEQ_TODO_FILE   "sequencer/todo"
-#define SEQ_OPTS_FILE   "sequencer/opts"
-
 static const char *action_name(const struct replay_opts *opts)
 {
 	return opts->action == REVERT ? "revert" : "cherry-pick";
@@ -114,7 +114,9 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
 	const char * const * usage_str = revert_or_cherry_pick_usage(opts);
 	const char *me = action_name(opts);
 	int noop;
+	int reset = 0;
 	struct option options[] = {
+		OPT_BOOLEAN(0, "reset", &reset, "forget 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)",
@@ -143,7 +145,27 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
 	opts->commit_argc = parse_options(argc, argv, NULL, options, usage_str,
 					PARSE_OPT_KEEP_ARGV0 |
 					PARSE_OPT_KEEP_UNKNOWN);
-	if (opts->commit_argc < 2)
+
+	/* Set the subcommand */
+	if (reset)
+		opts->subcommand = REPLAY_RESET;
+	else
+		opts->subcommand = REPLAY_NONE;
+
+	/* Check for incompatible command line arguments */
+	if (opts->subcommand == REPLAY_RESET) {
+		verify_opt_compatible(me, "--reset",
+				"--no-commit", opts->no_commit,
+				"--signoff", opts->signoff,
+				"--mainline", opts->mainline,
+				"--strategy", opts->strategy ? 1 : 0,
+				"--strategy-option", opts->xopts ? 1 : 0,
+				"-x", opts->record_origin,
+				"--ff", opts->allow_ff,
+				NULL);
+	}
+
+	else if (opts->commit_argc < 2)
 		usage_with_options(usage_str, options);
 
 	if (opts->allow_ff)
@@ -853,7 +875,6 @@ static void save_opts(struct replay_opts *opts)
 
 static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
 {
-	struct strbuf buf = STRBUF_INIT;
 	struct commit_list *cur;
 	int res;
 
@@ -874,8 +895,7 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
 	 * Sequence of picks finished successfully; cleanup by
 	 * removing the .git/sequencer directory
 	 */
-	strbuf_addf(&buf, "%s", git_path(SEQ_DIR));
-	remove_dir_recursively(&buf, 0);
+	remove_sequencer_state(1);
 	return 0;
 }
 
@@ -886,17 +906,22 @@ static int pick_revisions(struct replay_opts *opts)
 
 	read_and_refresh_cache(opts);
 
-	walk_revs_populate_todo(&todo_list, opts);
-	create_seq_dir();
-	if (get_sha1("HEAD", sha1)) {
-		if (opts->action == REVERT)
-			return error(_("Can't revert as initial commit"));
-		return error(_("Can't cherry-pick into empty head"));
-	} else
-		save_head(sha1_to_hex(sha1));
-	save_opts(opts);
-	save_todo(todo_list, opts);
-
+	if (opts->subcommand == REPLAY_RESET) {
+		remove_sequencer_state(1);
+		return 0;
+	} else {
+		/* Start a new cherry-pick/ revert sequence */
+		walk_revs_populate_todo(&todo_list, opts);
+		create_seq_dir();
+		if (get_sha1("HEAD", sha1)) {
+			if (opts->action == REVERT)
+				return error(_("Can't revert as initial commit"));
+			return error(_("Can't cherry-pick into empty head"));
+		} else
+			save_head(sha1_to_hex(sha1));
+		save_opts(opts);
+		save_todo(todo_list, opts);
+	}
 	return pick_commits(todo_list, opts);
 }
 
diff --git a/sequencer.c b/sequencer.c
new file mode 100644
index 0000000..bc2c046
--- /dev/null
+++ b/sequencer.c
@@ -0,0 +1,19 @@
+#include "cache.h"
+#include "sequencer.h"
+#include "strbuf.h"
+#include "dir.h"
+
+void remove_sequencer_state(int aggressive)
+{
+	struct strbuf seq_dir = STRBUF_INIT;
+	struct strbuf seq_old_dir = STRBUF_INIT;
+
+	strbuf_addf(&seq_dir, "%s", git_path(SEQ_DIR));
+	strbuf_addf(&seq_old_dir, "%s", git_path(SEQ_OLD_DIR));
+	remove_dir_recursively(&seq_old_dir, 0);
+	rename(git_path(SEQ_DIR), git_path(SEQ_OLD_DIR));
+	if (aggressive)
+		remove_dir_recursively(&seq_old_dir, 0);
+	strbuf_release(&seq_dir);
+	strbuf_release(&seq_old_dir);
+}
diff --git a/sequencer.h b/sequencer.h
new file mode 100644
index 0000000..905d295
--- /dev/null
+++ b/sequencer.h
@@ -0,0 +1,20 @@
+#ifndef SEQUENCER_H
+#define SEQUENCER_H
+
+#define SEQ_DIR		"sequencer"
+#define SEQ_OLD_DIR	"sequencer-old"
+#define SEQ_HEAD_FILE	"sequencer/head"
+#define SEQ_TODO_FILE	"sequencer/todo"
+#define SEQ_OPTS_FILE	"sequencer/opts"
+
+/*
+ * Removes SEQ_OLD_DIR and renames SEQ_DIR to SEQ_OLD_DIR, ignoring
+ * any errors.  Intended to be used by 'git reset'.
+ *
+ * With the aggressive flag, it additionally removes SEQ_OLD_DIR,
+ * ignoring any errors.  Inteded to be used by the sequencer's
+ * '--reset' subcommand.
+ */
+void remove_sequencer_state(int aggressive);
+
+#endif
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 79d868f..aea4f6c 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -37,7 +37,7 @@ test_expect_success 'cherry-pick persists data on failure' '
 	test_path_is_file .git/sequencer/head &&
 	test_path_is_file .git/sequencer/todo &&
 	test_path_is_file .git/sequencer/opts &&
-	rm -rf .git/sequencer
+	git cherry-pick --reset
 '
 
 test_expect_success 'cherry-pick persists opts correctly' '
@@ -62,7 +62,7 @@ test_expect_success 'cherry-pick persists opts correctly' '
 	EOF
 	git config --file=.git/sequencer/opts --get-all core.strategy-option >actual &&
 	test_cmp expect actual &&
-	rm -rf .git/sequencer
+	git cherry-pick --reset
 '
 
 test_expect_success 'cherry-pick cleans up sequencer state upon success' '
@@ -71,4 +71,16 @@ test_expect_success 'cherry-pick cleans up sequencer state upon success' '
 	test_path_is_missing .git/sequencer
 '
 
+test_expect_success '--reset does not complain when no cherry-pick is in progress' '
+	pristine_detach initial &&
+	git cherry-pick --reset
+'
+
+test_expect_success '--reset cleans up sequencer state' '
+	pristine_detach initial &&
+	test_must_fail git cherry-pick base..picked &&
+	git cherry-pick --reset &&
+	test_path_is_missing .git/sequencer
+'
+
 test_done
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 15/18] reset: Make reset remove the sequencer state
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
                   ` (13 preceding siblings ...)
  2011-07-19 17:17 ` [PATCH 14/18] revert: Introduce --reset to remove sequencer state Ramkumar Ramachandra
@ 2011-07-19 17:17 ` Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 16/18] revert: Remove sequencer state when no commits are pending Ramkumar Ramachandra
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

Years of muscle memory have trained users to use "git reset --hard" to
remove the branch state after any sort operation.  Make it also remove
the sequencer state to facilitate this established workflow:

$ git cherry-pick foo..bar
... conflict encountered ...
$ git reset --hard # Oops, I didn't mean that
$ git cherry-pick quux..bar
... cherry-pick succeeded ...

Guard against accidental removal of the sequencer state by providing
one level of "undo".  In the first "reset" invocation,
".git/sequencer" is moved to ".git/sequencer-old"; it is completely
removed only in the second invocation.

Helped-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 branch.c                 |    2 ++
 t/7106-reset-sequence.sh |   43 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 45 insertions(+), 0 deletions(-)
 create mode 100755 t/7106-reset-sequence.sh

diff --git a/branch.c b/branch.c
index c0c865a..d06aec4 100644
--- a/branch.c
+++ b/branch.c
@@ -3,6 +3,7 @@
 #include "refs.h"
 #include "remote.h"
 #include "commit.h"
+#include "sequencer.h"
 
 struct tracking {
 	struct refspec spec;
@@ -228,4 +229,5 @@ void remove_branch_state(void)
 	unlink(git_path("MERGE_MSG"));
 	unlink(git_path("MERGE_MODE"));
 	unlink(git_path("SQUASH_MSG"));
+	remove_sequencer_state(0);
 }
diff --git a/t/7106-reset-sequence.sh b/t/7106-reset-sequence.sh
new file mode 100755
index 0000000..c61c62d
--- /dev/null
+++ b/t/7106-reset-sequence.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+test_description='Test interaction of reset --hard with sequencer
+
+  + anotherpick: rewrites foo to d
+  + picked: rewrites foo to c
+  + unrelatedpick: rewrites unrelated to reallyunrelated
+  + base: rewrites foo to b
+  + initial: writes foo as a, unrelated as unrelated
+'
+
+. ./test-lib.sh
+
+pristine_detach () {
+	git checkout -f "$1^0" &&
+	git read-tree -u --reset HEAD &&
+	git clean -d -f -f -q -x
+}
+
+test_expect_success setup '
+	echo unrelated >unrelated &&
+	git add unrelated &&
+	test_commit initial foo a &&
+	test_commit base foo b &&
+	test_commit unrelatedpick unrelated reallyunrelated &&
+	test_commit picked foo c &&
+	test_commit anotherpick foo d &&
+	git config advice.detachedhead false
+
+'
+
+test_expect_success 'reset --hard cleans up sequencer state, providing one-level undo' '
+	pristine_detach initial &&
+	test_must_fail git cherry-pick base..anotherpick &&
+	test_path_is_dir .git/sequencer &&
+	git reset --hard &&
+	test_path_is_missing .git/sequencer &&
+	test_path_is_dir .git/sequencer-old &&
+	git reset --hard &&
+	test_path_is_missing .git/sequencer-old
+'
+
+test_done
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 16/18] revert: Remove sequencer state when no commits are pending
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
                   ` (14 preceding siblings ...)
  2011-07-19 17:17 ` [PATCH 15/18] reset: Make reset remove the " Ramkumar Ramachandra
@ 2011-07-19 17:17 ` Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 17/18] revert: Don't implictly stomp pending sequencer operation Ramkumar Ramachandra
                   ` (3 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

When cherry-pick or revert is called on a list of commits, and a
conflict encountered somewhere in the middle, the data in
".git/sequencer" is required to continue the operation.  However, when
a conflict is encountered in the very last commit, the user will have
to "continue" after resolving the conflict and committing just so that
the sequencer state is removed.  This is how the current "rebase -i"
script works as well.

$ git cherry-pick foo..bar
... conflict encountered while picking "bar" ...
$ echo "resolved" >problematicfile
$ git add problematicfile
$ git commit
$ git cherry-pick --continue # This would be a noop

Change this so that the sequencer state is cleared when a conflict is
encountered in the last commit.  Incidentally, this patch makes sure
that some existing tests don't break when features like "--reset" and
"--continue" are implemented later in the series.

Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 builtin/revert.c                |   12 +++++++++++-
 t/t3510-cherry-pick-sequence.sh |   24 ++++++++++++++++++++++++
 2 files changed, 35 insertions(+), 1 deletions(-)

diff --git a/builtin/revert.c b/builtin/revert.c
index bb82aa3..f23e9a3 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -887,8 +887,18 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
 	for (cur = todo_list; cur; cur = cur->next) {
 		save_todo(cur, opts);
 		res = do_pick_commit(cur->item, opts);
-		if (res)
+		if (res) {
+			if (!cur->next)
+				/*
+				 * An error was encountered while
+				 * picking the last commit; the
+				 * sequencer state is useless now --
+				 * the user simply needs to resolve
+				 * the conflict and commit
+				 */
+				remove_sequencer_state(0);
 			return res;
+		}
 	}
 
 	/*
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index aea4f6c..428b5bf 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -83,4 +83,28 @@ test_expect_success '--reset cleans up sequencer state' '
 	test_path_is_missing .git/sequencer
 '
 
+test_expect_success 'cherry-pick cleans up sequencer state when one commit is left' '
+	pristine_detach initial &&
+	test_must_fail git cherry-pick base..picked &&
+	test_path_is_missing .git/sequencer &&
+	echo "resolved" >foo &&
+	git add foo &&
+	git commit &&
+	{
+		git rev-list HEAD |
+		git diff-tree --root --stdin |
+		sed "s/$_x40/OBJID/g"
+	} >actual &&
+	cat >expect <<-\EOF &&
+	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_done
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 17/18] revert: Don't implictly stomp pending sequencer operation
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
                   ` (15 preceding siblings ...)
  2011-07-19 17:17 ` [PATCH 16/18] revert: Remove sequencer state when no commits are pending Ramkumar Ramachandra
@ 2011-07-19 17:17 ` Ramkumar Ramachandra
  2011-07-19 17:17 ` [PATCH 18/18] revert: Introduce --continue to continue the operation Ramkumar Ramachandra
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

Protect the user from forgetting about a pending sequencer operation
by immediately erroring out when an existing cherry-pick or revert
operation is in progress like:

$ git cherry-pick foo
... conflict ...
$ git cherry-pick moo
error: .git/sequencer already exists
hint: A cherry-pick or revert is in progress
hint: Use --reset to forget about it
fatal: cherry-pick failed

A naive version of this would break the following established ways of
working:

$ git cherry-pick foo
... conflict ...
$ git reset --hard  # I actually meant "moo" when I said "foo"
$ git cherry-pick moo

$ git cherry-pick foo
... conflict ...
$ git commit # commit the resolution
$ git cherry-pick moo # New operation

However, the previous patches "reset: Make reset remove the sequencer
state" and "revert: Remove sequencer state when no commits are
pending" make sure that this does not happen.

Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 builtin/revert.c                |   21 +++++++++++++++++----
 t/t3510-cherry-pick-sequence.sh |   10 ++++++++++
 2 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/builtin/revert.c b/builtin/revert.c
index f23e9a3..82a919a 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -796,12 +796,15 @@ static void walk_revs_populate_todo(struct commit_list **todo_list,
 		next = commit_list_append(commit, next);
 }
 
-static void create_seq_dir(void)
+static int create_seq_dir(void)
 {
 	const char *seq_dir = git_path(SEQ_DIR);
 
-	if (!file_exists(seq_dir) && mkdir(seq_dir, 0777) < 0)
+	if (file_exists(seq_dir))
+		return error(_("%s already exists."), seq_dir);
+	else if (mkdir(seq_dir, 0777) < 0)
 		die_errno(_("Could not create sequencer directory '%s'."), seq_dir);
+	return 0;
 }
 
 static void save_head(const char *head)
@@ -912,6 +915,7 @@ static int pick_commits(struct commit_list *todo_list, struct replay_opts *opts)
 static int pick_revisions(struct replay_opts *opts)
 {
 	struct commit_list *todo_list = NULL;
+	const char *me = action_name(opts);
 	unsigned char sha1[20];
 
 	read_and_refresh_cache(opts);
@@ -920,9 +924,18 @@ static int pick_revisions(struct replay_opts *opts)
 		remove_sequencer_state(1);
 		return 0;
 	} else {
-		/* Start a new cherry-pick/ revert sequence */
+		/*
+		 * Start a new cherry-pick/ revert sequence; but
+		 * first, make sure that an existing one isn't in
+		 * progress
+		 */
+
 		walk_revs_populate_todo(&todo_list, opts);
-		create_seq_dir();
+		if (create_seq_dir() < 0) {
+			advise(_("A cherry-pick or revert is in progress."));
+			advise(_("Use --reset to forget about it"));
+			return -1;
+		}
 		if (get_sha1("HEAD", sha1)) {
 			if (opts->action == REVERT)
 				return error(_("Can't revert as initial commit"));
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 428b5bf..44277f5 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -107,4 +107,14 @@ test_expect_success 'cherry-pick cleans up sequencer state when one commit is le
 	test_cmp expect actual
 '
 
+test_expect_success 'cherry-pick does not implicitly stomp an existing operation' '
+	pristine_detach initial &&
+	test_must_fail git cherry-pick base..anotherpick &&
+	test-chmtime -v +0 .git/sequencer >expect &&
+	test_must_fail git cherry-pick unrelatedpick &&
+	test-chmtime -v +0 .git/sequencer >actual &&
+	test_cmp expect actual &&
+	git cherry-pick --reset
+'
+
 test_done
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 18/18] revert: Introduce --continue to continue the operation
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
                   ` (16 preceding siblings ...)
  2011-07-19 17:17 ` [PATCH 17/18] revert: Don't implictly stomp pending sequencer operation Ramkumar Ramachandra
@ 2011-07-19 17:17 ` Ramkumar Ramachandra
  2011-07-19 18:47 ` [GSoC update] Sequencer for inclusion v2 Junio C Hamano
  2011-07-19 23:54 ` Junio C Hamano
  19 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-19 17:17 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

Introduce a new "git cherry-pick --continue" command which uses the
information in ".git/sequencer" to continue a cherry-pick that stopped
because of a conflict or other error.  It works by dropping the first
instruction from .git/sequencer/todo and performing the remaining
cherry-picks listed there, with options (think "-s" and "-X") from the
initial command listed in ".git/sequencer/opts".

So now you can do:

$ git cherry-pick -Xpatience foo..bar
... description conflict in commit moo ...
$ git cherry-pick --continue
error: 'cherry-pick' is not possible because you have unmerged files.
fatal: failed to resume cherry-pick
$ echo resolved >conflictingfile
$ git add conflictingfile && git commit
$ git cherry-pick --continue; # resumes with the commit after "moo"

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.

Helped-by: Jonathan Nieder <jrnieder@gmail.com>
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                  |   67 ++++++++++++++++++++++---
 t/t3510-cherry-pick-sequence.sh   |   99 +++++++++++++++++++++++++++++++++++++
 5 files changed, 165 insertions(+), 8 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 b6789be..923ae51 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 82a919a..6b5869e 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -40,7 +40,7 @@ static const char * const cherry_pick_usage[] = {
 };
 
 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;
@@ -65,7 +65,6 @@ struct replay_opts {
 };
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
-#define MAYBE_UNUSED __attribute__((__unused__))
 
 static const char *action_name(const struct replay_opts *opts)
 {
@@ -109,14 +108,40 @@ 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);
 	const char *me = action_name(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)",
@@ -146,15 +171,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,
@@ -702,8 +741,8 @@ static struct commit *parse_insn_line(char *start, struct replay_opts *opts)
 	return commit;
 }
 
-static void MAYBE_UNUSED read_populate_todo(struct commit_list **todo_list,
-					struct replay_opts *opts)
+static void read_populate_todo(struct commit_list **todo_list,
+			struct replay_opts *opts)
 {
 	const char *todo_file = git_path(SEQ_TODO_FILE);
 	struct strbuf buf = STRBUF_INIT;
@@ -772,7 +811,7 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
 	return 0;
 }
 
-static void MAYBE_UNUSED read_populate_opts(struct replay_opts **opts_ptr)
+static void read_populate_opts(struct replay_opts **opts_ptr)
 {
 	const char *opts_file = git_path(SEQ_OPTS_FILE);
 
@@ -923,6 +962,15 @@ static int pick_revisions(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
@@ -933,7 +981,8 @@ static int pick_revisions(struct replay_opts *opts)
 		walk_revs_populate_todo(&todo_list, opts);
 		if (create_seq_dir() < 0) {
 			advise(_("A cherry-pick or revert is in progress."));
-			advise(_("Use --reset to forget about it"));
+			advise(_("Use --continue to continue the operation"));
+			advise(_("or --reset to forget about it"));
 			return -1;
 		}
 		if (get_sha1("HEAD", sha1)) {
@@ -946,6 +995,8 @@ static int pick_revisions(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 44277f5..0267714 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -117,4 +117,103 @@ test_expect_success 'cherry-pick does not implicitly stomp an existing operation
 	git cherry-pick --reset
 '
 
+test_expect_success '--continue complains when no cherry-pick is in progress' '
+	pristine_detach initial &&
+	test_must_fail git cherry-pick --continue
+'
+
+test_expect_success '--continue complains when there are unresolved conflicts' '
+	pristine_detach initial &&
+	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 &&
+	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/$_x40/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 &&
+	test_must_fail git cherry-pick -x base..anotherpick &&
+	echo "c" >foo &&
+	git add foo &&
+	git commit &&
+	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 grep "cherry picked from" initial_msg &&
+	grep "cherry picked from" unrelatedpick_msg &&
+	grep "cherry picked from" picked_msg &&
+	grep "cherry picked from" anotherpick_msg
+'
+
+test_expect_success '--signoff is not automatically propogated to resolved conflict' '
+	pristine_detach initial &&
+	test_must_fail git cherry-pick --signoff base..anotherpick &&
+	echo "c" >foo &&
+	git add foo &&
+	git commit &&
+	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 grep "Signed-off-by:" initial_msg &&
+	grep "Signed-off-by:" unrelatedpick_msg &&
+	test_must_fail grep "Signed-off-by:" picked_msg &&
+	grep "Signed-off-by:" anotherpick_msg
+'
+
+test_expect_success 'malformed instruction sheet 1' '
+	pristine_detach initial &&
+	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 &&
+	git cherry-pick --reset
+'
+
+test_expect_success 'malformed instruction sheet 2' '
+	pristine_detach initial &&
+	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 &&
+	git cherry-pick --reset
+'
+
 test_done
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* Re: [GSoC update] Sequencer for inclusion v2
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
                   ` (17 preceding siblings ...)
  2011-07-19 17:17 ` [PATCH 18/18] revert: Introduce --continue to continue the operation Ramkumar Ramachandra
@ 2011-07-19 18:47 ` Junio C Hamano
  2011-07-20  9:02   ` Ramkumar Ramachandra
  2011-07-19 23:54 ` Junio C Hamano
  19 siblings, 1 reply; 38+ messages in thread
From: Junio C Hamano @ 2011-07-19 18:47 UTC (permalink / raw)
  To: Ramkumar Ramachandra
  Cc: Git List, Jonathan Nieder, Christian Couder, Daniel Barkalow, Jeff King

Ramkumar Ramachandra <artagnon@gmail.com> writes:

>>> +                     if (!cur->next)
>>> +                             /*
>>> +                              * An error was encountered while
>>> +                              * picking the last commit; the
>>> +                              * sequencer state is useless now --
>>> +                              * the user simply needs to resolve
>>> +                              * the conflict and commit
>>> +                              */
>>> +                             remove_sequencer_state();
>>>                       return res;
>>> +             }
>>>       }
>>
>> It may be useless for --continue, but wouldn't --abort need some clue on
>> what you were doing?
> ...
> Conclusion: Making "git commit" remove the sequencer state is WRONG.

Why not choose to not to clean it at all?  Then "rebase --continue" and
its equivalent to cherry-pick, rebase and any sequence command) can (and
have to anyway) notice that there is nothing more to do, remove the state
directory and state "there is nothing more to do".

You could make it even easier to use for people by tweaking "a sequence
state directory for an operation you earlier started still exists" logic
to see if everything is done, but I would say that is icing on the cake.

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

* Re: [PATCH 02/18] config: Introduce functions to write non-standard file
  2011-07-19 17:17 ` [PATCH 02/18] config: Introduce functions to write non-standard file Ramkumar Ramachandra
@ 2011-07-19 19:55   ` Jeff King
  2011-07-19 20:35     ` Jonathan Nieder
  2011-07-19 21:26     ` Junio C Hamano
  0 siblings, 2 replies; 38+ messages in thread
From: Jeff King @ 2011-07-19 19:55 UTC (permalink / raw)
  To: Ramkumar Ramachandra
  Cc: Git List, Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow

On Tue, Jul 19, 2011 at 10:47:40PM +0530, Ramkumar Ramachandra wrote:

> -int git_config_set_multivar(const char *key, const char *value,
> -	const char *value_regex, int multi_replace)
> +int git_config_set_multivar_in_file(const char *key, const char *value,
> +				const char *value_regex, int multi_replace,
> +				const char *filename)
>  {
>  	int fd = -1, in_fd;
>  	int ret;
>  	char *config_filename;
> +	const char *saved_config_filename;
>  	struct lock_file *lock = NULL;
>  
> +	saved_config_filename = config_exclusive_filename;
> +	config_exclusive_filename = filename;
> +
>  	if (config_exclusive_filename)
>  		config_filename = xstrdup(config_exclusive_filename);
>  	else
> @@ -1379,6 +1390,7 @@ out_free:
>  	if (lock)
>  		rollback_lock_file(lock);
>  	free(config_filename);
> +	config_exclusive_filename = saved_config_filename;
>  	return ret;
>  
>  write_err_out:

Is there a need for this "config_exclusive_filename" hackery at all?

I was thinking the result would look more like:

diff --git a/config.c b/config.c
index ee643eb..35be842 100644
--- a/config.c
+++ b/config.c
@@ -1193,19 +1193,14 @@ out_free_ret_1:
  * - the config file is removed and the lock file rename()d to it.
  *
  */
-int git_config_set_multivar(const char *key, const char *value,
+int git_config_set_multivar_in_file(const char *config_filename,
+	const char *key, const char *value,
 	const char *value_regex, int multi_replace)
 {
 	int fd = -1, in_fd;
 	int ret;
-	char *config_filename;
 	struct lock_file *lock = NULL;
 
-	if (config_exclusive_filename)
-		config_filename = xstrdup(config_exclusive_filename);
-	else
-		config_filename = git_pathdup("config");
-
 	/* parse-key returns negative; flip the sign to feed exit(3) */
 	ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
 	if (ret)
@@ -1382,7 +1377,6 @@ int git_config_set_multivar(const char *key, const char *value,
 out_free:
 	if (lock)
 		rollback_lock_file(lock);
-	free(config_filename);
 	return ret;
 
 write_err_out:
@@ -1391,6 +1385,24 @@ write_err_out:
 
 }
 
+int git_config_set_multivar(const char *key, const char *value,
+			    const char *value_regex, int multi_replace)
+{
+	char *config_filename;
+	int r;
+
+	if (config_exclusive_filename)
+		config_filename = xstrdup(config_exclusive_filename);
+	else
+		config_filename = git_pathdup("config");
+
+	r = git_config_set_multivar_in_file(config_filename, key, value,
+					    value_regex, multi_replace);
+
+	free(config_filename);
+	return r;
+}
+
 static int section_name_match (const char *buf, const char *name)
 {
 	int i = 0, j = 0, dot = 0;

Which is a bit cleaner to read, IMHO.

BTW, I'm unclear why we bother duplicating the filename in the first
place. It seems like we could go even simpler with:

int git_config_set_multivar(const char *key, const char *value,
                            const char *value_regex, int multi_replace)
{
	char *config_filename;

	if (config_exclusive_filename)
		config_filename = config_exclusive_filename;
	else
		config_filename = git_path("config");

        return git_config_set_multivar_in_file(config_filename, key, value,
                                               value_regex, multi_replace);
}

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

* Re: [PATCH 02/18] config: Introduce functions to write non-standard file
  2011-07-19 19:55   ` Jeff King
@ 2011-07-19 20:35     ` Jonathan Nieder
  2011-07-19 21:26     ` Junio C Hamano
  1 sibling, 0 replies; 38+ messages in thread
From: Jonathan Nieder @ 2011-07-19 20:35 UTC (permalink / raw)
  To: Jeff King
  Cc: Ramkumar Ramachandra, Git List, Junio C Hamano, Christian Couder,
	Daniel Barkalow

Jeff King wrote:

> BTW, I'm unclear why we bother duplicating the filename in the first
> place. It seems like we could go even simpler with:
>
> int git_config_set_multivar(const char *key, const char *value,
>                             const char *value_regex, int multi_replace)
> {
> 	char *config_filename;
>
> 	if (config_exclusive_filename)
> 		config_filename = config_exclusive_filename;
> 	else
> 		config_filename = git_path("config");
>
>         return git_config_set_multivar_in_file(config_filename, key, value,
>                                                value_regex, multi_replace);

FWIW, I suspect that would be a trap waiting to be triggered as soon
as git_config_set_multivar_in_file() calls git_path() four times.
I.e., it's not about the config_exclusive_filename but about the
git_path.

A little micro-optimization might be to do the following. :)

	const char *config_filename;
	char *filename_buf = NULL;
	int result;

	if (config_exclusive_filename)
		config_filename = config_exclusive_filename;
	else
		config_filename = filename_buf = git_pathdup("config");

	result = git_config_set_multivar_in_file(...);
	free(filename_buf);
	return result;

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

* Re: [PATCH 02/18] config: Introduce functions to write non-standard file
  2011-07-19 19:55   ` Jeff King
  2011-07-19 20:35     ` Jonathan Nieder
@ 2011-07-19 21:26     ` Junio C Hamano
  2011-07-19 21:57       ` Jeff King
  1 sibling, 1 reply; 38+ messages in thread
From: Junio C Hamano @ 2011-07-19 21:26 UTC (permalink / raw)
  To: Jeff King
  Cc: Ramkumar Ramachandra, Git List, Jonathan Nieder,
	Christian Couder, Daniel Barkalow

Jeff King <peff@peff.net> writes:

> Is there a need for this "config_exclusive_filename" hackery at all?
>
> I was thinking the result would look more like:
> ...

Exactly my thought. I think I suggested the same to Ramkumar earlier and
he said he will re-roll but perhaps this is a series before that exchange.

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

* Re: [PATCH 02/18] config: Introduce functions to write non-standard file
  2011-07-19 21:26     ` Junio C Hamano
@ 2011-07-19 21:57       ` Jeff King
  2011-07-24 10:17         ` Ramkumar Ramachandra
  0 siblings, 1 reply; 38+ messages in thread
From: Jeff King @ 2011-07-19 21:57 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Ramkumar Ramachandra, Git List, Jonathan Nieder,
	Christian Couder, Daniel Barkalow

On Tue, Jul 19, 2011 at 02:26:13PM -0700, Junio C Hamano wrote:

> Jeff King <peff@peff.net> writes:
> 
> > Is there a need for this "config_exclusive_filename" hackery at all?
> >
> > I was thinking the result would look more like:
> > ...
> 
> Exactly my thought. I think I suggested the same to Ramkumar earlier and
> he said he will re-roll but perhaps this is a series before that exchange.

This one is incrementally better than the initial one, but I still think
it doesn't go far enough.

-Peff

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

* Re: [GSoC update] Sequencer for inclusion v2
  2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
                   ` (18 preceding siblings ...)
  2011-07-19 18:47 ` [GSoC update] Sequencer for inclusion v2 Junio C Hamano
@ 2011-07-19 23:54 ` Junio C Hamano
  2011-07-24 10:11   ` Ramkumar Ramachandra
  19 siblings, 1 reply; 38+ messages in thread
From: Junio C Hamano @ 2011-07-19 23:54 UTC (permalink / raw)
  To: Ramkumar Ramachandra
  Cc: Git List, Jonathan Nieder, Christian Couder, Daniel Barkalow, Jeff King

Ramkumar Ramachandra <artagnon@gmail.com> writes:

> Mid-term is over, and I'm disappointed that I'm behind schedule -- I
> haven't been able to get the Sequencer merged yet.  The reviews have
> been incessant as ever, and I spend most of my time chipping
> everything to prefection.  On a positive note, the series looks like a
> beautiful work of art now; I never imagined that it could be this
> beautiful :)

Applying these to the same base commit as the previous series was queued
and comparing the result does show this round is indeed nicer. Haven't
started nitpicking the log messages, though ;-)

Thanks.

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

* Re: [GSoC update] Sequencer for inclusion v2
  2011-07-19 18:47 ` [GSoC update] Sequencer for inclusion v2 Junio C Hamano
@ 2011-07-20  9:02   ` Ramkumar Ramachandra
  0 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-20  9:02 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git List, Jonathan Nieder, Christian Couder, Daniel Barkalow, Jeff King

Hi Junio,

Junio C Hamano writes:
> Ramkumar Ramachandra <artagnon@gmail.com> writes:
>>>> +                     if (!cur->next)
>>>> +                             /*
>>>> +                              * An error was encountered while
>>>> +                              * picking the last commit; the
>>>> +                              * sequencer state is useless now --
>>>> +                              * the user simply needs to resolve
>>>> +                              * the conflict and commit
>>>> +                              */
>>>> +                             remove_sequencer_state();
>>>>                       return res;
>>>> +             }
>>>>       }
>>>
>>> It may be useless for --continue, but wouldn't --abort need some clue on
>>> what you were doing?
>> ...
>> Conclusion: Making "git commit" remove the sequencer state is WRONG.
>
> Why not choose to not to clean it at all?  Then "rebase --continue" and
> its equivalent to cherry-pick, rebase and any sequence command) can (and
> have to anyway) notice that there is nothing more to do, remove the state
> directory and state "there is nothing more to do".

1. Without this patch, some existing tests break.
2. There is nothing obviously wrong with the patch, and it even
enhances user experience.  Have we paid a heavy price for not breaking
existing tests?

I could drop the patch and fix all the tests, but is that what we'd like to see?

-- Ram

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

* Re: [PATCH 01/18] advice: Introduce error_resolve_conflict
  2011-07-19 17:17 ` [PATCH 01/18] advice: Introduce error_resolve_conflict Ramkumar Ramachandra
@ 2011-07-21 15:35   ` Phil Hord
  2011-07-22 22:35     ` Jeff King
  0 siblings, 1 reply; 38+ messages in thread
From: Phil Hord @ 2011-07-21 15:35 UTC (permalink / raw)
  To: Ramkumar Ramachandra
  Cc: Git List, Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

On 07/19/2011 01:17 PM, Ramkumar Ramachandra wrote:
> Enable future callers to report a conflict and not die immediately by
> introducing a new function called error_resolve_conflict.
> Re-implement die_resolve_conflict as a call to error_resolve_conflict
> followed by a call to die.  Consequently, the message printed by
> die_resolve_conflict changes from
>
> fatal: 'commit' is not possible because you have unmerged files.
>         Please, fix them up in the work tree ...
>         ...
>
> to
>
> error: 'commit' is not possible because you have unmerged files.
> hint: Please, fix them up in the work tree ...
> hint: ...
> fatal: Exiting because of an unresolved conflict.

This is a tiny grammar nit, but "Please," sounds superfluous now that 
it's preceded by "hint:".  "Hint" sounds like I'm doing you a favor by 
telling you something to do.  "Please" sounds like you're doing me a 
favor by doing something.  Together they just sound like a typo.

In either case, the comma after "Please" is wrong.  (There are other 
messages in the git code which make this same mistake, but there are 
more which use "Please <imperative-verb>..." correctly.)

Note: I see this repeated in patch 06/18 in this series, and there are 
probably others.

Phil

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

* Re: [PATCH 01/18] advice: Introduce error_resolve_conflict
  2011-07-21 15:35   ` Phil Hord
@ 2011-07-22 22:35     ` Jeff King
  2011-07-24 10:23       ` Ramkumar Ramachandra
  2011-07-24 18:15       ` Junio C Hamano
  0 siblings, 2 replies; 38+ messages in thread
From: Jeff King @ 2011-07-22 22:35 UTC (permalink / raw)
  To: Phil Hord
  Cc: Ramkumar Ramachandra, Git List, Jonathan Nieder, Junio C Hamano,
	Christian Couder, Daniel Barkalow

On Thu, Jul 21, 2011 at 11:35:44AM -0400, Phil Hord wrote:

> >error: 'commit' is not possible because you have unmerged files.
> >hint: Please, fix them up in the work tree ...
> >hint: ...
> >fatal: Exiting because of an unresolved conflict.
> 
> This is a tiny grammar nit, but "Please," sounds superfluous now that
> it's preceded by "hint:".  "Hint" sounds like I'm doing you a favor
> by telling you something to do.  "Please" sounds like you're doing me
> a favor by doing something.  Together they just sound like a typo.

Agreed.

> In either case, the comma after "Please" is wrong.  (There are other
> messages in the git code which make this same mistake, but there are
> more which use "Please <imperative-verb>..." correctly.)

I don't know that I'd go so far as to say it's wrong, exactly. Clearly
by traditional rules of written grammar it is, but I think the attempt
was to convey the pause that one might include if one were speaking the
sentence.

Still, I think I find it more readable without the comma, and better
still if every spot were converted to "hint: ". This question has come
up once or twice before, too, so I don't know that a patch removing the
commas would be out of line.

-Peff

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

* Re: [GSoC update] Sequencer for inclusion v2
  2011-07-19 23:54 ` Junio C Hamano
@ 2011-07-24 10:11   ` Ramkumar Ramachandra
  2011-07-24 18:17     ` Junio C Hamano
  0 siblings, 1 reply; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-24 10:11 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git List, Jonathan Nieder, Christian Couder, Daniel Barkalow, Jeff King

Hi Junio et al,

Junio C Hamano writes:
> Ramkumar Ramachandra <artagnon@gmail.com> writes:
>> Mid-term is over, and I'm disappointed that I'm behind schedule -- I
>> haven't been able to get the Sequencer merged yet.  The reviews have
>> been incessant as ever, and I spend most of my time chipping
>> everything to prefection.  On a positive note, the series looks like a
>> beautiful work of art now; I never imagined that it could be this
>> beautiful :)
>
> Applying these to the same base commit as the previous series was queued
> and comparing the result does show this round is indeed nicer. Haven't
> started nitpicking the log messages, though ;-)

Apart from the config.c patch, and the "Please, " nit (both of which
I've corrected now), this series seems to be more-or-less ready.  I'll
re-post this series with these two details corrected for your
convenience shortly.  I've started building my post mid-term work on
top of it -- if some other minor details come up before it gets
merged, I can rebase quickly and continue.

Thanks.

-- Ram

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

* Re: [PATCH 02/18] config: Introduce functions to write non-standard file
  2011-07-19 21:57       ` Jeff King
@ 2011-07-24 10:17         ` Ramkumar Ramachandra
  0 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-24 10:17 UTC (permalink / raw)
  To: Jeff King
  Cc: Junio C Hamano, Git List, Jonathan Nieder, Christian Couder,
	Daniel Barkalow

Hi Jeff and others,

Jeff King writes:
> This one is incrementally better than the initial one, but I still think
> it doesn't go far enough.

Thanks for showing me how to do this correctly.
I was waiting for reviews on other patches before re-rolling, but
there don't seem to be any more -- I'll send out a new
"sequencer-stable" series with this change and the "Please, " nit
corrected shortly; it's what I'm basing my post mid-term work on.

-- Ram

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

* Re: [PATCH 01/18] advice: Introduce error_resolve_conflict
  2011-07-22 22:35     ` Jeff King
@ 2011-07-24 10:23       ` Ramkumar Ramachandra
  2011-07-24 18:15       ` Junio C Hamano
  1 sibling, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-24 10:23 UTC (permalink / raw)
  To: Jeff King
  Cc: Phil Hord, Git List, Jonathan Nieder, Junio C Hamano,
	Christian Couder, Daniel Barkalow

Hi Phil and Jeff,

Jeff King writes:
> On Thu, Jul 21, 2011 at 11:35:44AM -0400, Phil Hord wrote:
>> In either case, the comma after "Please" is wrong.  (There are other
>> messages in the git code which make this same mistake, but there are
>> more which use "Please <imperative-verb>..." correctly.)
>
> Still, I think I find it more readable without the comma, and better
> still if every spot were converted to "hint: ". This question has come
> up once or twice before, too, so I don't know that a patch removing the
> commas would be out of line.

Ah, I was wondering about this too.  Even if a patch that goes around
removing commas or "Please, " in the entire codebase isn't desirable,
I can make the change in this series.

Thanks.

-- Ram

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

* Re: [PATCH 01/18] advice: Introduce error_resolve_conflict
  2011-07-22 22:35     ` Jeff King
  2011-07-24 10:23       ` Ramkumar Ramachandra
@ 2011-07-24 18:15       ` Junio C Hamano
  2011-07-25 21:17         ` Phil Hord
  1 sibling, 1 reply; 38+ messages in thread
From: Junio C Hamano @ 2011-07-24 18:15 UTC (permalink / raw)
  To: Jeff King
  Cc: Phil Hord, Ramkumar Ramachandra, Git List, Jonathan Nieder,
	Junio C Hamano, Christian Couder, Daniel Barkalow

Jeff King <peff@peff.net> writes:

> Still, I think I find it more readable without the comma, and better
> still if every spot were converted to "hint: ". This question has come
> up once or twice before, too, so I don't know that a patch removing the
> commas would be out of line.

Good to see that native speakers seem to think the comma there is funny.

I recall I've complained to one patch that added "Please, ", suggested to
remove that whole "Please, " thing, got ignored, but chose not to press it
further.

Thanks.

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

* Re: [GSoC update] Sequencer for inclusion v2
  2011-07-24 10:11   ` Ramkumar Ramachandra
@ 2011-07-24 18:17     ` Junio C Hamano
  0 siblings, 0 replies; 38+ messages in thread
From: Junio C Hamano @ 2011-07-24 18:17 UTC (permalink / raw)
  To: Ramkumar Ramachandra
  Cc: Junio C Hamano, Git List, Jonathan Nieder, Christian Couder,
	Daniel Barkalow, Jeff King

Ramkumar Ramachandra <artagnon@gmail.com> writes:

> ... convenience shortly.  I've started building my post mid-term work on
> top of it -- if some other minor details come up before it gets
> merged, I can rebase quickly and continue.

Thanks, both for a re-roll and for a heads-up.

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

* Re: [PATCH 01/18] advice: Introduce error_resolve_conflict
  2011-07-24 18:15       ` Junio C Hamano
@ 2011-07-25 21:17         ` Phil Hord
  0 siblings, 0 replies; 38+ messages in thread
From: Phil Hord @ 2011-07-25 21:17 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Jeff King, Ramkumar Ramachandra, Git List, Jonathan Nieder,
	Christian Couder, Daniel Barkalow

On 07/24/2011 02:15 PM, Junio C Hamano wrote:
> Jeff King <peff@peff.net> writes:
>
>> Still, I think I find it more readable without the comma, and better
>> still if every spot were converted to "hint: ". This question has come
>> up once or twice before, too, so I don't know that a patch removing the
>> commas would be out of line.
> Good to see that native speakers seem to think the comma there is funny.
I do, but I spent a fair amount of time finding out why before
complaining.  Wanted to make sure it wasn't a matter of taste.

"Please" is an adverb.  It modifies a verb or another adverb.  A comma
is used when the adverb is not adjacent to the target verb or adverb,
and in a few other odd conditions, none of which are met here.

"Please fix them up in the work tree."
"Fix them up in the work tree, please."
"Please, before committing these files, fix them up in the work tree."

Phil

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

* [PATCH 01/18] advice: Introduce error_resolve_conflict
  2011-08-04 10:38 [PATCH 00/18] Sequencer for inclusion v6 Ramkumar Ramachandra
@ 2011-08-04 10:38 ` Ramkumar Ramachandra
  0 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-08-04 10:38 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git List, Jonathan Nieder, Christian Couder, Daniel Barkalow, Jeff King

Enable future callers to report a conflict and not die immediately by
introducing a new function called error_resolve_conflict.
Re-implement die_resolve_conflict as a call to error_resolve_conflict
followed by a call to die.  Consequently, the message printed by
die_resolve_conflict changes from

  fatal: 'commit' is not possible because you have unmerged files.
         Please, fix them up in the work tree ...
         ...

to

  error: 'commit' is not possible because you have unmerged files.
  hint: Fix them up in the work tree ...
  hint: ...
  fatal: Exiting because of an unresolved conflict.

Hints are printed using the same advise function introduced in
v1.7.3-rc0~26^2~3 (Introduce advise() to print hints, 2010-08-11).

Inspired-by: Christian Couder <chistian.couder@gmail.com>
Helped-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 advice.c         |   31 ++++++++++++++++++++++++-------
 advice.h         |    3 ++-
 builtin/revert.c |    9 ---------
 3 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/advice.c b/advice.c
index 0be4b5f..e02e632 100644
--- a/advice.c
+++ b/advice.c
@@ -19,6 +19,15 @@ static struct {
 	{ "detachedhead", &advice_detached_head },
 };
 
+void advise(const char *advice, ...)
+{
+	va_list params;
+
+	va_start(params, advice);
+	vreportf("hint: ", advice, params);
+	va_end(params);
+}
+
 int git_default_advice_config(const char *var, const char *value)
 {
 	const char *k = skip_prefix(var, "advice.");
@@ -34,16 +43,24 @@ int git_default_advice_config(const char *var, const char *value)
 	return 0;
 }
 
-void NORETURN die_resolve_conflict(const char *me)
+int error_resolve_conflict(const char *me)
 {
-	if (advice_resolve_conflict)
+	error("'%s' is not possible because you have unmerged files.", me);
+	if (advice_resolve_conflict) {
 		/*
 		 * Message used both when 'git commit' fails and when
 		 * other commands doing a merge do.
 		 */
-		die("'%s' is not possible because you have unmerged files.\n"
-		    "Please, fix them up in the work tree, and then use 'git add/rm <file>' as\n"
-		    "appropriate to mark resolution and make a commit, or use 'git commit -a'.", me);
-	else
-		die("'%s' is not possible because you have unmerged files.", me);
+		advise("Fix them up in the work tree,");
+		advise("and then use 'git add/rm <file>' as");
+		advise("appropriate to mark resolution and make a commit,");
+		advise("or use 'git commit -a'.");
+	}
+	return -1;
+}
+
+void NORETURN die_resolve_conflict(const char *me)
+{
+	error_resolve_conflict(me);
+	die("Exiting because of an unresolved conflict.");
 }
diff --git a/advice.h b/advice.h
index 3244ebb..e5d0af7 100644
--- a/advice.h
+++ b/advice.h
@@ -11,7 +11,8 @@ extern int advice_implicit_identity;
 extern int advice_detached_head;
 
 int git_default_advice_config(const char *var, const char *value);
-
+void advise(const char *advice, ...);
+int error_resolve_conflict(const char *me);
 extern void NORETURN die_resolve_conflict(const char *me);
 
 #endif /* ADVICE_H */
diff --git a/builtin/revert.c b/builtin/revert.c
index 1f27c63..2df3f3b 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -214,15 +214,6 @@ static void write_cherry_pick_head(void)
 	strbuf_release(&buf);
 }
 
-static void advise(const char *advice, ...)
-{
-	va_list params;
-
-	va_start(params, advice);
-	vreportf("hint: ", advice, params);
-	va_end(params);
-}
-
 static void print_advice(void)
 {
 	char *msg = getenv("GIT_CHERRY_PICK_HELP");
-- 
1.7.6.351.gb35ac.dirty

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

* [PATCH 01/18] advice: Introduce error_resolve_conflict
  2011-08-01 18:06 [PATCH v5 00/18] Sequencer for inclusion Ramkumar Ramachandra
@ 2011-08-01 18:06 ` Ramkumar Ramachandra
  0 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-08-01 18:06 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git List, Jonathan Nieder, Christian Couder, Daniel Barkalow, Jeff King

Enable future callers to report a conflict and not die immediately by
introducing a new function called error_resolve_conflict.
Re-implement die_resolve_conflict as a call to error_resolve_conflict
followed by a call to die.  Consequently, the message printed by
die_resolve_conflict changes from

  fatal: 'commit' is not possible because you have unmerged files.
         Please, fix them up in the work tree ...
         ...

to

  error: 'commit' is not possible because you have unmerged files.
  hint: Fix them up in the work tree ...
  hint: ...
  fatal: Exiting because of an unresolved conflict.

Hints are printed using the same advise function introduced in
v1.7.3-rc0~26^2~3 (Introduce advise() to print hints, 2010-08-11).

Inspired-by: Christian Couder <chistian.couder@gmail.com>
Helped-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 advice.c         |   31 ++++++++++++++++++++++++-------
 advice.h         |    3 ++-
 builtin/revert.c |    9 ---------
 3 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/advice.c b/advice.c
index 0be4b5f..e02e632 100644
--- a/advice.c
+++ b/advice.c
@@ -19,6 +19,15 @@ static struct {
 	{ "detachedhead", &advice_detached_head },
 };
 
+void advise(const char *advice, ...)
+{
+	va_list params;
+
+	va_start(params, advice);
+	vreportf("hint: ", advice, params);
+	va_end(params);
+}
+
 int git_default_advice_config(const char *var, const char *value)
 {
 	const char *k = skip_prefix(var, "advice.");
@@ -34,16 +43,24 @@ int git_default_advice_config(const char *var, const char *value)
 	return 0;
 }
 
-void NORETURN die_resolve_conflict(const char *me)
+int error_resolve_conflict(const char *me)
 {
-	if (advice_resolve_conflict)
+	error("'%s' is not possible because you have unmerged files.", me);
+	if (advice_resolve_conflict) {
 		/*
 		 * Message used both when 'git commit' fails and when
 		 * other commands doing a merge do.
 		 */
-		die("'%s' is not possible because you have unmerged files.\n"
-		    "Please, fix them up in the work tree, and then use 'git add/rm <file>' as\n"
-		    "appropriate to mark resolution and make a commit, or use 'git commit -a'.", me);
-	else
-		die("'%s' is not possible because you have unmerged files.", me);
+		advise("Fix them up in the work tree,");
+		advise("and then use 'git add/rm <file>' as");
+		advise("appropriate to mark resolution and make a commit,");
+		advise("or use 'git commit -a'.");
+	}
+	return -1;
+}
+
+void NORETURN die_resolve_conflict(const char *me)
+{
+	error_resolve_conflict(me);
+	die("Exiting because of an unresolved conflict.");
 }
diff --git a/advice.h b/advice.h
index 3244ebb..e5d0af7 100644
--- a/advice.h
+++ b/advice.h
@@ -11,7 +11,8 @@ extern int advice_implicit_identity;
 extern int advice_detached_head;
 
 int git_default_advice_config(const char *var, const char *value);
-
+void advise(const char *advice, ...);
+int error_resolve_conflict(const char *me);
 extern void NORETURN die_resolve_conflict(const char *me);
 
 #endif /* ADVICE_H */
diff --git a/builtin/revert.c b/builtin/revert.c
index 1f27c63..2df3f3b 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -214,15 +214,6 @@ static void write_cherry_pick_head(void)
 	strbuf_release(&buf);
 }
 
-static void advise(const char *advice, ...)
-{
-	va_list params;
-
-	va_start(params, advice);
-	vreportf("hint: ", advice, params);
-	va_end(params);
-}
-
 static void print_advice(void)
 {
 	char *msg = getenv("GIT_CHERRY_PICK_HELP");
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 01/18] advice: Introduce error_resolve_conflict
  2011-07-28 16:52 [PATCH 00/18] Sequencer for inclusion v4 Ramkumar Ramachandra
@ 2011-07-28 16:52 ` Ramkumar Ramachandra
  0 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-28 16:52 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git List, Jonathan Nieder, Christian Couder, Daniel Barkalow, Jeff King

Enable future callers to report a conflict and not die immediately by
introducing a new function called error_resolve_conflict.
Re-implement die_resolve_conflict as a call to error_resolve_conflict
followed by a call to die.  Consequently, the message printed by
die_resolve_conflict changes from

  fatal: 'commit' is not possible because you have unmerged files.
         Please, fix them up in the work tree ...
         ...

to

  error: 'commit' is not possible because you have unmerged files.
  hint: Fix them up in the work tree ...
  hint: ...
  fatal: Exiting because of an unresolved conflict.

Hints are printed using the same advise function introduced in
v1.7.3-rc0~26^2~3 (Introduce advise() to print hints, 2010-08-11).

Inspired-by: Christian Couder <chistian.couder@gmail.com>
Helped-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 advice.c         |   31 ++++++++++++++++++++++++-------
 advice.h         |    3 ++-
 builtin/revert.c |    9 ---------
 3 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/advice.c b/advice.c
index 0be4b5f..e02e632 100644
--- a/advice.c
+++ b/advice.c
@@ -19,6 +19,15 @@ static struct {
 	{ "detachedhead", &advice_detached_head },
 };
 
+void advise(const char *advice, ...)
+{
+	va_list params;
+
+	va_start(params, advice);
+	vreportf("hint: ", advice, params);
+	va_end(params);
+}
+
 int git_default_advice_config(const char *var, const char *value)
 {
 	const char *k = skip_prefix(var, "advice.");
@@ -34,16 +43,24 @@ int git_default_advice_config(const char *var, const char *value)
 	return 0;
 }
 
-void NORETURN die_resolve_conflict(const char *me)
+int error_resolve_conflict(const char *me)
 {
-	if (advice_resolve_conflict)
+	error("'%s' is not possible because you have unmerged files.", me);
+	if (advice_resolve_conflict) {
 		/*
 		 * Message used both when 'git commit' fails and when
 		 * other commands doing a merge do.
 		 */
-		die("'%s' is not possible because you have unmerged files.\n"
-		    "Please, fix them up in the work tree, and then use 'git add/rm <file>' as\n"
-		    "appropriate to mark resolution and make a commit, or use 'git commit -a'.", me);
-	else
-		die("'%s' is not possible because you have unmerged files.", me);
+		advise("Fix them up in the work tree,");
+		advise("and then use 'git add/rm <file>' as");
+		advise("appropriate to mark resolution and make a commit,");
+		advise("or use 'git commit -a'.");
+	}
+	return -1;
+}
+
+void NORETURN die_resolve_conflict(const char *me)
+{
+	error_resolve_conflict(me);
+	die("Exiting because of an unresolved conflict.");
 }
diff --git a/advice.h b/advice.h
index 3244ebb..e5d0af7 100644
--- a/advice.h
+++ b/advice.h
@@ -11,7 +11,8 @@ extern int advice_implicit_identity;
 extern int advice_detached_head;
 
 int git_default_advice_config(const char *var, const char *value);
-
+void advise(const char *advice, ...);
+int error_resolve_conflict(const char *me);
 extern void NORETURN die_resolve_conflict(const char *me);
 
 #endif /* ADVICE_H */
diff --git a/builtin/revert.c b/builtin/revert.c
index 1f27c63..2df3f3b 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -214,15 +214,6 @@ static void write_cherry_pick_head(void)
 	strbuf_release(&buf);
 }
 
-static void advise(const char *advice, ...)
-{
-	va_list params;
-
-	va_start(params, advice);
-	vreportf("hint: ", advice, params);
-	va_end(params);
-}
-
 static void print_advice(void)
 {
 	char *msg = getenv("GIT_CHERRY_PICK_HELP");
-- 
1.7.4.rc1.7.g2cf08.dirty

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

* [PATCH 01/18] advice: Introduce error_resolve_conflict
  2011-07-27  3:18 [PATCH 00/18] GSoC update: Sequencer for inclusion v3 Ramkumar Ramachandra
@ 2011-07-27  3:18 ` Ramkumar Ramachandra
  0 siblings, 0 replies; 38+ messages in thread
From: Ramkumar Ramachandra @ 2011-07-27  3:18 UTC (permalink / raw)
  To: Git List
  Cc: Jonathan Nieder, Junio C Hamano, Christian Couder,
	Daniel Barkalow, Jeff King

Enable future callers to report a conflict and not die immediately by
introducing a new function called error_resolve_conflict.
Re-implement die_resolve_conflict as a call to error_resolve_conflict
followed by a call to die.  Consequently, the message printed by
die_resolve_conflict changes from

fatal: 'commit' is not possible because you have unmerged files.
       Please, fix them up in the work tree ...
       ...

to

error: 'commit' is not possible because you have unmerged files.
hint: Please, fix them up in the work tree ...
hint: ...
fatal: Exiting because of an unresolved conflict.

Hints are printed using the same advise function introduced in
v1.7.3-rc0~26^2~3 (Introduce advise() to print hints, 2010-08-11).

Inspired-by: Christian Couder <chistian.couder@gmail.com>
Helped-by: Jonathan Nieder <jrnieder@gmail.com>
Reviewed-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 advice.c         |   31 ++++++++++++++++++++++++-------
 advice.h         |    3 ++-
 builtin/revert.c |    9 ---------
 3 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/advice.c b/advice.c
index 0be4b5f..e02e632 100644
--- a/advice.c
+++ b/advice.c
@@ -19,6 +19,15 @@ static struct {
 	{ "detachedhead", &advice_detached_head },
 };
 
+void advise(const char *advice, ...)
+{
+	va_list params;
+
+	va_start(params, advice);
+	vreportf("hint: ", advice, params);
+	va_end(params);
+}
+
 int git_default_advice_config(const char *var, const char *value)
 {
 	const char *k = skip_prefix(var, "advice.");
@@ -34,16 +43,24 @@ int git_default_advice_config(const char *var, const char *value)
 	return 0;
 }
 
-void NORETURN die_resolve_conflict(const char *me)
+int error_resolve_conflict(const char *me)
 {
-	if (advice_resolve_conflict)
+	error("'%s' is not possible because you have unmerged files.", me);
+	if (advice_resolve_conflict) {
 		/*
 		 * Message used both when 'git commit' fails and when
 		 * other commands doing a merge do.
 		 */
-		die("'%s' is not possible because you have unmerged files.\n"
-		    "Please, fix them up in the work tree, and then use 'git add/rm <file>' as\n"
-		    "appropriate to mark resolution and make a commit, or use 'git commit -a'.", me);
-	else
-		die("'%s' is not possible because you have unmerged files.", me);
+		advise("Fix them up in the work tree,");
+		advise("and then use 'git add/rm <file>' as");
+		advise("appropriate to mark resolution and make a commit,");
+		advise("or use 'git commit -a'.");
+	}
+	return -1;
+}
+
+void NORETURN die_resolve_conflict(const char *me)
+{
+	error_resolve_conflict(me);
+	die("Exiting because of an unresolved conflict.");
 }
diff --git a/advice.h b/advice.h
index 3244ebb..e5d0af7 100644
--- a/advice.h
+++ b/advice.h
@@ -11,7 +11,8 @@ extern int advice_implicit_identity;
 extern int advice_detached_head;
 
 int git_default_advice_config(const char *var, const char *value);
-
+void advise(const char *advice, ...);
+int error_resolve_conflict(const char *me);
 extern void NORETURN die_resolve_conflict(const char *me);
 
 #endif /* ADVICE_H */
diff --git a/builtin/revert.c b/builtin/revert.c
index 1f27c63..2df3f3b 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -214,15 +214,6 @@ static void write_cherry_pick_head(void)
 	strbuf_release(&buf);
 }
 
-static void advise(const char *advice, ...)
-{
-	va_list params;
-
-	va_start(params, advice);
-	vreportf("hint: ", advice, params);
-	va_end(params);
-}
-
 static void print_advice(void)
 {
 	char *msg = getenv("GIT_CHERRY_PICK_HELP");
-- 
1.7.4.rc1.7.g2cf08.dirty

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

end of thread, other threads:[~2011-08-04 10:42 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-07-19 17:17 [GSoC update] Sequencer for inclusion v2 Ramkumar Ramachandra
2011-07-19 17:17 ` [PATCH 01/18] advice: Introduce error_resolve_conflict Ramkumar Ramachandra
2011-07-21 15:35   ` Phil Hord
2011-07-22 22:35     ` Jeff King
2011-07-24 10:23       ` Ramkumar Ramachandra
2011-07-24 18:15       ` Junio C Hamano
2011-07-25 21:17         ` Phil Hord
2011-07-19 17:17 ` [PATCH 02/18] config: Introduce functions to write non-standard file Ramkumar Ramachandra
2011-07-19 19:55   ` Jeff King
2011-07-19 20:35     ` Jonathan Nieder
2011-07-19 21:26     ` Junio C Hamano
2011-07-19 21:57       ` Jeff King
2011-07-24 10:17         ` Ramkumar Ramachandra
2011-07-19 17:17 ` [PATCH 03/18] revert: Simplify and inline add_message_to_msg Ramkumar Ramachandra
2011-07-19 17:17 ` [PATCH 04/18] revert: Don't check lone argument in get_encoding Ramkumar Ramachandra
2011-07-19 17:17 ` [PATCH 05/18] revert: Rename no_replay to record_origin Ramkumar Ramachandra
2011-07-19 17:17 ` [PATCH 06/18] revert: Propogate errors upwards from do_pick_commit Ramkumar Ramachandra
2011-07-19 17:17 ` [PATCH 07/18] revert: Eliminate global "commit" variable Ramkumar Ramachandra
2011-07-19 17:17 ` [PATCH 08/18] revert: Introduce struct to keep command-line options Ramkumar Ramachandra
2011-07-19 17:17 ` [PATCH 09/18] revert: Separate cmdline parsing from functional code Ramkumar Ramachandra
2011-07-19 17:17 ` [PATCH 10/18] revert: Don't create invalid replay_opts in parse_args Ramkumar Ramachandra
2011-07-19 17:17 ` [PATCH 11/18] revert: Save data for continuing after conflict resolution Ramkumar Ramachandra
2011-07-19 17:17 ` [PATCH 12/18] revert: Save command-line options for continuing operation Ramkumar Ramachandra
2011-07-19 17:17 ` [PATCH 13/18] revert: Make pick_commits functionally act on a commit list Ramkumar Ramachandra
2011-07-19 17:17 ` [PATCH 14/18] revert: Introduce --reset to remove sequencer state Ramkumar Ramachandra
2011-07-19 17:17 ` [PATCH 15/18] reset: Make reset remove the " Ramkumar Ramachandra
2011-07-19 17:17 ` [PATCH 16/18] revert: Remove sequencer state when no commits are pending Ramkumar Ramachandra
2011-07-19 17:17 ` [PATCH 17/18] revert: Don't implictly stomp pending sequencer operation Ramkumar Ramachandra
2011-07-19 17:17 ` [PATCH 18/18] revert: Introduce --continue to continue the operation Ramkumar Ramachandra
2011-07-19 18:47 ` [GSoC update] Sequencer for inclusion v2 Junio C Hamano
2011-07-20  9:02   ` Ramkumar Ramachandra
2011-07-19 23:54 ` Junio C Hamano
2011-07-24 10:11   ` Ramkumar Ramachandra
2011-07-24 18:17     ` Junio C Hamano
2011-07-27  3:18 [PATCH 00/18] GSoC update: Sequencer for inclusion v3 Ramkumar Ramachandra
2011-07-27  3:18 ` [PATCH 01/18] advice: Introduce error_resolve_conflict Ramkumar Ramachandra
2011-07-28 16:52 [PATCH 00/18] Sequencer for inclusion v4 Ramkumar Ramachandra
2011-07-28 16:52 ` [PATCH 01/18] advice: Introduce error_resolve_conflict Ramkumar Ramachandra
2011-08-01 18:06 [PATCH v5 00/18] Sequencer for inclusion Ramkumar Ramachandra
2011-08-01 18:06 ` [PATCH 01/18] advice: Introduce error_resolve_conflict Ramkumar Ramachandra
2011-08-04 10:38 [PATCH 00/18] Sequencer for inclusion v6 Ramkumar Ramachandra
2011-08-04 10:38 ` [PATCH 01/18] advice: Introduce error_resolve_conflict Ramkumar Ramachandra

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.