From: george@mail.dietrich.pub
To: ask+git@howdoi.land
Cc: george@mail.dietrich.pub, git@vger.kernel.org
Subject: Re: [Bug] Git subtree regression
Date: Sun, 4 Jan 2026 09:27:26 -0500 [thread overview]
Message-ID: <20260104142733.2334796-1-george@mail.dietrich.pub> (raw)
In-Reply-To: <e25b4d76-c1b5-4b6b-ba77-e1e2f7243ce9@howdoi.land>
---
Ahh, yes. It seems you also need to add `clock` as a remote and fetch it:
```
$ git clone git@github.com:athena-framework/athena.git
$ cd athena
$ git remote add clock git@github.com:athena-framework/clock.git
$ git fetch clock
$ git subtree split --prefix="src/components/clock"
0efb3d9858e3bfee65165508aeeacc50417c9a99
```
I wasn't able to use _exactly_ 2.43.7, but I was able to use 2.43.0 which would still be before that other change.
It also produced the expected commit hash, unlike 2.52.0.
It, also was significantly faster, took ~9s vs 2.51.1 which was ~26s.
2.52.0 was better at ~14s, but of course produces the wrong hash.
This reproduces the issue quite well, and what the root cause likely.
It does seem one component was added differently, as a non-merge commit, which seems break things.
Looking at the Athena monorepo, this can somewhat be confirmed via https://github.com/athena-framework/athena/commits/master/?after=ee21a41e9dfc969e759b532d45c0c0faa21876d6+0.
How the first two commits show up as verified, unlike the other times when I normally do `git subtree add --squash` and push directly to main, they show up as unverified.
```
#!/bin/bash
#
# THE BUG:
# When a commit's direct parent is a squash commit for a DIFFERENT subtree,
# and that squash commit's ancestry includes OUR subtree's squash commit,
# the split breaks.
#
# Old code: `git log -1 --grep` searches ancestry, finds our marker → don't ignore
# New code: only checks parent's own trailers → ignores → breaks parent chain
#
# This pattern occurs when subtree squash commits are cherry-picked or rebased
# into a linear history (instead of the normal merge structure).
set -e
KEEP_TMPDIR="${KEEP_TMPDIR:-}"
TMPDIR=$(mktemp -d)
echo "Working directory: $TMPDIR"
cleanup() {
if [ -n "$KEEP_TMPDIR" ]; then
echo "Preserving temp directory: $TMPDIR"
else
rm -rf "$TMPDIR"
fi
}
trap cleanup EXIT
create_repo() {
local repo="$1"
git init -b main "$repo"
git -C "$repo" config user.email "test@test.com"
git -C "$repo" config user.name "Test User"
git -C "$repo" config log.date relative
}
create_commit() {
local repo="$1"
local name="$2"
(
cd "$repo"
mkdir -p "$(dirname "$name")"
echo "$name" > "$name"
git add "$name"
git commit -m "$name"
)
}
cd "$TMPDIR"
echo "=== Creating repositories ==="
create_repo monorepo
create_repo subA
create_repo subB
echo "=== Creating upstream commits ==="
create_commit subA subA1
create_commit subA subA2
create_commit subB subB1
create_commit subB subB2
echo "=== Setting up monorepo with linear squash structure ==="
# Initial commit
create_commit monorepo main1
# Add subA with --squash (normal way - creates merge)
git -C monorepo fetch ../subA HEAD
git -C monorepo subtree add --prefix=subA --squash FETCH_HEAD
# Make a change in subA
create_commit monorepo subA/change1
# Now we simulate cherry-picking JUST the squash commit for subB
# (This is what seems to have happened in the athena repo)
# First, get subB ready
git -C monorepo fetch ../subB HEAD
# Create a LINEAR squash commit for subB (simulating cherry-pick of just the squash commit)
# This is the key pattern that triggers the bug - a squash commit as a regular linear commit
(
cd monorepo
mkdir -p subB
git -C ../subB archive HEAD | tar -x -C subB
git add subB
# Create a squash-style commit with subtree trailers but as a LINEAR commit
# Trailers must be in the last paragraph, separated by blank line
subB_short=$(git -C ../subB rev-parse --short HEAD)
subB_full=$(git -C ../subB rev-parse HEAD)
git commit -F - <<EOF
Squashed 'subB/' content from commit $subB_short
git-subtree-dir: subB
git-subtree-split: $subB_full
EOF
)
echo ""
echo "=== Key structure: subB squash is a LINEAR commit, not a merge ==="
git -C monorepo log -1 --format='%H %s' HEAD
echo "Parent count: $(git -C monorepo cat-file -p HEAD | grep -c '^parent')"
# Now make a commit that touches subA
# This commit's parent is the subB squash commit (linear)
create_commit monorepo subA/change2
echo ""
echo "=== Repository structure ==="
git -C monorepo log --oneline --graph
# Verify the squash commit's ancestry includes subA's marker
subB_squash=$(git -C monorepo rev-parse HEAD^)
echo ""
echo "=== Checking ancestry of subB squash commit ($subB_squash) ==="
echo "Looking for subA marker in ancestry..."
if git -C monorepo log -1 --grep="git-subtree-dir: subA" "$subB_squash" --oneline 2>/dev/null; then
echo " FOUND - old code would search this and NOT ignore"
else
echo " NOT FOUND - test setup may be incomplete"
fi
echo ""
echo "=== Running subtree split on subA ==="
split_hash=$(git -C monorepo subtree split --prefix=subA 2>/dev/null)
echo "Split hash: $split_hash"
split_count=$(git -C monorepo rev-list --count "$split_hash")
echo "Commits in split: $split_count"
echo ""
echo "=== Split history ==="
git -C monorepo log --oneline "$split_hash"
echo ""
echo "=== Result ==="
# Expected: 4 commits (2 upstream + 2 local changes)
if [ "$split_count" -ge 4 ]; then
echo "PASS: Split produced connected history ($split_count commits)"
exit 0
else
echo "FAIL: Split produced disconnected history (only $split_count commits, expected >= 4)"
exit 1
fi
```
next prev parent reply other threads:[~2026-01-04 14:28 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-12-26 19:58 [Bug] Git subtree regression dev
2025-12-30 17:07 ` george
2026-01-04 4:52 ` Colin Stagner
2026-01-04 14:27 ` george [this message]
2026-01-05 3:36 ` Colin Stagner
2026-01-06 4:55 ` george
2026-01-10 1:25 ` Colin Stagner
2026-01-10 17:22 ` george
2026-02-15 20:36 ` Colin Stagner
2026-02-16 21:25 ` D. Ben Knoble
2026-02-18 4:29 ` george
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=20260104142733.2334796-1-george@mail.dietrich.pub \
--to=george@mail.dietrich.pub \
--cc=ask+git@howdoi.land \
--cc=git@vger.kernel.org \
/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