From: "Harald Nordgren via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Harald Nordgren <haraldnordgren@gmail.com>
Subject: [PATCH v7 0/5] history: add squash subcommand to fold a range
Date: Mon, 06 Jul 2026 08:50:44 +0000 [thread overview]
Message-ID: <pull.2337.v7.git.git.1783327849.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2337.v6.git.git.1782635349.gitgitgadget@gmail.com>
Adds git history squash <revision-range> to fold a range of commits.
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 (5):
history: extract helper for a commit's parent tree
history: give commit_tree_ext a message template
history: add squash subcommand to fold a range
sequencer: extract helpers for the squash message markers
history: re-edit a squash with every message
Documentation/config/advice.adoc | 4 +
Documentation/git-history.adoc | 49 ++-
advice.c | 1 +
advice.h | 1 +
builtin/history.c | 390 +++++++++++++++++--
sequencer.c | 64 ++--
sequencer.h | 23 ++
t/meson.build | 1 +
t/t3455-history-squash.sh | 632 +++++++++++++++++++++++++++++++
9 files changed, 1099 insertions(+), 66 deletions(-)
create mode 100755 t/t3455-history-squash.sh
base-commit: e9019fcafe0040228b8631c30f97ae1adb61bcdc
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2337%2FHaraldNordgren%2Frebase-fixup-fold-v7
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2337/HaraldNordgren/rebase-fixup-fold-v7
Pull-Request: https://github.com/git/git/pull/2337
Range-diff vs v6:
1: fea6b79e60 = 1: 56ed8fadbb history: extract helper for a commit's parent tree
2: e2674e0bc4 = 2: 212e9c228f history: give commit_tree_ext a message template
3: 811e393ab4 ! 3: cf3346a1cd history: add squash subcommand to fold a range
@@ Commit message
Add "git history squash <revision-range>" to do this directly. It folds
every commit in the range into the oldest one, keeping that commit's
message and authorship and taking the tree of the newest commit, then
- replays the commits above the range on top. fixup!, squash! and amend!
- commits are folded like any other and are not interpreted, so the
- squashed message comes from the oldest commit, or from an editor with
- --reedit-message.
+ replays the commits above the range on top. The squashed message comes
+ from the oldest commit, or from an editor with --reedit-message. As that
+ message is reused, a range whose oldest commit is a fixup!, squash! or
+ amend! is refused, since the marker's target cannot be in the range.
The range is read like the arguments to "git rev-list", so several
- arguments such as "@~3.. ^topic" are allowed. A merge inside the range
- is folded when its other parent is reachable from the base, otherwise
- the range has more than one base and is rejected. By default the command
- also refuses when a ref points at a commit that the fold would discard.
- Use --update-refs=head to rewrite only the current branch instead.
+ arguments such as "HEAD~3..HEAD ^topic" are allowed. A merge inside the
+ range is folded when its other parent is reachable from the base,
+ otherwise the range has more than one base and is rejected. By default
+ the command also refuses when a ref points at a commit that the fold
+ would discard. Use --update-refs=head to rewrite only the current branch
+ instead.
Inspired-by: Sergey Chernov <serega.morph@gmail.com>
Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
@@ Documentation/git-history.adoc: SYNOPSIS
git history fixup <commit> [--dry-run] [--update-refs=(branches|head)] [--reedit-message] [--empty=(drop|keep|abort)]
git history reword <commit> [--dry-run] [--update-refs=(branches|head)]
git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--] [<pathspec>...]
-+git history squash <revision-range> [--dry-run] [--update-refs=(branches|head)] [--reedit-message]
++git history squash [--dry-run] [--update-refs=(branches|head)] [--reedit-message] <revision-range>
DESCRIPTION
-----------
+@@ Documentation/git-history.adoc: at once.
+ LIMITATIONS
+ -----------
+
+-This command does not (yet) work with histories that contain merges. You
+-should use linkgit:git-rebase[1] with the `--rebase-merges` flag instead.
++This command does not (yet) replay merge commits onto the rewritten
++history: if a commit that would be replayed is a merge, the operation is
++rejected, and you should use linkgit:git-rebase[1] with the
++`--rebase-merges` flag instead. The `squash` subcommand can still fold a
++merge that lies inside the range, as long as the range has a single base.
+
+ Furthermore, the command does not support operations that can result in merge
+ conflicts. This limitation is by design as history rewrites are not intended to
@@ Documentation/git-history.adoc: linkgit:gitglossary[7].
It is invalid to select either all or no hunks, as that would lead to
one of the commits becoming empty.
@@ Documentation/git-history.adoc: linkgit:gitglossary[7].
++
+The range is given in the usual `<base>..<tip>` form, where _<base>_ is
+the commit just below the oldest commit to squash. For example, `git
-+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. _<revision-range>_ is read like the
-+arguments to linkgit:git-rev-list[1], so several arguments may be given,
-+for example `@~3.. ^topic` to additionally exclude what is already on
-+`topic`.
++history squash HEAD~3..HEAD` folds the three most recent commits into
++one, and `git history squash HEAD~5..HEAD~2` squashes an interior range
++while leaving the two newest commits in place. _<revision-range>_ is read
++like the arguments to linkgit:git-rev-list[1], so several arguments may be
++given, for example `HEAD~3..HEAD ^topic` to additionally exclude what is
++already on `topic`.
++
+The oldest commit's message and authorship are preserved by default,
+unless you specify `--reedit-message`. A merge commit inside the range is
@@ Documentation/git-history.adoc: linkgit:gitglossary[7].
+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.
++
-+The folded commits disappear from the history, so with the default
-+`--update-refs=branches` the command refuses when another ref points at
-+one of them. Rerun with `--update-refs=head` to rewrite only the current
-+branch and leave those refs pointing at the old commits.
++Because the oldest commit's message is reused, the range may not begin
++with a `fixup!`, `squash!`, or `amend!` commit, whose target is
++necessarily outside the range.
+++
++A branch or tag that points at a commit inside the range would be left
++dangling once those commits are folded away, so with the default
++`--update-refs=branches` the command refuses. Rerun with
++`--update-refs=head` to rewrite only the current branch and leave such
++refs pointing at the old commits.
+
OPTIONS
-------
+@@ Documentation/git-history.adoc: OPTIONS
+ ref updates is generally safe.
+
+ `--reedit-message`::
+- Open an editor to modify the target commit's message.
++ Open an editor to modify the rewritten commit's message. For `squash`
++ the editor is pre-filled with the messages of all the folded commits.
+
+ `--empty=(drop|keep|abort)`::
+ Control what happens when a commit becomes empty as a result of the
## advice.c ##
@@ advice.c: static struct {
@@ builtin/history.c
#define GIT_HISTORY_SPLIT_USAGE \
N_("git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--] [<pathspec>...]")
+#define GIT_HISTORY_SQUASH_USAGE \
-+ N_("git history squash <revision-range> [--dry-run] [--update-refs=(branches|head)] [--reedit-message]")
++ N_("git history squash [--dry-run] [--update-refs=(branches|head)] [--reedit-message] <revision-range>")
static void change_data_free(void *util, const char *str UNUSED)
{
@@ builtin/history.c: out:
+ return ret;
+}
+
++static int reject_fixupish_oldest(struct repository *repo,
++ struct commit *oldest)
++{
++ const char *message, *subject;
++ int ret = 0;
++
++ message = repo_logmsg_reencode(repo, oldest, NULL, NULL);
++ find_commit_subject(message, &subject);
++ if (starts_with(subject, "fixup! ") ||
++ starts_with(subject, "squash! ") ||
++ starts_with(subject, "amend! "))
++ ret = error(_("the range begins with a fixup!, squash! or amend! "
++ "commit whose target is not in the range"));
++ repo_unuse_commit_buffer(repo, oldest, message);
++ return ret;
++}
++
+struct interior_ref_cb {
+ const struct oidset *interior;
+ const char *name;
@@ builtin/history.c: out:
+ if (ret < 0)
+ goto out;
+
++ ret = reject_fixupish_oldest(repo, oldest);
++ if (ret < 0)
++ goto out;
++
+ if (action == REF_ACTION_BRANCHES) {
+ struct interior_ref_cb cb = { .interior = &interior };
+
@@ t/t3455-history-squash.sh (new)
+
+test_expect_success 'squashes a range into a single commit without changing the tree' '
+ git reset --hard three &&
++ head_before=$(git rev-parse HEAD) &&
+ tip_tree=$(git rev-parse HEAD^{tree}) &&
+
++ git history squash --dry-run start.. >out &&
++ predicted=$(awk "/^update refs\/heads\// {print \$3}" out) &&
++ test_cmp_rev "$head_before" HEAD &&
++
+ git history squash start.. &&
+
++ test "$predicted" = "$(git rev-parse HEAD)" &&
+ git rev-list --count start..HEAD >count &&
+ echo 1 >expect &&
+ test_cmp expect count &&
@@ t/t3455-history-squash.sh (new)
+ test_cmp expect actual
+'
+
-+test_expect_success 'keeps the oldest message even if it is a fixup!' '
++test_expect_success 'refuses a range whose oldest commit is a fixup!' '
+ git reset --hard start &&
+ test_commit --no-tag "fixup! something" file b &&
-+ test_commit tail file c &&
++ test_commit --no-tag tail file c &&
++ head_before=$(git rev-parse HEAD) &&
++
++ test_must_fail git history squash start.. 2>err &&
++ test_grep "target is not in the range" err &&
++ test_cmp_rev "$head_before" HEAD
++'
++
++test_expect_success 'does not interpret squash! or amend! markers' '
++ git reset --hard start &&
++ test_commit --no-tag marker-oldest file b &&
++ git commit --allow-empty -m "squash! marker-oldest" &&
++ git commit --allow-empty -m "amend! marker-oldest" &&
++ test_commit --no-tag marker-newest file c &&
+
+ git history squash start.. &&
+
++ git rev-list --count start..HEAD >count &&
++ echo 1 >expect &&
++ test_cmp expect count &&
+ git log --format="%s" -1 >actual &&
-+ echo "fixup! something" >expect &&
++ echo marker-oldest >expect &&
+ test_cmp expect actual
+'
+
@@ t/t3455-history-squash.sh (new)
+ test_cmp expect actual
+'
+
-+test_expect_success '--dry-run predicts the rewrite without performing it' '
-+ git reset --hard three &&
-+ head_before=$(git rev-parse HEAD) &&
-+ tip_tree=$(git rev-parse HEAD^{tree}) &&
-+
-+ git history squash --dry-run start.. >out &&
-+ predicted=$(awk "/^update refs\/heads\// {print \$3}" out) &&
-+ test_cmp_rev "$head_before" HEAD &&
-+
-+ git history squash start.. &&
-+ test "$predicted" = "$(git rev-parse HEAD)" &&
-+ git rev-list --count start..HEAD >count &&
-+ echo 1 >expect &&
-+ test_cmp expect count &&
-+ test_cmp_rev start HEAD^ &&
-+ test "$tip_tree" = "$(git rev-parse HEAD^{tree})"
-+'
-+
+test_expect_success '--update-refs=head only moves HEAD' '
+ git reset --hard three &&
+ git branch -f other HEAD &&
-: ---------- > 4: 001356db93 sequencer: extract helpers for the squash message markers
4: 4edf012b77 ! 5: 615fe4dd3f history: re-edit a squash with every message
@@ Commit 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.
+ Gather the messages of every commit in the range, oldest first, and build
+ the same editor template that "git rebase -i" shows for a squash, using
+ add_squash_combination_header(), add_squash_message_header() and
+ squash_subject_comment_len(). Only the message text differs, the changes
+ are always folded in. Following autosquash, a fixup!'s message is
+ commented out in full under a "will be skipped" header, while a squash! or
+ amend! keeps its body with only the marker subject commented.
Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
## Documentation/git-history.adoc ##
-@@ Documentation/git-history.adoc: arguments to linkgit:git-rev-list[1], so several arguments may be given,
- for example `@~3.. ^topic` to additionally exclude what is already on
- `topic`.
+@@ Documentation/git-history.adoc: like the arguments to linkgit:git-rev-list[1], so several arguments may be
+ given, for example `HEAD~3..HEAD ^topic` to additionally exclude what is
+ already on `topic`.
+
-The oldest commit's message and authorship are preserved by default,
-unless you specify `--reedit-message`. A merge commit inside the range is
@@ Documentation/git-history.adoc: arguments to linkgit:git-rev-list[1], so several
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.
+ +
+ Because the oldest commit's message is reused, the range may not begin
+ with a `fixup!`, `squash!`, or `amend!` commit, whose target is
+-necessarily outside the range.
++necessarily outside the range. The changes from every commit in the range
++are always folded in. Only the message text differs. With
++`--reedit-message` the template mirrors `git rebase -i`: the message of a
++`fixup!` elsewhere in the range is commented out in full, while a
++`squash!` or `amend!` keeps its message body with only the marker subject
++commented, so you can fold the remark into the result.
+ +
+ A branch or tag that points at a commit inside the range would be left
+ dangling once those commits are folded away, so with the default
## builtin/history.c ##
@@ builtin/history.c: static int find_interior_ref(const struct reference *ref, void *cb_data)
@@ builtin/history.c: static int find_interior_ref(const struct reference *ref, voi
+ struct commit *tip,
+ struct strbuf *out)
+{
++ struct commit_list *commits = NULL, **tail = &commits, *c;
+ struct rev_info revs;
+ struct commit *commit;
+ struct strvec args = STRVEC_INIT;
-+ int n = 0, ret;
++ int n = 0, total, ret;
+
+ repo_init_revisions(repo, &revs, NULL);
+ strvec_push(&args, "ignored");
@@ builtin/history.c: static int find_interior_ref(const struct reference *ref, voi
+ goto out;
+ }
+
-+ while ((commit = get_revision(&revs))) {
++ while ((commit = get_revision(&revs)))
++ tail = &commit_list_insert(commit, tail)->next;
++ total = commit_list_count(commits);
++
++ for (c = commits; c; c = c->next) {
+ const char *message, *body;
-+ struct strbuf one = STRBUF_INIT;
++ size_t commented_len;
++ int skip;
+
-+ message = repo_logmsg_reencode(repo, commit, NULL, NULL);
++ message = repo_logmsg_reencode(repo, c->item, 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);
++ skip = starts_with(body, "fixup! ");
++ commented_len = skip ? strlen(body) :
++ squash_subject_comment_len(body, 1);
++
++ if (!n)
++ add_squash_combination_header(out, total);
+ strbuf_addch(out, '\n');
++ add_squash_message_header(out, ++n, skip);
++ strbuf_addstr(out, "\n\n");
++ strbuf_add_commented_lines(out, body, commented_len, comment_line_str);
++ strbuf_addstr(out, body + commented_len);
++ strbuf_complete_line(out);
+
-+ strbuf_release(&one);
-+ repo_unuse_commit_buffer(repo, commit, message);
++ repo_unuse_commit_buffer(repo, c->item, message);
+ }
+
+ ret = 0;
+
+out:
++ commit_list_free(commits);
+ reset_revision_walk();
+ release_revisions(&revs);
+ strvec_clear(&args);
@@ t/t3455-history-squash.sh: test_expect_success 'preserves authorship of the olde
+ test_commit re-three file d &&
+
+ write_script editor <<-\EOF &&
-+ cp "$1" buffer &&
++ cat "$1" >edited &&
+ echo combined >"$1"
+ EOF
+ test_set_editor "$(pwd)/editor" &&
+ git history squash --reedit-message start.. &&
+
-+ test_grep "re-one subject" buffer &&
-+ test_grep "re-one body line" buffer &&
-+ test_grep re-two buffer &&
-+ test_grep re-three buffer &&
-+ git log --format="%s" -1 >actual &&
++ cat >expect <<-EOF &&
++ # This is a combination of 3 commits.
++ # This is the 1st commit message:
++
++ re-one subject
++
++ re-one body line
++
++ # This is the commit message #2:
++
++ re-two
++
++ # This is the commit message #3:
++
++ re-three
++
++ # Please enter the commit message for the squash changes. Lines starting
++ # with ${SQ}#${SQ} will be ignored, and an empty message aborts the commit.
++ # Changes to be committed:
++ # modified: file
++ #
++ EOF
++ test_cmp expect edited &&
+ echo combined >expect &&
++ git log --format="%s" -1 >actual &&
+ test_cmp expect actual
+'
+
++test_expect_success '--reedit-message handles fixup!, squash! and amend! like rebase' '
++ git reset --hard start &&
++ test_commit --no-tag mark-base file b &&
++ printf "fixup! mark-base\n\nfixup body\n" >msg &&
++ echo c >file &&
++ git add file &&
++ git commit -qF msg &&
++ printf "squash! mark-base\n\nsquash remark\n" >msg &&
++ echo d >file &&
++ git add file &&
++ git commit -qF msg &&
++ printf "amend! mark-base\n\namended message\n" >msg &&
++ echo e >file &&
++ git add file &&
++ git commit -qF msg &&
++
++ write_script editor <<-\EOF &&
++ cat "$1" >edited
++ EOF
++ test_set_editor "$(pwd)/editor" &&
++ git history squash --reedit-message start.. &&
++
++ cat >expect <<-EOF &&
++ # This is a combination of 4 commits.
++ # This is the 1st commit message:
++
++ mark-base
++
++ # The commit message #2 will be skipped:
++
++ # fixup! mark-base
++ #
++ # fixup body
++
++ # This is the commit message #3:
++
++ # squash! mark-base
++
++ squash remark
++
++ # This is the commit message #4:
++
++ # amend! mark-base
++
++ amended message
++
++ # Please enter the commit message for the squash changes. Lines starting
++ # with ${SQ}#${SQ} will be ignored, and an empty message aborts the commit.
++ # Changes to be committed:
++ # modified: file
++ #
++ EOF
++ test_cmp expect edited &&
++ git log -1 --format="%B" >final &&
++ test_grep ! "fixup body" final &&
++ test_grep "squash remark" final &&
++ test_grep "amended message" final
++'
++
+test_expect_success '--reedit-message aborts on an empty message' '
+ git reset --hard three &&
+ head_before=$(git rev-parse HEAD) &&
@@ t/t3455-history-squash.sh: test_expect_success 'preserves authorship of the olde
+ test_cmp_rev "$head_before" HEAD
+'
+
- test_expect_success '--dry-run predicts the rewrite without performing it' '
+ test_expect_success '--update-refs=head only moves HEAD' '
git reset --hard three &&
- head_before=$(git rev-parse HEAD) &&
+ git branch -f other HEAD &&
--
gitgitgadget
next prev parent reply other threads:[~2026-07-06 8:50 UTC|newest]
Thread overview: 84+ 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
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
2026-06-22 11:54 ` [PATCH v4 0/4] history: add squash subcommand to fold a range Patrick Steinhardt
2026-06-23 10:41 ` Harald Nordgren
2026-06-24 21:54 ` [PATCH v5 " Harald Nordgren via GitGitGadget
2026-06-24 21:54 ` [PATCH v5 1/4] history: extract helper for a commit's parent tree Harald Nordgren via GitGitGadget
2026-06-24 21:55 ` [PATCH v5 2/4] history: give commit_tree_ext a message template Harald Nordgren via GitGitGadget
2026-06-24 21:55 ` [PATCH v5 3/4] history: add squash subcommand to fold a range Harald Nordgren via GitGitGadget
2026-06-24 21:55 ` [PATCH v5 4/4] history: re-edit a squash with every message Harald Nordgren via GitGitGadget
2026-06-26 8:52 ` [PATCH v5 0/4] history: add squash subcommand to fold a range Phillip Wood
2026-06-26 9:57 ` Harald Nordgren
2026-06-26 13:12 ` Phillip Wood
2026-06-26 14:02 ` Junio C Hamano
2026-06-26 18:36 ` Harald Nordgren
2026-06-29 6:26 ` Patrick Steinhardt
2026-06-29 15:51 ` Phillip Wood
2026-06-29 16:54 ` Junio C Hamano
2026-07-01 13:45 ` Phillip Wood
2026-06-29 18:03 ` Harald Nordgren
2026-06-29 19:48 ` Phillip Wood
2026-06-29 21:13 ` Harald Nordgren
2026-06-30 13:48 ` Phillip Wood
2026-06-30 18:38 ` Harald Nordgren
2026-07-01 10:31 ` Phillip Wood
2026-07-01 13:47 ` Junio C Hamano
2026-07-01 15:14 ` Phillip Wood
2026-07-01 17:41 ` Junio C Hamano
2026-07-02 13:58 ` Phillip Wood
2026-06-30 2:55 ` Matt Hunter
2026-06-30 7:19 ` Harald Nordgren
2026-06-30 9:23 ` Matt Hunter
2026-06-30 14:01 ` Phillip Wood
2026-07-02 12:54 ` Patrick Steinhardt
2026-07-02 20:28 ` Junio C Hamano
2026-06-29 16:09 ` Harald Nordgren
2026-06-28 8:29 ` [PATCH v6 " Harald Nordgren via GitGitGadget
2026-06-28 8:29 ` [PATCH v6 1/4] history: extract helper for a commit's parent tree Harald Nordgren via GitGitGadget
2026-06-28 8:29 ` [PATCH v6 2/4] history: give commit_tree_ext a message template Harald Nordgren via GitGitGadget
2026-06-28 8:29 ` [PATCH v6 3/4] history: add squash subcommand to fold a range Harald Nordgren via GitGitGadget
2026-06-29 5:50 ` Junio C Hamano
2026-06-28 8:29 ` [PATCH v6 4/4] history: re-edit a squash with every message Harald Nordgren via GitGitGadget
2026-06-29 5:50 ` Junio C Hamano
2026-06-29 13:49 ` Harald Nordgren
2026-06-29 14:49 ` Junio C Hamano
2026-06-29 17:38 ` Junio C Hamano
2026-07-06 8:50 ` Harald Nordgren via GitGitGadget [this message]
2026-07-06 8:50 ` [PATCH v7 1/5] history: extract helper for a commit's parent tree Harald Nordgren via GitGitGadget
2026-07-06 8:50 ` [PATCH v7 2/5] history: give commit_tree_ext a message template Harald Nordgren via GitGitGadget
2026-07-06 8:50 ` [PATCH v7 3/5] history: add squash subcommand to fold a range Harald Nordgren via GitGitGadget
2026-07-06 8:50 ` [PATCH v7 4/5] sequencer: extract helpers for the squash message markers Harald Nordgren via GitGitGadget
2026-07-06 8:50 ` [PATCH v7 5/5] history: re-edit a squash with every message Harald Nordgren via GitGitGadget
2026-07-06 14:06 ` [PATCH v7 0/5] history: add squash subcommand to fold a range Phillip Wood
2026-07-07 7:51 ` Harald Nordgren
2026-07-06 20:42 ` Junio C Hamano
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=pull.2337.v7.git.git.1783327849.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--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