Skip to content

Commit 66f4b98

Browse files
jaysoffiangitster
authored andcommitted
Teach merge the '[-e|--edit]' option
Implemented internally instead of as "git merge --no-commit && git commit" so that "merge --edit" is otherwise consistent (hooks, etc) with "merge". Note: the edit message does not include the status information that one gets with "commit --status" and it is cleaned up after editing like one gets with "commit --cleanup=default". A later patch could add the status information if desired. Note: previously we were not calling stripspace() after running the prepare-commit-msg hook. Now we are, stripping comments and leading/trailing whitespace lines if --edit is given, otherwise only stripping leading/trailing whitespace lines if not given --edit. Signed-off-by: Jay Soffian <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 703f05a commit 66f4b98

File tree

3 files changed

+99
-39
lines changed

3 files changed

+99
-39
lines changed

Documentation/merge-options.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ With --no-commit perform the merge but pretend the merge
77
failed and do not autocommit, to give the user a chance to
88
inspect and further tweak the merge result before committing.
99

10+
--edit::
11+
-e::
12+
+
13+
Invoke editor before committing successful merge to further
14+
edit the default merge message.
15+
1016
--ff::
1117
--no-ff::
1218
Do not generate a merge commit if the merge resolved as

builtin/merge.c

Lines changed: 70 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ static const char * const builtin_merge_usage[] = {
4646

4747
static int show_diffstat = 1, shortlog_len, squash;
4848
static int option_commit = 1, allow_fast_forward = 1;
49-
static int fast_forward_only;
49+
static int fast_forward_only, option_edit;
5050
static int allow_trivial = 1, have_message;
5151
static struct strbuf merge_msg;
5252
static struct commit_list *remoteheads;
@@ -190,6 +190,8 @@ static struct option builtin_merge_options[] = {
190190
"create a single commit instead of doing a merge"),
191191
OPT_BOOLEAN(0, "commit", &option_commit,
192192
"perform a commit if the merge succeeds (default)"),
193+
OPT_BOOLEAN('e', "edit", &option_edit,
194+
"edit message before committing"),
193195
OPT_BOOLEAN(0, "ff", &allow_fast_forward,
194196
"allow fast-forward (default)"),
195197
OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
@@ -832,30 +834,54 @@ static void add_strategies(const char *string, unsigned attr)
832834

833835
}
834836

835-
static void write_merge_msg(void)
837+
static void write_merge_msg(struct strbuf *msg)
836838
{
837839
int fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
838840
if (fd < 0)
839841
die_errno(_("Could not open '%s' for writing"),
840842
git_path("MERGE_MSG"));
841-
if (write_in_full(fd, merge_msg.buf, merge_msg.len) != merge_msg.len)
843+
if (write_in_full(fd, msg->buf, msg->len) != msg->len)
842844
die_errno(_("Could not write to '%s'"), git_path("MERGE_MSG"));
843845
close(fd);
844846
}
845847

846-
static void read_merge_msg(void)
848+
static void read_merge_msg(struct strbuf *msg)
847849
{
848-
strbuf_reset(&merge_msg);
849-
if (strbuf_read_file(&merge_msg, git_path("MERGE_MSG"), 0) < 0)
850+
strbuf_reset(msg);
851+
if (strbuf_read_file(msg, git_path("MERGE_MSG"), 0) < 0)
850852
die_errno(_("Could not read from '%s'"), git_path("MERGE_MSG"));
851853
}
852854

853-
static void run_prepare_commit_msg(void)
855+
static void write_merge_state(void);
856+
static void abort_commit(const char *err_msg)
854857
{
855-
write_merge_msg();
858+
if (err_msg)
859+
error("%s", err_msg);
860+
fprintf(stderr,
861+
_("Not committing merge; use 'git commit' to complete the merge.\n"));
862+
write_merge_state();
863+
exit(1);
864+
}
865+
866+
static void prepare_to_commit(void)
867+
{
868+
struct strbuf msg = STRBUF_INIT;
869+
strbuf_addbuf(&msg, &merge_msg);
870+
strbuf_addch(&msg, '\n');
871+
write_merge_msg(&msg);
856872
run_hook(get_index_file(), "prepare-commit-msg",
857873
git_path("MERGE_MSG"), "merge", NULL, NULL);
858-
read_merge_msg();
874+
if (option_edit) {
875+
if (launch_editor(git_path("MERGE_MSG"), NULL, NULL))
876+
abort_commit(NULL);
877+
}
878+
read_merge_msg(&msg);
879+
stripspace(&msg, option_edit);
880+
if (!msg.len)
881+
abort_commit(_("Empty commit message."));
882+
strbuf_release(&merge_msg);
883+
strbuf_addbuf(&merge_msg, &msg);
884+
strbuf_release(&msg);
859885
}
860886

861887
static int merge_trivial(void)
@@ -869,7 +895,7 @@ static int merge_trivial(void)
869895
parent->next = xmalloc(sizeof(*parent->next));
870896
parent->next->item = remoteheads->item;
871897
parent->next->next = NULL;
872-
run_prepare_commit_msg();
898+
prepare_to_commit();
873899
commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL);
874900
finish(result_commit, "In-index merge");
875901
drop_save();
@@ -897,9 +923,9 @@ static int finish_automerge(struct commit_list *common,
897923
for (j = remoteheads; j; j = j->next)
898924
pptr = &commit_list_insert(j->item, pptr)->next;
899925
}
900-
free_commit_list(remoteheads);
901926
strbuf_addch(&merge_msg, '\n');
902-
run_prepare_commit_msg();
927+
prepare_to_commit();
928+
free_commit_list(remoteheads);
903929
commit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL);
904930
strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
905931
finish(result_commit, buf.buf);
@@ -1005,6 +1031,36 @@ static int setup_with_upstream(const char ***argv)
10051031
return i;
10061032
}
10071033

1034+
static void write_merge_state(void)
1035+
{
1036+
int fd;
1037+
struct commit_list *j;
1038+
struct strbuf buf = STRBUF_INIT;
1039+
1040+
for (j = remoteheads; j; j = j->next)
1041+
strbuf_addf(&buf, "%s\n",
1042+
sha1_to_hex(j->item->object.sha1));
1043+
fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666);
1044+
if (fd < 0)
1045+
die_errno(_("Could not open '%s' for writing"),
1046+
git_path("MERGE_HEAD"));
1047+
if (write_in_full(fd, buf.buf, buf.len) != buf.len)
1048+
die_errno(_("Could not write to '%s'"), git_path("MERGE_HEAD"));
1049+
close(fd);
1050+
strbuf_addch(&merge_msg, '\n');
1051+
write_merge_msg(&merge_msg);
1052+
fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666);
1053+
if (fd < 0)
1054+
die_errno(_("Could not open '%s' for writing"),
1055+
git_path("MERGE_MODE"));
1056+
strbuf_reset(&buf);
1057+
if (!allow_fast_forward)
1058+
strbuf_addf(&buf, "no-ff");
1059+
if (write_in_full(fd, buf.buf, buf.len) != buf.len)
1060+
die_errno(_("Could not write to '%s'"), git_path("MERGE_MODE"));
1061+
close(fd);
1062+
}
1063+
10081064
int cmd_merge(int argc, const char **argv, const char *prefix)
10091065
{
10101066
unsigned char result_tree[20];
@@ -1409,33 +1465,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
14091465

14101466
if (squash)
14111467
finish(NULL, NULL);
1412-
else {
1413-
int fd;
1414-
struct commit_list *j;
1415-
1416-
for (j = remoteheads; j; j = j->next)
1417-
strbuf_addf(&buf, "%s\n",
1418-
sha1_to_hex(j->item->object.sha1));
1419-
fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666);
1420-
if (fd < 0)
1421-
die_errno(_("Could not open '%s' for writing"),
1422-
git_path("MERGE_HEAD"));
1423-
if (write_in_full(fd, buf.buf, buf.len) != buf.len)
1424-
die_errno(_("Could not write to '%s'"), git_path("MERGE_HEAD"));
1425-
close(fd);
1426-
strbuf_addch(&merge_msg, '\n');
1427-
write_merge_msg();
1428-
fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666);
1429-
if (fd < 0)
1430-
die_errno(_("Could not open '%s' for writing"),
1431-
git_path("MERGE_MODE"));
1432-
strbuf_reset(&buf);
1433-
if (!allow_fast_forward)
1434-
strbuf_addf(&buf, "no-ff");
1435-
if (write_in_full(fd, buf.buf, buf.len) != buf.len)
1436-
die_errno(_("Could not write to '%s'"), git_path("MERGE_MODE"));
1437-
close(fd);
1438-
}
1468+
else
1469+
write_merge_state();
14391470

14401471
if (merge_was_ok) {
14411472
fprintf(stderr, _("Automatic merge went well; "

t/t7600-merge.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,4 +643,27 @@ test_expect_success 'amending no-ff merge commit' '
643643

644644
test_debug 'git log --graph --decorate --oneline --all'
645645

646+
cat >editor <<\EOF
647+
#!/bin/sh
648+
# Add a new message string that was not in the template
649+
(
650+
echo "Merge work done on the side branch c1"
651+
echo
652+
cat <"$1"
653+
) >"$1.tmp" && mv "$1.tmp" "$1"
654+
# strip comments and blank lines from end of message
655+
sed -e '/^#/d' < "$1" | sed -e :a -e '/^\n*$/{$d;N;ba' -e '}' > expected
656+
EOF
657+
chmod 755 editor
658+
659+
test_expect_success 'merge --no-ff --edit' '
660+
git reset --hard c0 &&
661+
EDITOR=./editor git merge --no-ff --edit c1 &&
662+
verify_parents $c0 $c1 &&
663+
git cat-file commit HEAD >raw &&
664+
grep "work done on the side branch" raw &&
665+
sed "1,/^$/d" >actual raw &&
666+
test_cmp actual expected
667+
'
668+
646669
test_done

0 commit comments

Comments
 (0)