From: Ramkumar Ramachandra <artagnon@gmail.com>
To: Git List <git@vger.kernel.org>
Cc: Jonathan Nieder <jrnieder@gmail.com>,
Junio C Hamano <gitster@pobox.com>,
Christian Couder <chriscool@tuxfamily.org>,
Daniel Barkalow <barkalow@iabervon.org>
Subject: [PATCH 10/13] revert: Persist data for continuation
Date: Tue, 21 Jun 2011 13:04:46 +0000 [thread overview]
Message-ID: <1308661489-20080-11-git-send-email-artagnon@gmail.com> (raw)
In-Reply-To: <1308661489-20080-1-git-send-email-artagnon@gmail.com>
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. Don't touch
CHERRY_PICK_HEAD -- it will still be useful when a conflict is
encountered.
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 | 123 ++++++++++++++++++++++++++++++++++++--
t/t3510-cherry-pick-sequence.sh | 37 ++++++++++++
2 files changed, 153 insertions(+), 7 deletions(-)
create mode 100644 t/t3510-cherry-pick-sequence.sh
diff --git a/builtin/revert.c b/builtin/revert.c
index cfa898f..ca5756b 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.
@@ -25,6 +26,10 @@
* Copyright (c) 2005 Junio C Hamano
*/
+#define SEQ_DIR git_path("sequencer")
+#define HEAD_FILE git_path("sequencer/head")
+#define TODO_FILE git_path("sequencer/todo")
+
static const char * const revert_usage[] = {
"git revert [options] <commit-ish>",
NULL
@@ -412,7 +417,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
return error(_("Your index file is unmerged."));
} else {
if (get_sha1("HEAD", head))
- return error(_("You do not have a valid HEAD"));
+ return error(_("Can't %s on an unborn branch"), me);
if (index_differs_from("HEAD", 0))
return error_dirty_index(me);
}
@@ -574,23 +579,127 @@ static void read_and_refresh_cache(const char *me, struct replay_opts *opts)
rollback_lock_file(&index_lock);
}
-static int pick_commits(struct replay_opts *opts)
+static void 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;
+
+ action = (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))
+ die(_("Cannot get commit message for %s"), sha1_abbrev);
+ strbuf_addf(buf, "%s %s %s\n", action, sha1_abbrev, msg.subject);
+ }
+}
+
+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 *new_item;
+ struct commit_list *cur = NULL;
+
+ /* Insert into todo_list in the same order */
+ prepare_revs(&revs, opts);
+ while ((commit = get_revision(&revs))) {
+ new_item = xmalloc(sizeof(struct commit_list));
+ new_item->item = commit;
+ if (cur)
+ cur->next = new_item;
+ else
+ *todo_list = new_item;
+ cur = new_item;
+ }
+ cur->next = NULL;
+}
+
+static void persist_head(const char *head)
+{
+ static struct lock_file head_lock;
+ struct strbuf buf = STRBUF_INIT;
+ int fd;
+
+ if (file_exists(SEQ_DIR)) {
+ if (!is_directory(SEQ_DIR) && remove_path(SEQ_DIR) < 0) {
+ strbuf_release(&buf);
+ die(_("Could not remove %s"), SEQ_DIR);
+ }
+ } else {
+ if (mkdir(SEQ_DIR, 0777) < 0) {
+ strbuf_release(&buf);
+ die_errno(_("Could not create sequencer directory '%s'."), SEQ_DIR);
+ }
+ }
+ 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 persist_todo(struct commit_list *todo_list, struct replay_opts *opts)
+{
+ 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);
+ format_todo(&buf, todo_list, opts);
+ 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 cleanup_sequencer_data(void)
+{
+ static struct strbuf seq_dir = STRBUF_INIT;
+
+ strbuf_addf(&seq_dir, "%s", SEQ_DIR);
+ if (remove_dir_recursively(&seq_dir, 0) < 0) {
+ strbuf_release(&seq_dir);
+ return error(_("Unable to clean up after successful %s"), me);
+ }
+ strbuf_release(&seq_dir);
+ return 0;
+}
+
+static int pick_commits(struct replay_opts *opts)
+{
+ struct commit_list *todo_list = NULL;
+ unsigned char sha1[20];
+ struct commit_list *cur;
+ int res;
- setenv(GIT_REFLOG_ACTION, me, 0);
read_and_refresh_cache(me, opts);
+ setenv(GIT_REFLOG_ACTION, me, 0);
- prepare_revs(&revs, opts);
+ walk_revs_populate_todo(&todo_list, opts);
+ if (!get_sha1("HEAD", sha1))
+ persist_head(sha1_to_hex(sha1));
+ persist_todo(todo_list, opts);
- while ((commit = get_revision(&revs))) {
- int res = do_pick_commit(commit, opts);
+ for (cur = todo_list; cur; cur = cur->next) {
+ persist_todo(cur, opts);
+ res = do_pick_commit(cur->item, opts);
if (res)
return res;
}
- return 0;
+ /* Sequence of picks finished successfully; cleanup by
+ removing the .git/sequencer directory */
+ return cleanup_sequencer_data();
}
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
new file mode 100644
index 0000000..83e2722
--- /dev/null
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+test_description='Test cherry-pick continuation features
+
+ + 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 &&
+ git config advice.detachedhead false
+
+'
+
+test_expect_success 'cherry-pick cleans up sequencer directory upon success' '
+ pristine_detach initial &&
+ git cherry-pick initial..picked &&
+ test_path_is_missing .git/sequencer
+'
+
+test_done
--
1.7.5.GIT
next prev parent reply other threads:[~2011-06-21 13:05 UTC|newest]
Thread overview: 47+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-06-21 13:04 [PATCH 00/13] Sequencer with continuation features Ramkumar Ramachandra
2011-06-21 13:04 ` [PATCH 01/13] advice: Introduce error_resolve_conflict Ramkumar Ramachandra
2011-06-21 15:55 ` Jonathan Nieder
2011-06-21 18:43 ` Junio C Hamano
2011-07-02 9:44 ` Ramkumar Ramachandra
2011-07-02 10:09 ` Jonathan Nieder
2011-06-21 13:04 ` [PATCH 02/13] revert: Factor out add_message_to_msg function Ramkumar Ramachandra
2011-06-21 15:58 ` Jonathan Nieder
2011-06-21 19:01 ` Junio C Hamano
2011-06-21 13:04 ` [PATCH 03/13] revert: Don't check lone argument in get_encoding Ramkumar Ramachandra
2011-06-21 16:03 ` Jonathan Nieder
2011-06-21 13:04 ` [PATCH 04/13] revert: Propogate errors upwards from do_pick_commit Ramkumar Ramachandra
2011-06-21 16:22 ` Jonathan Nieder
2011-06-21 19:19 ` Junio C Hamano
2011-06-21 19:32 ` Jonathan Nieder
2011-06-21 20:18 ` Junio C Hamano
2011-07-02 10:31 ` Ramkumar Ramachandra
2011-06-21 13:04 ` [PATCH 05/13] revert: Eliminate global "commit" variable Ramkumar Ramachandra
2011-06-21 16:52 ` Jonathan Nieder
2011-06-21 19:23 ` Junio C Hamano
2011-06-21 13:04 ` [PATCH 06/13] revert: Rename no_replay to record_origin Ramkumar Ramachandra
2011-06-21 13:04 ` [PATCH 07/13] revert: Introduce struct to keep command-line options Ramkumar Ramachandra
2011-06-21 16:58 ` Jonathan Nieder
2011-06-21 19:28 ` Junio C Hamano
2011-06-21 13:04 ` [PATCH 08/13] revert: Separate cmdline parsing from functional code Ramkumar Ramachandra
2011-06-21 17:00 ` Jonathan Nieder
2011-06-21 13:04 ` [PATCH 09/13] revert: Catch incompatible command-line options early Ramkumar Ramachandra
2011-06-21 17:04 ` Jonathan Nieder
2011-07-02 9:47 ` Ramkumar Ramachandra
2011-07-02 9:53 ` Jonathan Nieder
2011-07-02 10:04 ` Jonathan Nieder
2011-07-02 11:19 ` Ramkumar Ramachandra
2011-07-02 11:30 ` Jonathan Nieder
2011-07-02 20:02 ` Jonathan Nieder
2011-06-21 13:04 ` Ramkumar Ramachandra [this message]
2011-06-21 17:11 ` [PATCH 10/13] revert: Persist data for continuation Jonathan Nieder
2011-06-21 19:49 ` Junio C Hamano
2011-06-21 13:04 ` [PATCH 11/13] revert: Introduce a layer of indirection over pick_commits Ramkumar Ramachandra
2011-06-21 19:59 ` Junio C Hamano
2011-06-21 13:04 ` [PATCH 12/13] revert: Introduce skip-all to cleanup sequencer data Ramkumar Ramachandra
2011-06-21 20:02 ` Junio C Hamano
2011-07-02 6:24 ` Ramkumar Ramachandra
2011-07-04 4:59 ` Miles Bader
2011-07-05 10:47 ` Ramkumar Ramachandra
2011-06-21 13:04 ` [RFC PATCH 13/13] revert: Introduce --continue to continue the operation Ramkumar Ramachandra
2011-06-21 17:19 ` Jonathan Nieder
2011-06-21 15:48 ` [PATCH 00/13] Sequencer with continuation features Jonathan Nieder
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1308661489-20080-11-git-send-email-artagnon@gmail.com \
--to=artagnon@gmail.com \
--cc=barkalow@iabervon.org \
--cc=chriscool@tuxfamily.org \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=jrnieder@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).