38

I have one branch (let's call it B) that ignores a certain file, which isn't ignored in some other branches (eg branch A). When i switch from branch B to branch A, then back to B again, the file has been deleted.

Is this normal? I can sort of see how it would happen, in the sense that branch B thinks it's not there, and branch A thinks that it is, so when i go back to B it 'tidies it away'. But it's kind of annoying.

Any suggestions?

Sheena
  • 15,590
  • 14
  • 75
  • 113
Max Williams
  • 32,435
  • 31
  • 130
  • 197
  • Is there a reason you can't either ignore it in all branches or include it in all branches? – ewall Apr 22 '10 at 14:57
  • There's a funny problem here: if you add it to .gitignore on branch A, and then want to add it to .gitignore on branch B, you must checkout branch B, at which point the file is deleted - BEFORE you've had a chance to ignore it. You probably need a dirty .gitignore in your working directory, *containing the file* when you switch branches to avoid the file getting deleted. – Armand Apr 22 '10 at 15:41
  • @Alison G: Your case doesn't make much sense. For all this to happen, the file has to be *tracked* on branch A. You therefore wouldn't ever add it to the gitignore on branch A. The file's being removed because it's tracked on one branch, and doesn't exist on the other. The only effect ignoring it has is allowing you to go from B to A without being warned that you're overwriting a file in the work tree. – Cascabel Apr 22 '10 at 17:04
  • @Alison G - yeah i think that's exactly what happened actually. And @Jefromi yeah maybe it doesn't make much sense. I probably went about things in the wrong order.I had a file which was actually listed in .gitignore but was also being tracked. I noticed this discrepancy and removed it from the cached file list. Then i switched branches and it deleted it from the branch i switched to. – Max Williams Apr 28 '10 at 08:45
  • @ewall - no reason at all, i was going to ignore it in all branches as it happens. But it deleted it before i had a chance to. – Max Williams Apr 28 '10 at 08:55
  • What was the solution then? – itsazzad Mar 03 '15 at 13:02
  • Does anyone know if Mercurial has same issue? – Daniel Sokolowski Aug 12 '15 at 19:24

6 Answers6

5

Since the only viable solution is to track the file 'f' at all times, you could add, only in branch B, a smudge/clean process with a gitattributes filter driver:

https://raw.github.com/adisp007/ProGit/master/figures/18333fig0702-tn.png

When checkouting branch B:

  • the smudge process would change:

    • save the content of f in a temp file
    • replace the content of f with one fbis file (a tracked file which would contain, for branch B, the "B content of f")
  • the clean process would:

    • save f content (as modified in B) in fbis (fbis gets committed with f modifications in branch B)
    • restore f content (with the temp file), meaning f is not committed (ignored in B)

You can add, still in branch B, a custom merge driver which will protect fbis in case of merges from other branches to B:
the merge driver will always keep B version of fbis content, making sure the ignored file f gets back its content whenever branch B is checked-out.

Since those drivers (filter and merge) are not committed in other branches, the file 'f' is committed in other branches with all its modifications.
In branch B, its content will never change, and yet the "local modifications" made to f are still restored whenever B is checked-out.

If that content restoration is not needed, you don't need to manage fbis.
Just keep the filter driver and you will be sure that whatever modification you do to f in branch B, those changes will never be committed, effectively ignoring f content.

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 3
    wow, @VonC that is an amazing answer, thank you very much. I'm not sure i'm smart enough to understand it though...hang on, need another cup of coffee... – Max Williams Apr 28 '10 at 08:46
  • Or for each branch have two separate working directories and avoid the checkout problem completely...similar to SVN branching model which is what I did. On unrelated note an interesting history read: http://blog.red-bean.com/sussman/?p=20 – Daniel Sokolowski Sep 23 '16 at 18:29
  • @DanielSokolowski This is actually possible since Git 2.5: http://stackoverflow.com/a/39626426/6309 and http://stackoverflow.com/a/30185564/6309 – VonC Sep 23 '16 at 20:12
2

It's normal, though I'm a little surprised by one step of it.

When you switch from B to A, git sees that the file must be updated to match A's version, and does so silently because you've ignored it, saying that as far as branch B is concerned, the file doesn't matter. It has to do this - the only alternative is to refuse to check out branch A. (It would refuse if the file weren't ignored, saying "Untracked working tree file '' would be overwritten by merge". I'm actually surprised it doesn't do that in this case too. Anyone have any insight as to whether that's feature or bug?)

When you switch back to, git sees that branch B doesn't have that file, and removes it. Again, it has to do this. It has no way to read your mind and realize that that ignored file that was there a minute ago is one you want back - it simply gives you the contents of branch B, which state that the file doesn't exist.

As ewall suggests in the comments, if you want the file to survive these transitions, it needs to be tracked by all branches, or ignored in all branches.

Cascabel
  • 479,068
  • 72
  • 370
  • 318
2

Answer

If you ignore the file only in branch B but not in branch A, then you can't prevent it from becoming deleted when switching from A to B.

However, from you comments I see that you try to ignore the file in both branches, but that it still gets deleted. Its probably still tracked in A. Go to branch A and removed the ignored file from the index

git rm --cached <file>

Careful if you push branch A into your repository. This causes the file to get deleted on repository and other developers machines on their next git pull but not on your local. You may add the file after the pull on these machines.


Explanation

I had the exact same issue. Here is how it happened to me:

  1. The file test.json was once tracked in B.
  2. I created a branch A from B (so the file test.json also tracked in A).
  3. Later I ignored the file test.json in branch B. However git will continue to track any files that are already being tracked. To stop tracking the file, I removed it from the index git rm --cached foo/test.json.

After these 3 steps the following happes: If I am in branch B the file test.json exists. If I switch to A it still exists. If I checkout back to B its gone. So I noticed the same as you did:

I can sort of see how it would happen, in the sense that branch B thinks it's not there, and branch A thinks that it is, so when i go back to B it 'tidies it away'. But it's kind of annoying.

I added test.json in the .gitignore file from branch A but it still got deleted when switching back to B. And this is, because it was tracked in A before I ignored it.

Thus you need to remove it from the index in branch A. Then its ignored and untracked by A and B and it does not get deleted when you switch back from A to B.

Adam
  • 25,960
  • 22
  • 158
  • 247
1

Ignoring files in git does not mean they are out of revisioning. You can have a file checked in and managed in git and have it ignored in .gitignore.

dpc.pw
  • 3,462
  • 1
  • 19
  • 24
1

You can add the file in a folder and ignore the folder. The file does not get deleted when you switch branches.

0

I have experienced an similar situation, and i do that:

I'll call the file which deleted in checkout of index.php.

In your .gitignore of branch A and branch B remove the line which ignore your index.php, after this commit your .gitignore in both branches.

In branch A, create a backup of index.php, delete this and commit.

Restore in branch A the index.php from your backup previously created and in your .gitignore ignore again the index.php and commit this.

In your branch B add the index.php, ignore the file in your .gitignore, commit and be happy.

Matt K
  • 7,207
  • 5
  • 39
  • 60
Samuel Simões
  • 141
  • 1
  • 2
  • 13