13
#include "rerere.h"
14
#include "merge-recursive.h"
15
#include "refs.h"
16
+#include "dir.h"
17
18
/*
19
* This implements the builtins revert and cherry-pick.
61
62
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
63
64
+#define SEQ_DIR "sequencer"
65
+#define SEQ_HEAD_FILE "sequencer/head"
66
+#define SEQ_TODO_FILE "sequencer/todo"
67
+
68
static const char *action_name(const struct replay_opts *opts)
69
{
70
return opts->action == REVERT ? "revert" : "cherry-pick";
592
rollback_lock_file(&index_lock);
593
}
594
590
-static int pick_commits(struct replay_opts *opts)
595
+/*
596
+ * Append a commit to the end of the commit_list.
597
+ *
598
+ * next starts by pointing to the variable that holds the head of an
599
+ * empty commit_list, and is updated to point to the "next" field of
600
+ * the last item on the list as new commits are appended.
601
+ *
602
+ * Usage example:
603
+ *
604
+ * struct commit_list *list;
605
+ * struct commit_list **next = &list;
606
+ *
607
+ * next = commit_list_append(c1, next);
608
+ * next = commit_list_append(c2, next);
609
+ * assert(commit_list_count(list) == 2);
610
+ * return list;
611
+ */
612
+struct commit_list **commit_list_append(struct commit *commit,
613
+ struct commit_list **next)
614
+{
615
+ struct commit_list *new = xmalloc(sizeof(struct commit_list));
616
+ new->item = commit;
617
+ *next = new;
618
+ new->next = NULL;
619
+ return &new->next;
620
+}
621
+
622
+static int format_todo(struct strbuf *buf, struct commit_list *todo_list,
623
+ struct replay_opts *opts)
624
+{
625
+ struct commit_list *cur = NULL;
626
+ struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
627
+ const char *sha1_abbrev = NULL;
628
+ const char *action_str = opts->action == REVERT ? "revert" : "pick";
629
+
630
+ for (cur = todo_list; cur; cur = cur->next) {
631
+ sha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);
632
+ if (get_message(cur->item, &msg))
633
+ return error(_("Cannot get commit message for %s"), sha1_abbrev);
634
+ strbuf_addf(buf, "%s %s %s\n", action_str, sha1_abbrev, msg.subject);
635
+ }
636
+ return 0;
637
+}
638
+
639
+static void walk_revs_populate_todo(struct commit_list **todo_list,
640
+ struct replay_opts *opts)
641
{
642
struct rev_info revs;
643
struct commit *commit;
644
+ struct commit_list **next;
645
+
646
+ prepare_revs(&revs, opts);
647
+
648
+ next = todo_list;
649
+ while ((commit = get_revision(&revs)))
650
+ next = commit_list_append(commit, next);
651
+}
652
+
653
+static void create_seq_dir(void)
654
+{
655
+ const char *seq_dir = git_path(SEQ_DIR);
656
+
657
+ if (!(file_exists(seq_dir) && is_directory(seq_dir))
658
+ && mkdir(seq_dir, 0777) < 0)
659
+ die_errno(_("Could not create sequencer directory '%s'."), seq_dir);
660
+}
661
+
662
+static void save_head(const char *head)
663
+{
664
+ const char *head_file = git_path(SEQ_HEAD_FILE);
665
+ static struct lock_file head_lock;
666
+ struct strbuf buf = STRBUF_INIT;
667
+ int fd;
668
+
669
+ fd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);
670
+ strbuf_addf(&buf, "%s\n", head);
671
+ if (write_in_full(fd, buf.buf, buf.len) < 0)
672
+ die_errno(_("Could not write to %s."), head_file);
673
+ if (commit_lock_file(&head_lock) < 0)
674
+ die(_("Error wrapping up %s."), head_file);
675
+}
676
+
677
+static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)
678
+{
679
+ const char *todo_file = git_path(SEQ_TODO_FILE);
680
+ static struct lock_file todo_lock;
681
+ struct strbuf buf = STRBUF_INIT;
682
+ int fd;
683
+
684
+ fd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);
685
+ if (format_todo(&buf, todo_list, opts) < 0)
686
+ die(_("Could not format %s."), todo_file);
687
+ if (write_in_full(fd, buf.buf, buf.len) < 0) {
688
+ strbuf_release(&buf);
689
+ die_errno(_("Could not write to %s."), todo_file);
690
+ }
691
+ if (commit_lock_file(&todo_lock) < 0) {
692
+ strbuf_release(&buf);
693
+ die(_("Error wrapping up %s."), todo_file);
694
+ }
695
+ strbuf_release(&buf);
696
+}
697
+
698
+static int pick_commits(struct replay_opts *opts)
699
+{
700
+ struct commit_list *todo_list = NULL;
701
+ struct strbuf buf = STRBUF_INIT;
702
+ unsigned char sha1[20];
703
+ struct commit_list *cur;
704
+ int res;
705
706
setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
707
if (opts->allow_ff)
709
opts->record_origin || opts->edit));
710
read_and_refresh_cache(opts);
711
601
- prepare_revs(&revs, opts);
712
+ walk_revs_populate_todo(&todo_list, opts);
713
+ create_seq_dir();
714
+ if (get_sha1("HEAD", sha1)) {
715
+ if (opts->action == REVERT)
716
+ die(_("Can't revert as initial commit"));
717
+ die(_("Can't cherry-pick into empty head"));
718
+ }
719
+ save_head(sha1_to_hex(sha1));
720
603
- while ((commit = get_revision(&revs))) {
604
- int res = do_pick_commit(commit, opts);
721
+ for (cur = todo_list; cur; cur = cur->next) {
722
+ save_todo(cur, opts);
723
+ res = do_pick_commit(cur->item, opts);
724
if (res)
725
return res;
726
}
727
728
+ /*
729
+ * Sequence of picks finished successfully; cleanup by
730
+ * removing the .git/sequencer directory
731
+ */
732
+ strbuf_addf(&buf, "%s", git_path(SEQ_DIR));
733
+ remove_dir_recursively(&buf, 0);
734
+ strbuf_release(&buf);
735
return 0;
736
}
737