{"thread":{"id":"65566","subject":"[PATCH] subtree: allow duplicate cache entries with same value","startedAt":"2026-04-29T14:20:12Z","lastAt":"2026-04-29T14:20:12Z","messageCount":1,"participants":["Eric Wendland"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"542478","messageId":"20260429141550.167194-2-eric@wendland.dev","threadId":"65566","inReplyTo":null,"subject":"[PATCH] subtree: allow duplicate cache entries with same value","fromName":"Eric Wendland","fromEmail":"eric@wendland.dev","sentAt":"2026-04-29T14:15:51Z","receivedAt":"2026-04-29T14:20:12Z","isPatch":true,"body":"When a subtree is added, removed, and later re-added at the same path,\n'git subtree split' fails with:\n\n    fatal: cache for <hash> already exists!\n\nThis happens because 'find_existing_splits()' scans history and calls\n'cache_set()' for each prior split. When the same subtree commit was\nadded twice at different mainline commits, both map to the same subtree\ncommit. The second 'cache_set()' call tries to write the same mapping\nagain, but the old code treated any existing cache entry as a fatal error.\n\nFix 'cache_set()' to silently succeed when the existing cache entry\nalready contains the same value we're trying to write.\n\nAdd a regression test for this scenario.\n\nSigned-off-by: Eric Wendland <eric@wendland.dev>\nCloses: <1d8fa22c-eb13-4cdb-8499-e19f0dea6b42@tionis.dev>\n---\n contrib/subtree/git-subtree.sh     |  5 +++++\n contrib/subtree/t/t7900-subtree.sh | 29 +++++++++++++++++++++++++++++\n 2 files changed, 34 insertions(+)\n\ndiff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh\nindex 791fd8260c..f0f2a47d53 100755\n--- a/contrib/subtree/git-subtree.sh\n+++ b/contrib/subtree/git-subtree.sh\n@@ -343,6 +343,11 @@ cache_set () {\n \t\ttest \"$oldrev\" != \"latest_new\" &&\n \t\ttest -e \"$cachedir/$oldrev\"\n \tthen\n+\t\tread oldcache <\"$cachedir/$oldrev\"\n+\t\tif test \"$oldcache\" = \"$newrev\"\n+\t\tthen\n+\t\t\treturn\n+\t\tfi\n \t\tdie \"fatal: cache for $oldrev already exists!\"\n \tfi\n \techo \"$newrev\" >\"$cachedir/$oldrev\"\ndiff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh\nindex 18d2b56448..85fe4362fb 100755\n--- a/contrib/subtree/t/t7900-subtree.sh\n+++ b/contrib/subtree/t/t7900-subtree.sh\n@@ -528,6 +528,35 @@ do\n \t'\n done\n \n+test_expect_success 'split after add, remove, and re-add at same path' '\n+\tsubtree_test_create_repo \"$test_count\" &&\n+\tsubtree_test_create_repo \"$test_count/sub proj\" &&\n+\ttest_create_commit \"$test_count\" main1 &&\n+\ttest_create_commit \"$test_count/sub proj\" sub1 &&\n+\t(\n+\t\tcd \"$test_count\" &&\n+\t\tgit fetch ./\"sub proj\" HEAD &&\n+\t\tgit subtree add --prefix=\"sub dir\" FETCH_HEAD\n+\t) &&\n+\ttest_create_commit \"$test_count\" \"sub dir\"/main-sub1 &&\n+\t(\n+\t\tcd \"$test_count\" &&\n+\t\tgit rm -rf \"sub dir\" &&\n+\t\tgit commit -m \"remove sub dir\"\n+\t) &&\n+\t(\n+\t\tcd \"$test_count\" &&\n+\t\tgit fetch ./\"sub proj\" HEAD &&\n+\t\tgit subtree add --prefix=\"sub dir\" FETCH_HEAD\n+\t) &&\n+\ttest_create_commit \"$test_count\" \"sub dir\"/main-sub2 &&\n+\t(\n+\t\tcd \"$test_count\" &&\n+\t\tgit subtree split --prefix=\"sub dir\" --branch=subproj-br &&\n+\t\ttest \"$(git rev-parse --verify subproj-br)\"\n+\t)\n+'\n+\n # Usually,\n #\n #    git subtree merge -P subA --squash f00...\n-- \n2.47.3\n\n"}]}