git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: git-subtree split misbehaviour with a commit having empty ls-tree for the specified subdir

From
Tom Clarkson <tqclarkson@icloud.com>
Date
Dec 18, 2019, 00:17 UTC
Message-ID
<D4C58338-10C6-4E5A-BF1F-F48EC2EBDAD5@icloud.com>
In-Reply-To
<CAPyFy2AsmaxU-BDf_teZJE5hiaVpTSZc8fftnuXPb_4-j7j5Fw@mail.gmail.com>
Show 46 quoted lines
> On 23 Nov 2019, at 3:55 am, Ed Maste <emaste@freebsd.org> wrote:
> 
> I encountered an issue while trying to use git subtree with the
> FreeBSD svn->git mirror: I found that when "git subtree split"
> encounters a commit with an empty "git ls-tree" for the subdirectory
> being split, it ends up recording the original parent as the new
> parent in the split history that's being created. This then leads to
> unrelated history appearing in the split subtree.
> 
> Below is a shell script that demonstrates the issue - this is not the
> precise case that I encountered in the FreeBSD repo, but the behaviour
> is identical (and it doesn't take nearly 10 minutes to run). Running
> the script and then "git log" of the commit printed by the final (git
> subtree) command includes the unrelated history in dir2/.
> 
> It looks like this comes from the cache_set "$rev" "$rev" in
> process_split_commit() added in 39f5fff0d53. This is under the
> suspicious-looking "ugly. is there no better way to tell if this is a
> subtree vs. a mainline commit? Does it matter" comment. However, I
> don't yet understand enough of git-subtree's operation to propose a
> fix.
> 
> --repro.sh--
> #!/bin/sh
> 
> rm -rf subrepo-issue
> mkdir -p subrepo-issue
> cd subrepo-issue
> 
> git init .
> mkdir -p dir1 dir2
> touch dir1/file1 dir2/file2
> git add dir1 dir2
> git commit -m 'initial commit'
> echo 'file2' > dir2/file2
> git commit -m 'file2 modified' dir2/file2
> git rm dir1/file1
> git commit -m 'remove file1'
> mkdir -p dir1
> touch dir1/file1
> git add dir1
> git commit -m 'restore file1'
> echo 'file1' > dir1/file1
> git commit -m 'file1 modified' dir1/file1
> git subtree split --prefix=dir1/
> 
The algorithm I am looking at to replace the file based mainline detection is
 - If subtree root is unknown (as on the initial split), everything is mainline.
 - If subtree root is reachable and mainline root is not, it’s a subtree commit 
 - Otherwise, treat as mainline. This will also pick up commits from other subtrees but they hopefully won’t contain the subtree folder. I don’t think there is an unambiguous way to distinguish a subtree merge from a regular merge - the message produced is pretty generic. It may be possible to check reachability of all known subtrees, but that adds a fair bit of complexity.
That leaves us with the question of how to record the empty mainline commits. The most correct result for your repro is probably four commits (add/delete everything/restore/modify), but I can see that falling over in a scenario where deleting a subtree is more like unlinking a library than editing that library to do nothing.
Is it sufficiently correct for your scenario to treat ‘restore file1’ as the initial subtree commit?
Previous: Ed MasteNext: Ed Maste
Message 2 of 10 in “git-subtree split misbehaviour with a commit having empty ls-tree for the specified subdir”
  1. Ed MasteNov 22, 2019
  2. Tom ClarksonDec 18, 2019
  3. Ed MasteDec 18, 2019
  4. Tom ClarksonDec 19, 2019
  5. Ed MasteDec 20, 2019
  6. Tom ClarksonDec 22, 2019
  7. Ed MasteJan 21, 2020
  8. Ed MasteJun 17, 2020
  9. Tom ClarksonJun 18, 2020
  10. Ed MasteApr 28, 2020

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.