pr-git-2337/HaraldNordgren/rebase-fixup-fold-v12
tagged this
04 Aug 08:30
Adds git history squash <revision-range> to fold a range of commits.
Changes in v12:
* Incorporated fixups from Phillip:
* Reworks range validation into a single walk that rejects roots and
multiple tips while preserving every parent when squashing into a
merge.
* Resolves fixup!, squash! and amend! targets directly, preserving
message intent and safely consolidating related markers.
* Builds the editor template from the exact selected revisions, including
exclusions, while retaining --no-edit behavior.
* Protects descendant local branches while leaving tags and
remote-tracking refs unchanged.
Changes in v11:
* Make message editing the default with the autosquash-style template, add
--no-edit instead of squash-specific --reedit-message.
* Validate one actual boundary and tip, rejecting root-reaching and
multi-tip ranges.
* Protect only interior local branches, leaving tags and remote-tracking
refs unchanged.
* Move sequencer preparation before squash and fold the later
message-editing patch into the feature commit.
Changes in v10:
* Record the full revision expression in squash reflog.
* Preserve the boundary-walk invariant when sanitizing rev-list options.
* Clarify amend! and --reedit-message documentation.
Changes in v9:
* Use the last amend! targeting the oldest folded commit as the default
squashed message. Ignore amend! markers targeting later commits while
selecting that replacement message.
* Improve tests.
Changes in v8:
* --reedit-message now builds the same editor template as git rebase -i
--autosquash: fixup!, squash! and amend! commits are grouped under the
commit they target instead of shown in commit order, and an amend!
replaces its target's message.
* A fixup!, squash! or amend! is refused only when its target is outside
the range, so several fixups for an in-range commit fold together. A
range that is entirely markers for one below-range target is combined
into a single commit, keeping the last amend! message.
* Merges inside the range are folded when the range has a single base, with
no dedicated opt-in flag, --ancestry-path ensures only commits descended
from the base are folded, and a range reaching more than one base is
rejected.
* Rev-list options are accepted and sanitized the way git replay does,
forcing the walk order back with a warning, which also fixes git history
squash -- --reverse slipping past the previous option check.
* Kept this as an explicit squash subcommand rather than making
--reedit-message the default or renaming the command.
Changes in v7:
* --reedit-message now builds the same editor template git rebase -i shows
for a squash (a combination of N commits banner with each folded message
under its own header) and follows autosquash for markers: a fixup!
message falls out (commented under a will be skipped header), while a
squash! or amend! keeps its body with only the marker subject commented
so its remark can be reworded in. Only the message text is affected,
every commit's changes are always folded in.
* Reuse git rebase -i's squash-message code: a preparatory sequencer:
commit extracts the banner, header and marker-comment helpers so both
rebase and git history squash build the identical template from one
source.
* Refuse a range whose oldest commit is a fixup!, squash! or amend!, since
the marker's target cannot be inside the range.
* Reorder the squash usage so dashed options come before <revision-range>,
and spell out HEAD instead of @ in the documentation and examples.
* Expand the squash commit message and documentation with this overview,
and scope the merge limitation so it no longer contradicts squash folding
a single-base interior merge.
Changes in v6:
* git history squash now accepts multiple revision arguments, read like the
arguments to git-rev-list, so a compound range such as @~3.. ^topic
works.
* The base to reparent onto is now the oldest in-range commit's parent; a
boundary other than that base means the range has more than one base and
is rejected. This also fixes the earlier overly-restrictive handling of
merges and side branches.
* A single-commit range (e.g. @^!) is rejected with "nothing to squash"
(this also covers the @^!-style example that previously succeeded
silently).
* Commit messages reworded: the squash commit now gives an overview of
fixup!/squash!/amend! handling, rewording, merge-parent and ref behavior.
Changes in v5:
* The range walk now uses --ancestry-path, so only commits descended from
the base are folded; a single revision such as HEAD or HEAD~1 is now
rejected as "not a <base>..<tip> range" rather than treated as a squash
down to the root.
* This adopts the --ancestry-path suggestion; the multi-base rejection is
unchanged, so a side branch that forked before the base and merged in is
still refused.
* Added tests covering more merge topologies: two interior merges, a nested
merge, an octopus merge, an octopus arm forked before the base, a merge
among the descendants replayed above the range, and a ref pointing at an
interior merge commit.
Changes in v4:
* git history squash now detects when another ref points at a commit inside
the range being folded and refuses, with an advice.historyUpdateRefs hint
to use --update-refs=head.
* A merge inside the range is folded fine as long as the range has a single
base; a range with merge commit at the tip or base also folds correctly.
Only a range with more than one base is rejected.
Changes in v3:
* Moved the feature out of git rebase and into a new git history squash
<revision-range> subcommand, per the list discussion. git rebase --squash
is dropped.
* Takes an arbitrary range (git history squash @~3.., git history squash
@~5..@~2), folding it into the oldest commit and replaying any
descendants on top.
* Implemented as a single tree operation rather than picking each commit,
so there are no repeated conflict stops (addresses Phillip's efficiency
point).
* A merge inside the range is folded fine, only a range with more than one
base is rejected.
* --reedit-message seeds the editor with every folded-in message, not just
the oldest.
Harald Nordgren (4):
history: extract helper for a commit's parent tree
history: give commit_tree_ext a message template
sequencer: share the squash message marker helpers and flags
history: add squash subcommand to fold a range
Documentation/config/advice.adoc | 4 +
Documentation/git-history.adoc | 59 ++-
advice.c | 1 +
advice.h | 1 +
builtin/history.c | 685 ++++++++++++++++++++++--
object.h | 1 +
sequencer.c | 70 +--
sequencer.h | 30 ++
t/meson.build | 1 +
t/t3455-history-squash.sh | 884 +++++++++++++++++++++++++++++++
10 files changed, 1665 insertions(+), 71 deletions(-)
create mode 100755 t/t3455-history-squash.sh
base-commit: 5b2471720c93ee30e5764a19f3d3b3ae9ec9712a
Submitted-As: https://lore.kernel.org/git/pull.2337.v12.git.git.1785832251.gitgitgadget@gmail.com
In-Reply-To: https://lore.kernel.org/git/pull.2337.git.git.1781465141.gitgitgadget@gmail.com
In-Reply-To: https://lore.kernel.org/git/pull.2337.v2.git.git.1781512625.gitgitgadget@gmail.com
In-Reply-To: https://lore.kernel.org/git/pull.2337.v3.git.git.1781810226.gitgitgadget@gmail.com
In-Reply-To: https://lore.kernel.org/git/pull.2337.v4.git.git.1782021195.gitgitgadget@gmail.com
In-Reply-To: https://lore.kernel.org/git/pull.2337.v5.git.git.1782338102.gitgitgadget@gmail.com
In-Reply-To: https://lore.kernel.org/git/pull.2337.v6.git.git.1782635349.gitgitgadget@gmail.com
In-Reply-To: https://lore.kernel.org/git/pull.2337.v7.git.git.1783327849.gitgitgadget@gmail.com
In-Reply-To: https://lore.kernel.org/git/pull.2337.v8.git.git.1783674396.gitgitgadget@gmail.com
In-Reply-To: https://lore.kernel.org/git/pull.2337.v9.git.git.1784128573.gitgitgadget@gmail.com
In-Reply-To: https://lore.kernel.org/git/pull.2337.v10.git.git.1784536024.gitgitgadget@gmail.com
In-Reply-To: https://lore.kernel.org/git/pull.2337.v11.git.git.1785567209.gitgitgadget@gmail.com