mirror of
https://github.com/git/git.git
synced 2026-06-19 15:39:47 +02:00
history: re-edit a squash with every message
By default "git history squash" reuses the oldest commit's message. When --reedit-message is given it only reopened that one message, so the messages of the folded-in commits were lost. Gather the messages of every commit in the range, oldest first, and use them as the editor template when re-editing, mirroring how "git rebase -i" presents a squash. The combined message is built before the descendant walk so it is not disturbed by the flags that walk leaves on the commits. Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
committed by
Junio C Hamano
parent
08ae39413a
commit
4fcf3481b2
@@ -111,8 +111,9 @@ history squash @~3..` folds the three most recent commits into one, and
|
||||
`git history squash @~5..@~2` squashes an interior range while leaving
|
||||
the two newest commits in place.
|
||||
+
|
||||
The oldest commit's message and authorship are preserved by default,
|
||||
unless you specify `--reedit-message`. A merge commit inside the range is
|
||||
The oldest commit's message and authorship are preserved by default. With
|
||||
`--reedit-message`, an editor opens pre-filled with the messages of all the
|
||||
folded commits so you can combine them. A merge commit inside the range is
|
||||
folded like any other, but the range must have a single base, so a range
|
||||
that reaches more than one entry point (for example a side branch that
|
||||
forked before the range and was later merged into it) is rejected.
|
||||
|
||||
+60
-1
@@ -1047,6 +1047,56 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int build_squash_message(struct repository *repo,
|
||||
struct commit *base,
|
||||
struct commit *tip,
|
||||
struct strbuf *out)
|
||||
{
|
||||
struct rev_info revs;
|
||||
struct commit *commit;
|
||||
struct strvec args = STRVEC_INIT;
|
||||
int n = 0, ret;
|
||||
|
||||
repo_init_revisions(repo, &revs, NULL);
|
||||
strvec_push(&args, "ignored");
|
||||
strvec_push(&args, "--reverse");
|
||||
strvec_push(&args, "--topo-order");
|
||||
strvec_pushf(&args, "%s..%s", oid_to_hex(&base->object.oid),
|
||||
oid_to_hex(&tip->object.oid));
|
||||
setup_revisions_from_strvec(&args, &revs, NULL);
|
||||
|
||||
if (prepare_revision_walk(&revs) < 0) {
|
||||
ret = error(_("error preparing revisions"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
while ((commit = get_revision(&revs))) {
|
||||
const char *message, *body;
|
||||
struct strbuf one = STRBUF_INIT;
|
||||
|
||||
message = repo_logmsg_reencode(repo, commit, NULL, NULL);
|
||||
find_commit_subject(message, &body);
|
||||
strbuf_addstr(&one, body);
|
||||
strbuf_trim_trailing_newline(&one);
|
||||
|
||||
if (n++)
|
||||
strbuf_addch(out, '\n');
|
||||
strbuf_addbuf(out, &one);
|
||||
strbuf_addch(out, '\n');
|
||||
|
||||
strbuf_release(&one);
|
||||
repo_unuse_commit_buffer(repo, commit, message);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
reset_revision_walk();
|
||||
release_revisions(&revs);
|
||||
strvec_clear(&args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cmd_history_squash(int argc,
|
||||
const char **argv,
|
||||
const char *prefix,
|
||||
@@ -1071,6 +1121,7 @@ static int cmd_history_squash(int argc,
|
||||
OPT_END(),
|
||||
};
|
||||
struct strbuf reflog_msg = STRBUF_INIT;
|
||||
struct strbuf message = STRBUF_INIT;
|
||||
struct commit *base, *oldest, *tip, *rewritten;
|
||||
const struct object_id *base_tree_oid, *tip_tree_oid;
|
||||
struct commit_list *parents = NULL;
|
||||
@@ -1091,6 +1142,12 @@ static int cmd_history_squash(int argc,
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (flags & COMMIT_TREE_EDIT_MESSAGE) {
|
||||
ret = build_squash_message(repo, base, tip, &message);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = setup_revwalk(repo, action, tip, &revs);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@@ -1099,7 +1156,8 @@ static int cmd_history_squash(int argc,
|
||||
tip_tree_oid = &repo_get_commit_tree(repo, tip)->object.oid;
|
||||
commit_list_append(base, &parents);
|
||||
|
||||
ret = commit_tree_ext(repo, "squash", oldest, NULL, parents,
|
||||
ret = commit_tree_ext(repo, "squash", oldest,
|
||||
message.len ? message.buf : NULL, parents,
|
||||
base_tree_oid, tip_tree_oid, &rewritten, flags);
|
||||
if (ret < 0) {
|
||||
ret = error(_("failed writing squashed commit"));
|
||||
@@ -1120,6 +1178,7 @@ static int cmd_history_squash(int argc,
|
||||
|
||||
out:
|
||||
strbuf_release(&reflog_msg);
|
||||
strbuf_release(&message);
|
||||
commit_list_free(parents);
|
||||
release_revisions(&revs);
|
||||
return ret;
|
||||
|
||||
@@ -135,6 +135,43 @@ test_expect_success 'preserves authorship of the oldest commit' '
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '--reedit-message offers every folded-in message' '
|
||||
git reset --hard start &&
|
||||
echo b >file &&
|
||||
git add file &&
|
||||
git commit -m "re-one subject" -m "re-one body line" &&
|
||||
test_commit re-two file c &&
|
||||
test_commit re-three file d &&
|
||||
|
||||
write_script editor <<-\EOF &&
|
||||
cp "$1" buffer &&
|
||||
echo combined >"$1"
|
||||
EOF
|
||||
test_set_editor "$(pwd)/editor" &&
|
||||
git history squash --reedit-message start.. &&
|
||||
|
||||
grep "re-one subject" buffer &&
|
||||
grep "re-one body line" buffer &&
|
||||
grep re-two buffer &&
|
||||
grep re-three buffer &&
|
||||
git log --format="%s" -1 >actual &&
|
||||
echo combined >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '--reedit-message aborts on an empty message' '
|
||||
git reset --hard three &&
|
||||
head_before=$(git rev-parse HEAD) &&
|
||||
|
||||
write_script editor <<-\EOF &&
|
||||
>"$1"
|
||||
EOF
|
||||
test_set_editor "$(pwd)/editor" &&
|
||||
test_must_fail git history squash --reedit-message start.. &&
|
||||
|
||||
test_cmp_rev "$head_before" HEAD
|
||||
'
|
||||
|
||||
test_expect_success '--dry-run predicts the rewrite without performing it' '
|
||||
git reset --hard three &&
|
||||
head_before=$(git rev-parse HEAD) &&
|
||||
|
||||
Reference in New Issue
Block a user