Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

git checkout --ours when file spec includes deleted file

When we merge we keep the local version of our Maven pom.xml files:

git merge origin/remote_branch
git checkout --ours **/pom.xml pom.xml
git add **/pom.xml pom.xml
git commit -m "Merge"

This works great except if a pom.xml file has been removed in the local branch. After running command #2 above we get an error:

d:\code>git checkout --ours **/pom.xml pom.xml
error: path 'blah/pom.xml' does not have our version

... and after this error the next command #3 git add **/pom.xml pom.xml effectively adds the remote pom.xml files - exactly what we don't want.

How can we update our script to handle this?

like image 212
Marcus Leon Avatar asked Apr 21 '17 21:04

Marcus Leon


People also ask

What is deleted by us in git status?

'deleted by us' means the file is deleted in the commit which you are trying to do a cherry-pick. It is not file is deleted by you. Git tells that the file was deleted in some other commit, and allows you to decide to retain it (git add) or to remove it. You can do git cherry-pick --continue once you sort this out.

What is us in rebasing?

When you rebase, us refers the upstream branch, and them is the branch you're moving about. It's a bit counter-intuitive in case of a rebase.

Does git merge delete files?

If a file is modified in one branch and moved in another branch, Git has no way to propagate the changes from the original file to the moved copy of the file. If a file is deleted in one branch and isn't modified in the other, the file is deleted during the merge.


1 Answers

How to solve the error error: path 'some/file' does not have our version after running command git checkout --ours **/some_file2.xml some_file2.xml.

1.A. As a human, here are the steps

As a human, you need to do the following. Let's assume you ran the following, as I explain and recommend here:

git checkout --ours -- path/to/some/dir

...and it didn't work! It didn't do anything. Instead, it output these errors:

error: path 'path/to/some/dir/file1.cpp' does not have our version
error: path 'path/to/some/dir/file2.cpp' does not have our version
error: path 'path/to/some/dir/file3.cpp' does not have our version

The problem is that these are deleted files on the our side, so we must git rm each of them manually from our working tree (working file system), to manually force our working tree to match the our side for these files:

git rm path/to/some/dir/file1.cpp
git rm path/to/some/dir/file2.cpp
git rm path/to/some/dir/file3.cpp

# OR (same thing)
git rm path/to/some/dir/file1.cpp path/to/some/dir/file2.cpp \
path/to/some/dir/file3.cpp

Now, re-run your checkout --ours command and it will work just fine!:

git checkout --ours -- path/to/some/dir

Works! Done.

1.B. To script the above process, it's a little harder, but here is how

Let's script that stuff above. There are undoubtedly many ways to do this, but here's the easiest way I could find:

# 1. attempt to run `git checkout --ours` the first time,
# collecting any filenames which errored out, if any, and 
# `git rm` them all.
git checkout --ours -- path/to/some/dir \
|& gawk '{ print $3 }' | xargs git rm

# 2. Now run it again. If it worked the first time above already, 
# no big deal--running it again causes no problems. If it failed
# above though, the above command just ran `git rm` on all those
# failed files, so now this time it will succeed!
git checkout --ours -- path/to/some/dir

Done! You could also store the output from the first attempt into a file as well, of course, and only run the 2nd attempt if the first attempt failed (meaning the output is not an empty string), but I'll leave that up to you.

Sample output: By git rming your deleted files, you'll see the following output (the first line here contains the actual command after the $ char):

$ git checkout --ours -- path/to/some/dir |& gawk '{ print $3 }' | xargs git rm
path/to/some/dir/file1.cpp: needs merge
path/to/some/dir/file2.cpp: needs merge
path/to/some/dir/file3.cpp: needs merge
rm 'path/to/some/dir/file1.cpp'
rm 'path/to/some/dir/file2.cpp'
rm 'path/to/some/dir/file3.cpp'

Explanation of git checkout --ours -- path/to/some/dir |& gawk '{ print $3 }' | xargs git rm:

  1. git checkout --ours -- path/to/some/dir accepts all the merge conflicts from the --ours side (read more in my answer here: Who is "us" and who is "them" according to Git?).
  2. |& pipes both the stderr output as well as the stdout output, since the error messages that may be printed out by the git command are to stderr and that's what we need to pipe.
  3. gawk '{ print $3 }'prints only the 3rd space-separated field of each row, which means it captures the 'path/to/some/dir/file1.cpp' part of error: path 'path/to/some/dir/file1.cpp' does not have our version, for instance.
  4. | xargs git rm pipes all of those files to git rm to "git remove" them.

2. Finishing up

And now you can add these auto-fixed-up files and continue the process:

git add path/to/some/dir 
git status 

# Use the appropriate one of these based on whatever operation 
# you were in at the time when the conflicts happened.
git merge --continue 
git rebase --continue
git revert --continue
# etc.

References:

  1. For awk/gawk:
    1. My git-diffn.sh "git diff with line numbers" script (I can never remember awk syntax so I just look at previous known examples, including my own).
    2. https://en.wikipedia.org/wiki/AWK
    3. Official GNU AWK user guide
  2. Using | xargs git rm: Git rm several files?
  3. Using |& to pipe both stdout and stderr: Piping both stdout and stderr in bash?
  4. Why use 'git rm' to remove a file instead of 'rm'?
like image 163
Gabriel Staples Avatar answered Sep 21 '22 00:09

Gabriel Staples