rebase -i: add fixup [-C | -c] command
Add options to `fixup` command to fixup both the commit contents and message. `fixup -C` command is used to replace the original commit message and `fixup -c`, additionally allows to edit the commit message. Original-patch-by: Phillip Wood <phillip.wood@dunelm.org.uk> Mentored-by: Christian Couder <chriscool@tuxfamily.org> Mentored-by: Phillip Wood <phillip.wood@dunelm.org.uk> Signed-off-by: Charvi Mendiratta <charvi077@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
71ee81cd9e
commit
9e3cebd97c
@ -44,7 +44,9 @@ void append_todo_help(int command_count,
|
|||||||
"r, reword <commit> = use commit, but edit the commit message\n"
|
"r, reword <commit> = use commit, but edit the commit message\n"
|
||||||
"e, edit <commit> = use commit, but stop for amending\n"
|
"e, edit <commit> = use commit, but stop for amending\n"
|
||||||
"s, squash <commit> = use commit, but meld into previous commit\n"
|
"s, squash <commit> = use commit, but meld into previous commit\n"
|
||||||
"f, fixup <commit> = like \"squash\", but discard this commit's log message\n"
|
"f, fixup [-C | -c] <commit> = like \"squash\", but discard this\n"
|
||||||
|
" commit's log message. Use -C to replace with this\n"
|
||||||
|
" commit message or -c to edit the commit message\n"
|
||||||
"x, exec <command> = run command (the rest of the line) using shell\n"
|
"x, exec <command> = run command (the rest of the line) using shell\n"
|
||||||
"b, break = stop here (continue rebase later with 'git rebase --continue')\n"
|
"b, break = stop here (continue rebase later with 'git rebase --continue')\n"
|
||||||
"d, drop <commit> = remove commit\n"
|
"d, drop <commit> = remove commit\n"
|
||||||
|
213
sequencer.c
213
sequencer.c
@ -1718,6 +1718,12 @@ static int is_pick_or_similar(enum todo_command command)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum todo_item_flags {
|
||||||
|
TODO_EDIT_MERGE_MSG = (1 << 0),
|
||||||
|
TODO_REPLACE_FIXUP_MSG = (1 << 1),
|
||||||
|
TODO_EDIT_FIXUP_MSG = (1 << 2),
|
||||||
|
};
|
||||||
|
|
||||||
static size_t subject_length(const char *body)
|
static size_t subject_length(const char *body)
|
||||||
{
|
{
|
||||||
const char *p = body;
|
const char *p = body;
|
||||||
@ -1734,32 +1740,176 @@ static size_t subject_length(const char *body)
|
|||||||
|
|
||||||
static const char first_commit_msg_str[] = N_("This is the 1st commit message:");
|
static const char first_commit_msg_str[] = N_("This is the 1st commit message:");
|
||||||
static const char nth_commit_msg_fmt[] = N_("This is the commit message #%d:");
|
static const char nth_commit_msg_fmt[] = N_("This is the commit message #%d:");
|
||||||
|
static const char skip_first_commit_msg_str[] = N_("The 1st commit message will be skipped:");
|
||||||
static const char skip_nth_commit_msg_fmt[] = N_("The commit message #%d will be skipped:");
|
static const char skip_nth_commit_msg_fmt[] = N_("The commit message #%d will be skipped:");
|
||||||
static const char combined_commit_msg_fmt[] = N_("This is a combination of %d commits.");
|
static const char combined_commit_msg_fmt[] = N_("This is a combination of %d commits.");
|
||||||
|
|
||||||
static void append_squash_message(struct strbuf *buf, const char *body,
|
static int check_fixup_flag(enum todo_command command,
|
||||||
struct replay_opts *opts)
|
enum todo_item_flags flag)
|
||||||
{
|
{
|
||||||
size_t commented_len = 0;
|
return command == TODO_FIXUP && ((flag & TODO_REPLACE_FIXUP_MSG) ||
|
||||||
|
(flag & TODO_EDIT_FIXUP_MSG));
|
||||||
|
}
|
||||||
|
|
||||||
unlink(rebase_path_fixup_msg());
|
/*
|
||||||
if (starts_with(body, "squash!") || starts_with(body, "fixup!"))
|
* Wrapper around strbuf_add_commented_lines() which avoids double
|
||||||
|
* commenting commit subjects.
|
||||||
|
*/
|
||||||
|
static void add_commented_lines(struct strbuf *buf, const void *str, size_t len)
|
||||||
|
{
|
||||||
|
const char *s = str;
|
||||||
|
while (len > 0 && s[0] == comment_line_char) {
|
||||||
|
size_t count;
|
||||||
|
const char *n = memchr(s, '\n', len);
|
||||||
|
if (!n)
|
||||||
|
count = len;
|
||||||
|
else
|
||||||
|
count = n - s + 1;
|
||||||
|
strbuf_add(buf, s, count);
|
||||||
|
s += count;
|
||||||
|
len -= count;
|
||||||
|
}
|
||||||
|
strbuf_add_commented_lines(buf, s, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Does the current fixup chain contain a squash command? */
|
||||||
|
static int seen_squash(struct replay_opts *opts)
|
||||||
|
{
|
||||||
|
return starts_with(opts->current_fixups.buf, "squash") ||
|
||||||
|
strstr(opts->current_fixups.buf, "\nsquash");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_comment_bufs(struct strbuf *buf1, struct strbuf *buf2, int n)
|
||||||
|
{
|
||||||
|
strbuf_setlen(buf1, 2);
|
||||||
|
strbuf_addf(buf1, _(nth_commit_msg_fmt), n);
|
||||||
|
strbuf_addch(buf1, '\n');
|
||||||
|
strbuf_setlen(buf2, 2);
|
||||||
|
strbuf_addf(buf2, _(skip_nth_commit_msg_fmt), n);
|
||||||
|
strbuf_addch(buf2, '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Comment out any un-commented commit messages, updating the message comments
|
||||||
|
* to say they will be skipped but do not comment out the empty lines that
|
||||||
|
* surround commit messages and their comments.
|
||||||
|
*/
|
||||||
|
static void update_squash_message_for_fixup(struct strbuf *msg)
|
||||||
|
{
|
||||||
|
void (*copy_lines)(struct strbuf *, const void *, size_t) = strbuf_add;
|
||||||
|
struct strbuf buf1 = STRBUF_INIT, buf2 = STRBUF_INIT;
|
||||||
|
const char *s, *start;
|
||||||
|
char *orig_msg;
|
||||||
|
size_t orig_msg_len;
|
||||||
|
int i = 1;
|
||||||
|
|
||||||
|
strbuf_addf(&buf1, "# %s\n", _(first_commit_msg_str));
|
||||||
|
strbuf_addf(&buf2, "# %s\n", _(skip_first_commit_msg_str));
|
||||||
|
s = start = orig_msg = strbuf_detach(msg, &orig_msg_len);
|
||||||
|
while (s) {
|
||||||
|
const char *next;
|
||||||
|
size_t off;
|
||||||
|
if (skip_prefix(s, buf1.buf, &next)) {
|
||||||
|
/*
|
||||||
|
* Copy the last message, preserving the blank line
|
||||||
|
* preceding the current line
|
||||||
|
*/
|
||||||
|
off = (s > start + 1 && s[-2] == '\n') ? 1 : 0;
|
||||||
|
copy_lines(msg, start, s - start - off);
|
||||||
|
if (off)
|
||||||
|
strbuf_addch(msg, '\n');
|
||||||
|
/*
|
||||||
|
* The next message needs to be commented out but the
|
||||||
|
* message header is already commented out so just copy
|
||||||
|
* it and the blank line that follows it.
|
||||||
|
*/
|
||||||
|
strbuf_addbuf(msg, &buf2);
|
||||||
|
if (*next == '\n')
|
||||||
|
strbuf_addch(msg, *next++);
|
||||||
|
start = s = next;
|
||||||
|
copy_lines = add_commented_lines;
|
||||||
|
update_comment_bufs(&buf1, &buf2, ++i);
|
||||||
|
} else if (skip_prefix(s, buf2.buf, &next)) {
|
||||||
|
off = (s > start + 1 && s[-2] == '\n') ? 1 : 0;
|
||||||
|
copy_lines(msg, start, s - start - off);
|
||||||
|
start = s - off;
|
||||||
|
s = next;
|
||||||
|
copy_lines = strbuf_add;
|
||||||
|
update_comment_bufs(&buf1, &buf2, ++i);
|
||||||
|
} else {
|
||||||
|
s = strchr(s, '\n');
|
||||||
|
if (s)
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
copy_lines(msg, start, orig_msg_len - (start - orig_msg));
|
||||||
|
free(orig_msg);
|
||||||
|
strbuf_release(&buf1);
|
||||||
|
strbuf_release(&buf2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int append_squash_message(struct strbuf *buf, const char *body,
|
||||||
|
enum todo_command command, struct replay_opts *opts,
|
||||||
|
enum todo_item_flags flag)
|
||||||
|
{
|
||||||
|
const char *fixup_msg;
|
||||||
|
size_t commented_len = 0, fixup_off;
|
||||||
|
/*
|
||||||
|
* amend is non-interactive and not normally used with fixup!
|
||||||
|
* or squash! commits, so only comment out those subjects when
|
||||||
|
* squashing commit messages.
|
||||||
|
*/
|
||||||
|
if (starts_with(body, "amend!") ||
|
||||||
|
((command == TODO_SQUASH || seen_squash(opts)) &&
|
||||||
|
(starts_with(body, "squash!") || starts_with(body, "fixup!"))))
|
||||||
commented_len = subject_length(body);
|
commented_len = subject_length(body);
|
||||||
|
|
||||||
strbuf_addf(buf, "\n%c ", comment_line_char);
|
strbuf_addf(buf, "\n%c ", comment_line_char);
|
||||||
strbuf_addf(buf, _(nth_commit_msg_fmt),
|
strbuf_addf(buf, _(nth_commit_msg_fmt),
|
||||||
++opts->current_fixup_count + 1);
|
++opts->current_fixup_count + 1);
|
||||||
strbuf_addstr(buf, "\n\n");
|
strbuf_addstr(buf, "\n\n");
|
||||||
strbuf_add_commented_lines(buf, body, commented_len);
|
strbuf_add_commented_lines(buf, body, commented_len);
|
||||||
|
/* buf->buf may be reallocated so store an offset into the buffer */
|
||||||
|
fixup_off = buf->len;
|
||||||
strbuf_addstr(buf, body + commented_len);
|
strbuf_addstr(buf, body + commented_len);
|
||||||
|
|
||||||
|
/* fixup -C after squash behaves like squash */
|
||||||
|
if (check_fixup_flag(command, flag) && !seen_squash(opts)) {
|
||||||
|
/*
|
||||||
|
* We're replacing the commit message so we need to
|
||||||
|
* append the Signed-off-by: trailer if the user
|
||||||
|
* requested '--signoff'.
|
||||||
|
*/
|
||||||
|
if (opts->signoff)
|
||||||
|
append_signoff(buf, 0, 0);
|
||||||
|
|
||||||
|
if ((command == TODO_FIXUP) &&
|
||||||
|
(flag & TODO_REPLACE_FIXUP_MSG) &&
|
||||||
|
(file_exists(rebase_path_fixup_msg()) ||
|
||||||
|
!file_exists(rebase_path_squash_msg()))) {
|
||||||
|
fixup_msg = skip_blank_lines(buf->buf + fixup_off);
|
||||||
|
if (write_message(fixup_msg, strlen(fixup_msg),
|
||||||
|
rebase_path_fixup_msg(), 0) < 0)
|
||||||
|
return error(_("cannot write '%s'"),
|
||||||
|
rebase_path_fixup_msg());
|
||||||
|
} else {
|
||||||
|
unlink(rebase_path_fixup_msg());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unlink(rebase_path_fixup_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int update_squash_messages(struct repository *r,
|
static int update_squash_messages(struct repository *r,
|
||||||
enum todo_command command,
|
enum todo_command command,
|
||||||
struct commit *commit,
|
struct commit *commit,
|
||||||
struct replay_opts *opts)
|
struct replay_opts *opts,
|
||||||
|
enum todo_item_flags flag)
|
||||||
{
|
{
|
||||||
struct strbuf buf = STRBUF_INIT;
|
struct strbuf buf = STRBUF_INIT;
|
||||||
int res;
|
int res = 0;
|
||||||
const char *message, *body;
|
const char *message, *body;
|
||||||
const char *encoding = get_commit_output_encoding();
|
const char *encoding = get_commit_output_encoding();
|
||||||
|
|
||||||
@ -1779,6 +1929,8 @@ static int update_squash_messages(struct repository *r,
|
|||||||
opts->current_fixup_count + 2);
|
opts->current_fixup_count + 2);
|
||||||
strbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len);
|
strbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len);
|
||||||
strbuf_release(&header);
|
strbuf_release(&header);
|
||||||
|
if (check_fixup_flag(command, flag) && !seen_squash(opts))
|
||||||
|
update_squash_message_for_fixup(&buf);
|
||||||
} else {
|
} else {
|
||||||
struct object_id head;
|
struct object_id head;
|
||||||
struct commit *head_commit;
|
struct commit *head_commit;
|
||||||
@ -1792,18 +1944,22 @@ static int update_squash_messages(struct repository *r,
|
|||||||
return error(_("could not read HEAD's commit message"));
|
return error(_("could not read HEAD's commit message"));
|
||||||
|
|
||||||
find_commit_subject(head_message, &body);
|
find_commit_subject(head_message, &body);
|
||||||
if (command == TODO_FIXUP && write_message(body, strlen(body),
|
if (command == TODO_FIXUP && !flag && write_message(body, strlen(body),
|
||||||
rebase_path_fixup_msg(), 0) < 0) {
|
rebase_path_fixup_msg(), 0) < 0) {
|
||||||
unuse_commit_buffer(head_commit, head_message);
|
unuse_commit_buffer(head_commit, head_message);
|
||||||
return error(_("cannot write '%s'"), rebase_path_fixup_msg());
|
return error(_("cannot write '%s'"), rebase_path_fixup_msg());
|
||||||
}
|
}
|
||||||
|
|
||||||
strbuf_addf(&buf, "%c ", comment_line_char);
|
strbuf_addf(&buf, "%c ", comment_line_char);
|
||||||
strbuf_addf(&buf, _(combined_commit_msg_fmt), 2);
|
strbuf_addf(&buf, _(combined_commit_msg_fmt), 2);
|
||||||
strbuf_addf(&buf, "\n%c ", comment_line_char);
|
strbuf_addf(&buf, "\n%c ", comment_line_char);
|
||||||
strbuf_addstr(&buf, _(first_commit_msg_str));
|
strbuf_addstr(&buf, check_fixup_flag(command, flag) ?
|
||||||
|
_(skip_first_commit_msg_str) :
|
||||||
|
_(first_commit_msg_str));
|
||||||
strbuf_addstr(&buf, "\n\n");
|
strbuf_addstr(&buf, "\n\n");
|
||||||
strbuf_addstr(&buf, body);
|
if (check_fixup_flag(command, flag))
|
||||||
|
strbuf_add_commented_lines(&buf, body, strlen(body));
|
||||||
|
else
|
||||||
|
strbuf_addstr(&buf, body);
|
||||||
|
|
||||||
unuse_commit_buffer(head_commit, head_message);
|
unuse_commit_buffer(head_commit, head_message);
|
||||||
}
|
}
|
||||||
@ -1813,8 +1969,8 @@ static int update_squash_messages(struct repository *r,
|
|||||||
oid_to_hex(&commit->object.oid));
|
oid_to_hex(&commit->object.oid));
|
||||||
find_commit_subject(message, &body);
|
find_commit_subject(message, &body);
|
||||||
|
|
||||||
if (command == TODO_SQUASH) {
|
if (command == TODO_SQUASH || check_fixup_flag(command, flag)) {
|
||||||
append_squash_message(&buf, body, opts);
|
res = append_squash_message(&buf, body, command, opts, flag);
|
||||||
} else if (command == TODO_FIXUP) {
|
} else if (command == TODO_FIXUP) {
|
||||||
strbuf_addf(&buf, "\n%c ", comment_line_char);
|
strbuf_addf(&buf, "\n%c ", comment_line_char);
|
||||||
strbuf_addf(&buf, _(skip_nth_commit_msg_fmt),
|
strbuf_addf(&buf, _(skip_nth_commit_msg_fmt),
|
||||||
@ -1825,7 +1981,9 @@ static int update_squash_messages(struct repository *r,
|
|||||||
return error(_("unknown command: %d"), command);
|
return error(_("unknown command: %d"), command);
|
||||||
unuse_commit_buffer(commit, message);
|
unuse_commit_buffer(commit, message);
|
||||||
|
|
||||||
res = write_message(buf.buf, buf.len, rebase_path_squash_msg(), 0);
|
if (!res)
|
||||||
|
res = write_message(buf.buf, buf.len, rebase_path_squash_msg(),
|
||||||
|
0);
|
||||||
strbuf_release(&buf);
|
strbuf_release(&buf);
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
@ -2026,7 +2184,8 @@ static int do_pick_commit(struct repository *r,
|
|||||||
if (command == TODO_REWORD)
|
if (command == TODO_REWORD)
|
||||||
reword = 1;
|
reword = 1;
|
||||||
else if (is_fixup(command)) {
|
else if (is_fixup(command)) {
|
||||||
if (update_squash_messages(r, command, commit, opts))
|
if (update_squash_messages(r, command, commit,
|
||||||
|
opts, item->flags))
|
||||||
return -1;
|
return -1;
|
||||||
flags |= AMEND_MSG;
|
flags |= AMEND_MSG;
|
||||||
if (!final_fixup)
|
if (!final_fixup)
|
||||||
@ -2191,10 +2350,6 @@ static int read_and_refresh_cache(struct repository *r,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum todo_item_flags {
|
|
||||||
TODO_EDIT_MERGE_MSG = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
void todo_list_release(struct todo_list *todo_list)
|
void todo_list_release(struct todo_list *todo_list)
|
||||||
{
|
{
|
||||||
strbuf_release(&todo_list->buf);
|
strbuf_release(&todo_list->buf);
|
||||||
@ -2281,6 +2436,18 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item->command == TODO_FIXUP) {
|
||||||
|
if (skip_prefix(bol, "-C", &bol) &&
|
||||||
|
(*bol == ' ' || *bol == '\t')) {
|
||||||
|
bol += strspn(bol, " \t");
|
||||||
|
item->flags |= TODO_REPLACE_FIXUP_MSG;
|
||||||
|
} else if (skip_prefix(bol, "-c", &bol) &&
|
||||||
|
(*bol == ' ' || *bol == '\t')) {
|
||||||
|
bol += strspn(bol, " \t");
|
||||||
|
item->flags |= TODO_EDIT_FIXUP_MSG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (item->command == TODO_MERGE) {
|
if (item->command == TODO_MERGE) {
|
||||||
if (skip_prefix(bol, "-C", &bol))
|
if (skip_prefix(bol, "-C", &bol))
|
||||||
bol += strspn(bol, " \t");
|
bol += strspn(bol, " \t");
|
||||||
@ -5287,6 +5454,14 @@ static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_lis
|
|||||||
short_commit_name(item->commit) :
|
short_commit_name(item->commit) :
|
||||||
oid_to_hex(&item->commit->object.oid);
|
oid_to_hex(&item->commit->object.oid);
|
||||||
|
|
||||||
|
if (item->command == TODO_FIXUP) {
|
||||||
|
if (item->flags & TODO_EDIT_FIXUP_MSG)
|
||||||
|
strbuf_addstr(buf, " -c");
|
||||||
|
else if (item->flags & TODO_REPLACE_FIXUP_MSG) {
|
||||||
|
strbuf_addstr(buf, " -C");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (item->command == TODO_MERGE) {
|
if (item->command == TODO_MERGE) {
|
||||||
if (item->flags & TODO_EDIT_MERGE_MSG)
|
if (item->flags & TODO_EDIT_MERGE_MSG)
|
||||||
strbuf_addstr(buf, " -c");
|
strbuf_addstr(buf, " -c");
|
||||||
|
Loading…
Reference in New Issue
Block a user