From: Patrick Steinhardt <ps@pks.im>
To: Harald Nordgren via GitGitGadget <gitgitgadget@gmail.com>
Cc: git@vger.kernel.org, Harald Nordgren <haraldnordgren@gmail.com>
Subject: Re: [PATCH v3 3/4] history: add squash subcommand to fold a range
Date: Fri, 19 Jun 2026 14:55:43 +0200 [thread overview]
Message-ID: <ajU8T2JFJTdk1hr2@pks.im> (raw)
In-Reply-To: <66b2f49fb427c7328136b2d440dc7461b97fb4e0.1781810227.git.gitgitgadget@gmail.com>
On Thu, Jun 18, 2026 at 07:17:05PM +0000, Harald Nordgren via GitGitGadget wrote:
> diff --git a/builtin/history.c b/builtin/history.c
> index 305bde3102..9d9416870f 100644
> --- a/builtin/history.c
> +++ b/builtin/history.c
> @@ -973,6 +975,156 @@ out:
> return ret;
> }
>
> +/*
> + * Resolve a "<base>..<tip>" revision range into the base commit just outside
> + * the range (which becomes the parent of the squashed commit), the oldest
> + * commit contained in the range (whose message the squash reuses), and the
> + * range tip (whose tree becomes the result). A merge inside the range is fine,
> + * but the range must have a single base and must not reach a root commit.
> + */
> +static int resolve_squash_range(struct repository *repo,
> + const char *range,
> + struct commit **base_out,
> + struct commit **oldest_out,
> + struct commit **tip_out)
> +{
> + struct rev_info revs;
> + struct commit *commit, *base = NULL, *oldest = NULL, *tip = NULL;
> + struct strvec args = STRVEC_INIT;
> + int ret;
> +
> + repo_init_revisions(repo, &revs, NULL);
> + strvec_push(&args, "ignored");
> + strvec_push(&args, "--reverse");
> + strvec_push(&args, "--topo-order");
> + strvec_push(&args, "--boundary");
> + strvec_push(&args, range);
We don't have any kind of input verification for "range". So in theory,
the user could pass whatever string here, and this may or may not work.
Also, should we use "--ancestry-path" with the first commit of the range
here? Otherwise we may incldue commits that aren't descendants of A in a
range "A..B". If not I wonder whether we might see multiple boundaries
even though we would be able to resolve the boundary unambiguously in
some cases.
> + setup_revisions_from_strvec(&args, &revs, NULL);
> + if (args.nr != 1) {
> + ret = error(_("'%s' does not name a revision range"), range);
> + goto out;
> + }
> +
> + if (prepare_revision_walk(&revs) < 0) {
> + ret = error(_("error preparing revisions"));
> + goto out;
> + }
> +
> + while ((commit = get_revision(&revs))) {
> + if (commit->object.flags & BOUNDARY) {
> + if (base) {
> + ret = error(_("range '%s' has more than one base; "
> + "cannot squash"), range);
> + goto out;
> + }
> + base = commit;
> + continue;
> + }
> + if (!oldest)
> + oldest = commit;
> + tip = commit;
> + }
Hmm. I really wonder whether we should also restrict merges. It might be
somewhat obvious that intermediate merge commits should just be
discarded. But is that equally obvious for HEAD and the base commit?
> + if (!oldest) {
> + ret = error(_("the range '%s' is empty"), range);
> + goto out;
> + }
> +
> + if (!base) {
> + ret = error(_("cannot squash the root commit"));
> + goto out;
> + }
In theory we can by squashing onto an empty tree. But it's fine to not
care about this edge case, we can still address it at a later point in
time if we ever feel the need to.
> + *base_out = base;
> + *oldest_out = oldest;
> + *tip_out = tip;
> + 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,
> + struct repository *repo)
> +{
> + const char * const usage[] = {
> + GIT_HISTORY_SQUASH_USAGE,
> + NULL,
> + };
> + enum ref_action action = REF_ACTION_DEFAULT;
> + enum commit_tree_flags flags = 0;
> + int dry_run = 0;
> + struct option options[] = {
> + OPT_CALLBACK_F(0, "update-refs", &action, "(branches|head)",
> + N_("control which refs should be updated"),
> + PARSE_OPT_NONEG, parse_ref_action),
> + OPT_BOOL('n', "dry-run", &dry_run,
> + N_("perform a dry-run without updating any refs")),
> + OPT_BIT(0, "reedit-message", &flags,
> + N_("open an editor to modify the commit message"),
> + COMMIT_TREE_EDIT_MESSAGE),
> + OPT_END(),
> + };
> + struct strbuf reflog_msg = STRBUF_INIT;
> + struct commit *base, *oldest, *tip, *rewritten;
> + const struct object_id *base_tree_oid, *tip_tree_oid;
> + struct commit_list *parents = NULL;
> + struct rev_info revs = { 0 };
> + int ret;
> +
> + argc = parse_options(argc, argv, prefix, options, usage, 0);
> + if (argc != 1) {
> + ret = error(_("command expects a single revision range"));
> + goto out;
> + }
> + repo_config(repo, git_default_config, NULL);
> +
> + if (action == REF_ACTION_DEFAULT)
> + action = REF_ACTION_BRANCHES;
> +
> + ret = resolve_squash_range(repo, argv[0], &base, &oldest, &tip);
> + if (ret < 0)
> + goto out;
> +
> + ret = setup_revwalk(repo, action, tip, &revs);
> + if (ret < 0)
> + goto out;
Oh, you already use `setup_revwalk()` here. Wouldn't that keep us from
accepting merge commits?
Patrick
next prev parent reply other threads:[~2026-06-19 12:55 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-14 19:25 [PATCH 0/2] rebase: add --fixup to fold a range into its oldest commit Harald Nordgren via GitGitGadget
2026-06-14 19:25 ` [PATCH 1/2] t3415: remove prepare-commit-msg hook after use Harald Nordgren via GitGitGadget
2026-06-14 19:25 ` [PATCH 2/2] rebase: add --fixup-all to fold a range Harald Nordgren via GitGitGadget
2026-06-15 2:01 ` [PATCH 0/2] rebase: add --fixup to fold a range into its oldest commit Junio C Hamano
2026-06-15 8:18 ` Harald Nordgren
2026-06-15 15:17 ` D. Ben Knoble
2026-06-16 8:34 ` Patrick Steinhardt
2026-06-17 9:30 ` Harald Nordgren
2026-06-15 8:37 ` [PATCH v2 0/2] rebase: add --squash to fold a range into its first commit Harald Nordgren via GitGitGadget
2026-06-15 8:37 ` [PATCH v2 1/2] t3415: remove prepare-commit-msg hook after use Harald Nordgren via GitGitGadget
2026-06-15 8:37 ` [PATCH v2 2/2] rebase: add --squash to fold a range Harald Nordgren via GitGitGadget
2026-06-16 10:10 ` [PATCH v2 0/2] rebase: add --squash to fold a range into its first commit Phillip Wood
2026-06-17 9:11 ` Harald Nordgren
2026-06-17 9:48 ` Phillip Wood
2026-06-18 19:17 ` [PATCH v3 0/4] history: add squash subcommand to fold a range Harald Nordgren via GitGitGadget
2026-06-18 19:17 ` [PATCH v3 1/4] history: extract helper for a commit's parent tree Harald Nordgren via GitGitGadget
2026-06-18 19:17 ` [PATCH v3 2/4] history: give commit_tree_ext a message template Harald Nordgren via GitGitGadget
2026-06-18 19:17 ` [PATCH v3 3/4] history: add squash subcommand to fold a range Harald Nordgren via GitGitGadget
2026-06-18 20:30 ` Junio C Hamano
2026-06-18 21:24 ` Junio C Hamano
2026-06-18 21:29 ` D. Ben Knoble
2026-06-19 12:55 ` Patrick Steinhardt [this message]
2026-06-18 19:17 ` [PATCH v3 4/4] history: re-edit a squash with every message Harald Nordgren via GitGitGadget
2026-06-18 21:23 ` [PATCH v3 0/4] history: add squash subcommand to fold a range D. Ben Knoble
2026-06-19 0:34 ` Junio C Hamano
2026-06-19 12:37 ` Patrick Steinhardt
2026-06-19 16:11 ` Junio C Hamano
2026-06-21 5:53 ` [PATCH v4 " Harald Nordgren via GitGitGadget
2026-06-21 5:53 ` [PATCH v4 1/4] history: extract helper for a commit's parent tree Harald Nordgren via GitGitGadget
2026-06-21 5:53 ` [PATCH v4 2/4] history: give commit_tree_ext a message template Harald Nordgren via GitGitGadget
2026-06-21 5:53 ` [PATCH v4 3/4] history: add squash subcommand to fold a range Harald Nordgren via GitGitGadget
2026-06-21 5:53 ` [PATCH v4 4/4] history: re-edit a squash with every message Harald Nordgren via GitGitGadget
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ajU8T2JFJTdk1hr2@pks.im \
--to=ps@pks.im \
--cc=git@vger.kernel.org \
--cc=gitgitgadget@gmail.com \
--cc=haraldnordgren@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox