sequencer (rebase -i): copy commit notes at end
authorJohannes Schindelin <johannes.schindelin@gmx.de>
Mon, 2 Jan 2017 15:28:16 +0000 (16:28 +0100)
committerJunio C Hamano <gitster@pobox.com>
Mon, 9 Jan 2017 22:57:30 +0000 (14:57 -0800)
When rebasing commits that have commit notes attached, the interactive
rebase rewrites those notes faithfully at the end. The sequencer must
do this, too, if it wishes to do interactive rebase's job.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
sequencer.c
index 95ae4bcd1e90cad5d4937c17276348aa01fbf949..50380a15b83d7df54cdc489bfff50e33dab7afe7 100644 (file)
@@ -95,6 +95,15 @@ static GIT_PATH_FUNC(rebase_path_amend, "rebase-merge/amend")
  * the abbreviated commit name of the corresponding patch.
  */
 static GIT_PATH_FUNC(rebase_path_stopped_sha, "rebase-merge/stopped-sha")
+/*
+ * For the post-rewrite hook, we make a list of rewritten commits and
+ * their new sha1s.  The rewritten-pending list keeps the sha1s of
+ * commits that have been processed, but not committed yet,
+ * e.g. because they are waiting for a 'squash' command.
+ */
+static GIT_PATH_FUNC(rebase_path_rewritten_list, "rebase-merge/rewritten-list")
+static GIT_PATH_FUNC(rebase_path_rewritten_pending,
+       "rebase-merge/rewritten-pending")
 /*
  * The following files are written by git-rebase just after parsing the
  * command-line (and are only consumed, not modified, by the sequencer).
@@ -850,6 +859,44 @@ static int update_squash_messages(enum todo_command command,
        return res;
 }
 
+static void flush_rewritten_pending(void) {
+       struct strbuf buf = STRBUF_INIT;
+       unsigned char newsha1[20];
+       FILE *out;
+
+       if (strbuf_read_file(&buf, rebase_path_rewritten_pending(), 82) > 0 &&
+                       !get_sha1("HEAD", newsha1) &&
+                       (out = fopen(rebase_path_rewritten_list(), "a"))) {
+               char *bol = buf.buf, *eol;
+
+               while (*bol) {
+                       eol = strchrnul(bol, '\n');
+                       fprintf(out, "%.*s %s\n", (int)(eol - bol),
+                                       bol, sha1_to_hex(newsha1));
+                       if (!*eol)
+                               break;
+                       bol = eol + 1;
+               }
+               fclose(out);
+               unlink(rebase_path_rewritten_pending());
+       }
+       strbuf_release(&buf);
+}
+
+static void record_in_rewritten(struct object_id *oid,
+               enum todo_command next_command) {
+       FILE *out = fopen(rebase_path_rewritten_pending(), "a");
+
+       if (!out)
+               return;
+
+       fprintf(out, "%s\n", oid_to_hex(oid));
+       fclose(out);
+
+       if (!is_fixup(next_command))
+               flush_rewritten_pending();
+}
+
 static int do_pick_commit(enum todo_command command, struct commit *commit,
                struct replay_opts *opts, int final_fixup)
 {
@@ -1743,6 +1790,17 @@ static int is_final_fixup(struct todo_list *todo_list)
        return 1;
 }
 
+static enum todo_command peek_command(struct todo_list *todo_list, int offset)
+{
+       int i;
+
+       for (i = todo_list->current + offset; i < todo_list->nr; i++)
+               if (!is_noop(todo_list->items[i].command))
+                       return todo_list->items[i].command;
+
+       return -1;
+}
+
 static const char *reflog_message(struct replay_opts *opts,
        const char *sub_action, const char *fmt, ...)
 {
@@ -1801,6 +1859,9 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                                        item->arg, item->arg_len, opts, res,
                                        !res);
                        }
+                       if (is_rebase_i(opts) && !res)
+                               record_in_rewritten(&item->commit->object.oid,
+                                       peek_command(todo_list, 1));
                        if (res && is_fixup(item->command)) {
                                if (res == 1)
                                        intend_to_amend();
@@ -1827,6 +1888,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
 
        if (is_rebase_i(opts)) {
                struct strbuf head_ref = STRBUF_INIT, buf = STRBUF_INIT;
+               struct stat st;
 
                /* Stopped in the middle, as planned? */
                if (todo_list->current < todo_list->nr)
@@ -1891,6 +1953,20 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                                log_tree_diff_flush(&log_tree_opt);
                        }
                }
+               flush_rewritten_pending();
+               if (!stat(rebase_path_rewritten_list(), &st) &&
+                               st.st_size > 0) {
+                       struct child_process child = CHILD_PROCESS_INIT;
+
+                       child.in = open(rebase_path_rewritten_list(), O_RDONLY);
+                       child.git_cmd = 1;
+                       argv_array_push(&child.args, "notes");
+                       argv_array_push(&child.args, "copy");
+                       argv_array_push(&child.args, "--for-rewrite=rebase");
+                       /* we don't care if this copying failed */
+                       run_command(&child);
+               }
+
                strbuf_release(&buf);
                strbuf_release(&head_ref);
        }