How to fix git subtree rev-parse split issue

112 views Asked by At

I am using SourceTree to interact with branches. Also, my git knowledge is extremely limited

I have a NAV-482 branch (/nav.git repository). Subtree branch is NAV-482-DLL (/nav.dll.git repository). Subtree name (and local relative path) is nav.dll

I have run into issue when I can pull changes from my subtree without any problem, but when I'm trying to push changes into it, I am failing with the followings error:

git push using: nav.dll.git NAV-482-DLL
fatal: remote error: upload-pack: not our ref 4fb3fa1d8661a6294e798912d39f3ffbbe60a9dd
fatal: the remote end hung up unexpectedly
fatal: could not rev-parse split hash 4fb3fa1d8661a6294e798912d39f3ffbbe60a9dd from commit 872b637c702ca5f45b41bdecbded976b47313b23

Jumping to the commit 872b637c702ca5f45b41bdecbded976b47313b23 shows that this one is back from 2017 where 4fb3fa1d8661a6294e798912d39f3ffbbe60a9dd is git-subtree-split

Not sure why push suddenly choose to die in that way, but now it is a problem.

From what I could find about this issue there are potentially multiple solutions:

Git push subtree fail unknown revision or path possibly due to missing git-subtree-split commit suggest using git fetch <subtree rep> <hash> to get the commit in question. Unfortunalety it fails:

$ git fetch nav.dll.git 4fb3fa1d8661a6294e798912d39f3ffbbe60a9dd
fatal: remote error: upload-pack: not our ref 4fb3fa1d8661a6294e798912d39f3ffbbe60a9dd
fatal: the remote end hung up unexpectedly

As I understood this means that this commit is no longer in the repository

Removing invalid git-subtree-split hash suggest to use git subtree split --ignore-joins --rejoin and the persons seems to managed resolve the problem.

This one I am reluctant to try because to be honest, I straight up don't understand what this command will do. As I understood it should look something like git subtree split -P NAV-482-DLL --ignore-joins --rejoin -b NAV-482-TEMP but what exactly is an expected result? Will it make a separate branch, or will it make a commit into a current checked out NAV-482 branch?

Is the a way to resolve the issue with the SourceTree UI or if there not, how should a proper git subtree split command look, or should I seek for another solution?

1

There are 1 answers

10
VonC On

As I illustrated before, make sure to use Git 2.39+ (which includes a bug fix). And consider making a backup of your repository to prevent any accidental loss of data.

Make sure you are on the correct branch (NAV-482) in your main repository (/nav.git).

git switch NAV-482

Execute the git subtree split command to recreate the subtree history.
The command you mentioned should be correct:

git subtree split -P NAV-482-DLL --ignore-joins --rejoin -b NAV-482-TEMP
  • -P NAV-482-DLL: specifies the prefix (path) of the subtree.
  • --ignore-joins: ignores join commits created by previous subtree merges.
  • --rejoin: creates a new join commit after the split.
  • -b NAV-482-TEMP: creates a new branch with the split subtree.

That command will create a new branch (NAV-482-TEMP) in your main repo with the history of the changes made to the subtree.

Push the newly created branch to the subtree repository.

git push nav.dll.git NAV-482-TEMP:NAV-482-DLL

The git subtree split essentially extracts the history of a subfolder (in your case, nav.dll) and puts it into a new branch. The --ignore-joins and --rejoin options help in cleaning and maintaining the correct history.
If some of these operations might not be available or straightforward in the SourceTree UI, particularly the specific options of git subtree. You might need to use the command line for these operations.


As noted by Olegs Jasjko in the comments, you can add a squash option:

git subtree split -P NAV-482-DLL --ignore-joins --rejoin --squash -b NAV-482-TEMP 

With this command, all the changes in the NAV-482-DLL subtree will be squashed into a single commit on the new NAV-482-TEMP branch. This means the NAV-482-TEMP branch will have a simplified history, with one commit representing the entire history of changes from the subtree.

This approach is particularly useful when you want to maintain a cleaner, more concise history in the branch that is being created. It reduces the number of commits and makes it easier to understand the overall changes made in the subtree over time.


I suppose a git reflog show --all | grep 4fb3fa1d8661a6294e798912d39f3ffbbe60a9dd would return nothing...

If recreating the missing data is not feasible or necessary, you might need to consider methods to bypass this commit in their operations. That could involve adjusting the subtree split to exclude the range involving the missing commit.

Determine the range of commits that are necessary for the subtree split, excluding the missing commit. That involves identifying the commit just before the missing one and the commit just after (or a later commit that is present in the repository).

Generate a series of patches for the commits that you want to include in the subtree. That can be done using git format-patch. Apply these patches to a new branch, effectively recreating the history without the missing commit.

git switch -c new-branch <commit-before-missing>
git format-patch <commit-before-missing>..<commit-after-missing> --stdout | git apply

Alternatively, you can cherry-pick individual commits from the range you identified, again excluding the missing commit. That method is more manual but offers more control over which commits are included.

git switch -c new-branch <commit-before-missing>
# Cherry-pick commits individually or in a range, excluding the missing one
git cherry-pick <commit-after-missing>