git unable to merge unrelated histories shallow clone

Issue

I have shallow cloned a repo with depth 1 and create a local dev branch for development. After raising the PR, I have received merge conflicts, So I have pulled the latest master and tried to merge it with the dev branch.

Approach 1:

  1. When I try to pull the branch, it says fatal: refusing to merge unrelated histories
  2. So, I deleted the local master and used switch to create a new branch git switch -c master origin/master
  3. Now when I try to merge the master branch to local to resolve merge conflicts I am getting the error fatal: refusing to merge unrelated histories

Approach 2:

  1. Same till step 2 in Approach 1.
  2. I have tried to cherry pick the commit that caused my merge conflicts. Since the repo is shallow cloned it does not have the commit history and throws error bad object.

I have tried –allow-unrelated-histories, there were so many merge conflicts that had to be hand fixed.

How to resolve this issue and merge master to my dev branch?

Solution

Never1 use --allow-unrelated-histories. Instead, deepen (or "unshallow") your clone. Edit: as per comments, you also need to convert your clone to a full clone (it’s currently a single-branch clone, which I should have anticipated since using --depth during cloning defaults to creating a single-branch clone as well).

Merging, in Git, depends on three commits, not two:

  • there is your current commit (aka HEAD), which will represent work done since some common starting point;
  • there is some other commit, which will represent work done in some other branch since the same common starting point; and
  • there is a third commit, the merge base, which is this common starting point.

When you run git merge, the argument you supply—generally a branch name like feature or master—locates the middle of these three: the "other" or --theirs commit. The current or --ours commit is implied by what you have checked out. The third commit, the common starting point, Git finds on its own. But Git needs the history to find this commit.

History, in a Git repository, is nothing more or less than the commits in the repository. A normal, non-shallow clone has all the commits,2 so it has all the history. The point of a shallow clone is to deliberately omit some history in the name of expediency. For some purposes, this is fine (and hence actually expedient). For merging it’s not fine; don’t do it.

If you have an existing shallow clone, e.g., because your CI system made one, consider running:

git remote set-branches origin "*"
git fetch --unshallow

The first command converts the clone to a standard full clone (undoing the single-branch-ness). Note that you may be able to type in the * without quotes, but using the quotes like this should always work.

The second command redirects the usual git fetch operation such that it removes the shallowness. If this proves to use too much in the way of system resources, you can alternatively use git fetch --depth or git fetch --deepen to incrementally add more commits to the shallow repository until you have enough history—enough commits—for git merge to locate the common starting point. The problem with this approach is that there’s no guarantee how deep that depth might be, which means there’s no correct way to choose the right depth. This in turn implies a loop, where you deepen the depth repeatedly until it’s "enough", and that uses more and more system resources each time, which is presumably what you were trying to avoid in the first place through shallow cloning.

Most CI systems that do make shallow clones offer you a way to force a full clone in the first place (which is overall more efficient than making a shallow clone and then un-shallowing it). So if the shallow clone is the problem, as in this case, just don’t do it in the first place.


1Rule for advanced Git users: Never use --allow-unrelated-histories until you’ve proven somehow that it’s OK.

2Remember, the core of a git clone operation is copy all the commits, but none of the branch names. Git doesn’t need branch names to function, but it does need commits. We—humans—don’t like using Git without branch names, so we always have Git create some, although CI systems sometimes don’t bother with them; we use branch names and/or remote-tracking names (which remember some other repository’s branch names) to find the interesting commits. Git can use these as well, but can use the raw hash IDs directly, which is how those CI systems work.

Answered By – torek

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published