Skip to content

Commit 56dc3ab

Browse files
dschogitster
authored andcommitted
sequencer (rebase -i): implement the 'edit' command
This patch is a straight-forward reimplementation of the `edit` operation of the interactive rebase command. Well, not *quite* straight-forward: when stopping, the `edit` command wants to write the `patch` file (which is not only the patch, but includes the commit message and author information). To that end, this patch requires the earlier work that taught the log-tree machinery to respect the `file` setting of rev_info->diffopt to write to a file stream different than stdout. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 25c4366 commit 56dc3ab

File tree

1 file changed

+114
-3
lines changed

1 file changed

+114
-3
lines changed

sequencer.c

Lines changed: 114 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "argv-array.h"
1818
#include "quote.h"
1919
#include "trailer.h"
20+
#include "log-tree.h"
2021

2122
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
2223

@@ -44,6 +45,20 @@ static GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
4445
* being rebased.
4546
*/
4647
static GIT_PATH_FUNC(rebase_path_author_script, "rebase-merge/author-script")
48+
/*
49+
* When an "edit" rebase command is being processed, the SHA1 of the
50+
* commit to be edited is recorded in this file. When "git rebase
51+
* --continue" is executed, if there are any staged changes then they
52+
* will be amended to the HEAD commit, but only provided the HEAD
53+
* commit is still the commit to be edited. When any other rebase
54+
* command is processed, this file is deleted.
55+
*/
56+
static GIT_PATH_FUNC(rebase_path_amend, "rebase-merge/amend")
57+
/*
58+
* When we stop at a given patch via the "edit" command, this file contains
59+
* the abbreviated commit name of the corresponding patch.
60+
*/
61+
static GIT_PATH_FUNC(rebase_path_stopped_sha, "rebase-merge/stopped-sha")
4762
/*
4863
* The following files are written by git-rebase just after parsing the
4964
* command-line (and are only consumed, not modified, by the sequencer).
@@ -616,13 +631,15 @@ enum todo_command {
616631
/* commands that handle commits */
617632
TODO_PICK = 0,
618633
TODO_REVERT,
634+
TODO_EDIT,
619635
/* commands that do nothing but are counted for reporting progress */
620636
TODO_NOOP
621637
};
622638

623639
static const char *todo_command_strings[] = {
624640
"pick",
625641
"revert",
642+
"edit",
626643
"noop"
627644
};
628645

@@ -1302,9 +1319,87 @@ static int save_opts(struct replay_opts *opts)
13021319
return res;
13031320
}
13041321

1322+
static int make_patch(struct commit *commit, struct replay_opts *opts)
1323+
{
1324+
struct strbuf buf = STRBUF_INIT;
1325+
struct rev_info log_tree_opt;
1326+
const char *subject, *p;
1327+
int res = 0;
1328+
1329+
p = short_commit_name(commit);
1330+
if (write_message(p, strlen(p), rebase_path_stopped_sha(), 1) < 0)
1331+
return -1;
1332+
1333+
strbuf_addf(&buf, "%s/patch", get_dir(opts));
1334+
memset(&log_tree_opt, 0, sizeof(log_tree_opt));
1335+
init_revisions(&log_tree_opt, NULL);
1336+
log_tree_opt.abbrev = 0;
1337+
log_tree_opt.diff = 1;
1338+
log_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH;
1339+
log_tree_opt.disable_stdin = 1;
1340+
log_tree_opt.no_commit_id = 1;
1341+
log_tree_opt.diffopt.file = fopen(buf.buf, "w");
1342+
log_tree_opt.diffopt.use_color = GIT_COLOR_NEVER;
1343+
if (!log_tree_opt.diffopt.file)
1344+
res |= error_errno(_("could not open '%s'"), buf.buf);
1345+
else {
1346+
res |= log_tree_commit(&log_tree_opt, commit);
1347+
fclose(log_tree_opt.diffopt.file);
1348+
}
1349+
strbuf_reset(&buf);
1350+
1351+
strbuf_addf(&buf, "%s/message", get_dir(opts));
1352+
if (!file_exists(buf.buf)) {
1353+
const char *commit_buffer = get_commit_buffer(commit, NULL);
1354+
find_commit_subject(commit_buffer, &subject);
1355+
res |= write_message(subject, strlen(subject), buf.buf, 1);
1356+
unuse_commit_buffer(commit, commit_buffer);
1357+
}
1358+
strbuf_release(&buf);
1359+
1360+
return res;
1361+
}
1362+
1363+
static int intend_to_amend(void)
1364+
{
1365+
unsigned char head[20];
1366+
char *p;
1367+
1368+
if (get_sha1("HEAD", head))
1369+
return error(_("cannot read HEAD"));
1370+
1371+
p = sha1_to_hex(head);
1372+
return write_message(p, strlen(p), rebase_path_amend(), 1);
1373+
}
1374+
1375+
static int error_with_patch(struct commit *commit,
1376+
const char *subject, int subject_len,
1377+
struct replay_opts *opts, int exit_code, int to_amend)
1378+
{
1379+
if (make_patch(commit, opts))
1380+
return -1;
1381+
1382+
if (to_amend) {
1383+
if (intend_to_amend())
1384+
return -1;
1385+
1386+
fprintf(stderr, "You can amend the commit now, with\n"
1387+
"\n"
1388+
" git commit --amend %s\n"
1389+
"\n"
1390+
"Once you are satisfied with your changes, run\n"
1391+
"\n"
1392+
" git rebase --continue\n", gpg_sign_opt_quoted(opts));
1393+
} else if (exit_code)
1394+
fprintf(stderr, "Could not apply %s... %.*s\n",
1395+
short_commit_name(commit), subject_len, subject);
1396+
1397+
return exit_code;
1398+
}
1399+
13051400
static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
13061401
{
1307-
int res;
1402+
int res = 0;
13081403

13091404
setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
13101405
if (opts->allow_ff)
@@ -1317,17 +1412,33 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
13171412
struct todo_item *item = todo_list->items + todo_list->current;
13181413
if (save_todo(todo_list, opts))
13191414
return -1;
1320-
if (item->command <= TODO_REVERT)
1415+
if (item->command <= TODO_EDIT) {
13211416
res = do_pick_commit(item->command, item->commit,
13221417
opts);
1323-
else if (!is_noop(item->command))
1418+
if (item->command == TODO_EDIT) {
1419+
struct commit *commit = item->commit;
1420+
if (!res)
1421+
warning(_("stopped at %s... %.*s"),
1422+
short_commit_name(commit),
1423+
item->arg_len, item->arg);
1424+
return error_with_patch(commit,
1425+
item->arg, item->arg_len, opts, res,
1426+
!res);
1427+
}
1428+
} else if (!is_noop(item->command))
13241429
return error(_("unknown command %d"), item->command);
13251430

13261431
todo_list->current++;
13271432
if (res)
13281433
return res;
13291434
}
13301435

1436+
if (is_rebase_i(opts)) {
1437+
/* Stopped in the middle, as planned? */
1438+
if (todo_list->current < todo_list->nr)
1439+
return 0;
1440+
}
1441+
13311442
/*
13321443
* Sequence of picks finished successfully; cleanup by
13331444
* removing the .git/sequencer directory

0 commit comments

Comments
 (0)