Git Merge: error: unable to unlink old <file>: No such file or directory
Asked Answered
N

3

8

I'm pulling a branch into master in order to merge it.

Command I'm running is (while having master checked out): git pull origin feature/some_branch

Unfortunately it seems my colleagues have performed some (what I would think benign) file deletions on there, and now git spews out a error: unable to unlink old 'somefile': No such file or directory.

I've tried to look online but most of the references for this error concern permissions, which is not the case here.

The file in question is not present on master before the merge, while is it in the new branch.

The problem is that master wasn't updated in a long time so there are way too many changes and files affected for me to start figuring the code out.

I just need master to contain all the changes that came from the new branch. We never commit anything to master directly, always through merges.

What I've tried so far:

  • Using --force parameter, same issue
  • git reset origin/master --hard and running the pull again, same issue

How can I update master with another, more recent branch without caring for such issues, and while keeping its history?

Nonillion answered 18/7, 2018 at 9:48 Comment(4)
Have you tried doing a git merge with master before trying to pull in the changes from your feature branch?Erhart
@JacobSievers same issue, I'm using pull because I can --force, which is not possible with mergeNonillion
Do you have local commits on master that you want to preserve, or is it okay to completely overwrite your master with the version in origin/master?Handcuff
@RoryO'Kane master is free from any commits, we never commit to it directly... only through merges. And yes, free to completely overwrite from originNonillion
P
4

I just need master to contain all the changes that came from the new branch.

Then you can force the merge, in order to reflect the content of that branch feature/some_branch.

But instead of using merge --ours master, you can use a similar idea, which preserve the first-parent history:

git checkout master

# make merge commit but without conflicts!!
# the contents of 'ours' will be discarded later
git merge -s ours feature/some_branch    

# make temporary branch to merged commit
git branch tmp         

# get contents of working tree and index to the one of feature/some_branch
git reset --hard feature/some_branch

# reset to our merged commit but 
# keep contents of working tree and index
git reset --soft tmp

# change the contents of the merged commit
# with the contents of feature/some_branch
git commit --amend

# get rid off our temporary branch
git branch -D tmp

# verify that the merge commit contains only contents of feature/some_branch
git diff HEAD feature/some_branch
Pelasgian answered 20/7, 2018 at 16:8 Comment(5)
Doesn't this change the first parent history away from master's history?Cilice
@YazeedSabri I believe so. For a solution which preserve the first parent: https://mcmap.net/q/13248/-is-there-a-quot-theirs-quot-version-of-quot-git-merge-s-ours-quotPelasgian
The first part of your solution updates feature/some_branch with the contents of master first if I understand this correctly, but I'd rather avoid that. I'll give the 2nd part of your answer a goNonillion
@Pelasgian So I've ran the commands in the 2nd part of your answer and it did the trick. Could you ideally rephrase your answer so that that part is more prominent ? I'll mark it as accepted and deliver the bounty :)Nonillion
@Nonillion Sorry, for the late update: I was away. I have rewritten the answer accordingly.Pelasgian
S
0

To replace your master branch with the version on origin/master, you could try deleting and recreating it. master is not required to be checked out while you do this, so you might be able to avoid errors relating to changing your working directory.

First check out a different branch that you are able to check out:

git checkout feature/some-branch

Then delete your local master branch and create it again, including its tracking information:

git branch --delete master
git branch master origin/master
git branch --set-upstream-to=origin/master master

Finally, try switching to the new master:

git branch checkout master
Spat answered 20/7, 2018 at 23:57 Comment(3)
That doesn't keep master's history though, which I'd like to keep. Also local & remotes are all up to date with their counterparts. I think deleting master always sounds wrong in any situation to be honest :dNonillion
@Nonillion I don't understand what you're asking then. I thought your comment replying to me said that this is what you wanted…Handcuff
Well master and origin/master are essentially the same right ? So overwriting my local master with its remote is fine (actually they should always be in sync, and right now they already are). What I want to avoid though is to lose the commit history on master. Maybe we both mean something else with origin/master ?Nonillion
D
0

I came across this question because I had a similar issue. While the other answers already address possible fixes, I think I've found a cause.

In my situation, the conflict arose because of different file name case conventions. If you author the commits on a platform which has case-insensitive file names (Windows, macOS) and you change the case of the file name (whether intentionally or in my situation due to generated code), Git may use the new name in future commits but the O/S will not regard the file as having changed in any way, and Git seems to defer to the O/S on this matter.

This can result in bizarre situations, such as a file foo.txt getting deleted by some commit even though it never existed in the first place, because the previous commit to touch it considered it to be called Foo.txt instead. If you apply the commits in order on a case-insensitive platform, you won't see a problem. However, if you apply the commits in order on a case-sensitive platform (e.g., Linux), you will get the unable to unlink error in the question. You can skip past the problem by hard resetting ahead to the new commit or cloning the repo fresh at its latest state, but the "bad" commits will still be in the history lurking.

This should probably be considered a bug in Git, even though it's not Git's fault per se, because it's too trusting of the O/S.

Distinctly answered 19/1 at 18:56 Comment(1)
I'm actually not confident about the third paragraph. I can repro this issue consistently but closer inspection of the commits shows no contradictions in the history. It definitely manifests from crossing platforms (seen inside and outside of a devcontainer on macOS).Distinctly

© 2022 - 2024 — McMap. All rights reserved.