Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

git commit error message: cannot open (a previously deleted source file)

Tags:

Every time I commit anything in that git repository, I am getting an (apparently harmless) error message mentioning a few files that no longer exist:

$ git commit -a
error: Could not open cssrc/csgrpc/Main.cs
error: Could not open cssrc/csgrpc/Main.cs
Recorded preimage for 'cssrc/csgrpc/Main.cs'
... more 3-line groups like one above ...
[x017-upgrade a86861b] Point Audio to new location of....
 1 file changed, 1 insertion(+), 1 deletion(-)

The files were initially part of a subtree, maintained with git subtree -P cssrc ..., and I deleted them during a git subtree pull conflict, since they are not needed in this project any more (but were modified previously and committed, hence a conflict).

The files are indeed found neither in the index nor in the worktree:

$ git ls-files -cs | grep Main.cs
$ find -name 'Main.cs'
$

The rerere.enabled option is set to true. Is it a culprit? Where is the stuck pointer to these file names stored? How to clean it?

like image 731
kkm Avatar asked Dec 04 '16 05:12

kkm


People also ask

How do I commit a deletion file?

In order to remove some files from a Git commit, use the “git reset” command with the “–soft” option and specify the commit before HEAD. When running this command, you will be presented with the files from the most recent commit (HEAD) and you will be able to commit them.

What does deleted mean in git status?

As you can see, by executing the “git rm” command, a “deleted” action was added to the changes to be committed. It means that the file was removed from the filesystem but it was not deleted from the index just yet.

How do I commit to a previous commit?

The git commit --amend command is a convenient way to modify the most recent commit. It lets you combine staged changes with the previous commit instead of creating an entirely new commit. It can also be used to simply edit the previous commit message without changing its snapshot.


1 Answers

The source of the error is git's rerere or “reuse recorded resolution” feature. Since you "deleted them(your files) during a git subtree pull conflict", you inadvertently put git into an "unresolved" state. git rerere tries its very best to keep a clean conflict resolution record for reuse later on down the line. Because you deleted your files in the middle of a conflict resolution, you had a reference to those files in rerere, causing git to look for files that no longer existed. Understand that in order for git to reuse a recorded resolution, it has to record the steps involved. However, this does not mean that git is tracking the commands that you use; which explains why it doesn't know why the file that is in its conflict state, is missing from it's resolved state. It expected you to choose one version of that file or the other, not remove the file/'s entirely.

If you are interested in learning more about git rerere I recommend reading the git manual pages.

If you are interested in a working example of the problem described in this question, and a solution(I'm sure there are many more) to the problem please follow the steps listed below. I know it is quite lengthy but I don't know how else I can clarify the issue.

In a directory suitable for testing code...

  1. $ mkdir test_rerere
  2. $ cd test_rerere
  3. $ git init
  4. $ cd .git
  5. $ mkdir rr-cache
  6. $ cd ..
  7. create a file named hello.rb with the following code

    #! /usr/bin/env ruby
    
    def hello
      puts 'hello world'
    end
    
  8. $ git commit -a -m"added hello.rb"
  9. create a file named goodbye.rb with the following code

    #! /usr/bin/env ruby
    
    def bye
      puts "goodbye world"
    end
    
  10. $ git commit -m"added goodbye.rb"
  11. $ git branch i18-world
  12. Change hello to hola in hello.rb
  13. $ git commit -a -m"changed hello to hola in hello.rb"
  14. $ git checkout i18-world
  15. Change world to mundo in hello.rb
  16. $ git commit -a -m"changed world to mundo in hello.rb"
  17. Change goodbye to adios in goodbye.rb
  18. $ git commit -a -m"changed goodbye to adios in goodbye.rb"
  19. $ git checkout master
  20. Change world to mundo in goodbye.rb
  21. $ git commit -a -m"changed world to mundo in goodbye.rb" Now we have two branches, with two files that are similar, but not identical. Take a minute to look at each file as it exists in both branches.
  22. $ git status should say

    On branch master
    nothing to commit, working tree clean
    
  23. $ git merge i18-world
  24. You should get something that looks like this...

    Auto-merging hello.rb
    CONFLICT (content): Merge conflict in hello.rb
    Auto-merging goodbye.rb
    CONFLICT (content): Merge conflict in goodbye.rb
    Recorded preimage for 'goodbye.rb'
    Recorded preimage for 'hello.rb'
    Automatic merge failed; fix conflicts and then commit the result.
    
  25. You are not in a conflict state, and rerere is doing its job... notice the Recorded preimage logs above.
  26. $ git rerere status returns goodbye.rb and hello.rb, since both files are being recorded for this conflict. Not is a good time to try out git rerere diff and git ls-files -u Now, lets create an error state.
  27. First, lets resolve hello.rb to print hola mundo, and without committing, lets $ git rm goodbye.rb. This will cause your error. Have a look with $ git status to see that indeed goodbye.rb has been deleted(although git complains) and hello.rb has been modified and is ready to merge.
  28. $ git commit -a -m"HUH?"
  29. Notice that the error you get at this point is exactly the same as the error described in the question, and that we also have a HUGE clue as to what happened when that Recorded preimage bit shows up. Now let's make sure it's really broken.
  30. $ git log --oneline --decorate --graph --all to see a pretty picture of our commit history.
  31. Change hello.rb from 'hola mundo' to 'hola chica bonita!'
  32. $ git commit -a -m"changed 'hola mundo' to 'hola chica bonita!' in hello.rb"
  33. Notice that the error still shows up, and we are still getting Recorded preimage.
  34. $ git rerere status should still return goodbye.rb because it is still recorded in the preimage.
  35. $ git rerere diff returns a fatal error because goodbye.rb does not exist.
  36. For a clear picture, $ git log --oneline --decorate --graph --all
  37. Again, notice that git status and git rerere status will not return the same thing in this unresolved state. Verify for your own sanity that goodbye.rb does not exist in your working directory.
  38. Now, lets fix that pesky error message by checking out the commit that we made just before our merge. If you have been following along with your commit messages, it's the one that says "changed goodbye to adios in goodbye.rb". We will use a file specific checkout (since we don't want to mess up our work in hello.rb) with $ git checkout **yoursha1** goodbye.rb.
  39. Verify that hello.rb has not been altered. Should say 'hola chica bonita!'.
  40. The BIG FIX! git rerere ... Ta Da! Notice that git says Recorded resolution for 'goodbye.rb'.
  41. Okay, if you're not confused, Bravo! This was very confusing to me at first, but then I remembered... rerere was just itching to reuse the resolution that I had already applied. It's only complaint was that I had deleted a file out from underneath it and not given it an opportunity to catch up. Now that goodbye.rb existed again, it was more than happy to remove the reference that it had been carrying around, and live in a world where goodbye.rb did not exist. Magically, no more errors.
  42. Okay, I'll prove it to you since you're still sceptical.
  43. $ git checkout i18-world
  44. $ git rebase master
  45. $ git status returns nothing to commit, working tree clean
  46. $ ls returns "hello.rb" so we know that we are rid of that pesky goodbye.rb file.
  47. $ git log --oneline --decorate --graph --all shows us all of our hard work.
  48. $ git rerere status returns nothing because there is nothing being tracked (no preimages).
  49. $ cat hello.rb shows us

    #! /usr/bin/env ruby
    
    def hello
      puts 'hola chica bonita!'
    end
    
  50. Change hello.rb from 'hola chica bonita!' to 'hola chica bonita! te amo'.
  51. $ git commit -a -m"updated hello in hello.rb to say 'hola chica bonita! te amo"
  52. git log --oneline --decorate --graph --all to see that i18-world is ahead of master.
  53. $ git checkout master
  54. $ git merge i18-world
  55. $ git branch should return i18-world *master
  56. $ git branch -d i18-world

You can now be confident that your working directory is clean and error free. Plus if you ever need to go back to a state where 'goodbye.rb' exists, you can!

I apologize for the length of this answer, but hopefully now you understand where things went wrong, and how easy(at least in this tiny example case) it can be to fix them.

like image 106
quarterpi Avatar answered Sep 23 '22 16:09

quarterpi