Search code examples
gitbranchgitignoregit-checkout

Preserve ignored untracked files across checkout


I picked up an external project, which I will be developing from now on. As a failsafe measure, I made 2 branches:

  • One without a .gitignore, so as to not lose untracked files, called gitignoreless.
  • One with my guess at the right ignore pattern, called master.

Turns out I guessed wrong, and I need to track some of them.

I expected that checking out master after checking out gitignoreless would preserve the ignored files, but it's not so.

Are they now tracked just because they are tracked in a single branch?
Is there another reason that gets them deleted?
Is there a way to obtain this behaviour?

Most of the documentation and answers I found focus on the opposite: having the files deleted.


Solution

  • When you switch to a branch, Git makes sure that your working tree matches the content of the commit you're switching. Quoting an extract of Basic Branching of the Git Pro book:

    when you switch branches, Git resets your working directory to look like it did the last time you committed on that branch. It adds, removes, and modifies files automatically to make sure your working copy is what the branch looked like on your last commit to it.

    This means that:

    • Every tracked file in your working directory that is not present in the selected commit will be removed.

    • Every tracked file in the selected commit that is not present in your working directory will be added.

    • Every tracked file that is present in both your working directory and the selected commit will be updated to the version contained in the commit.

    In your case, even though master had a .gitignore for some of the files on gitignoreless, once you switched to master those files had to be removed in order to match the content of master's head commit. This is because the files were already tracked on gitignoreless, and every tracked file in the working directory that is not contained in the selected commit is removed.

    As already explained by @matt, .gitignore affects only files that are not already in the index. Therefore, you might want to consider having your .gitignore on all your branches, and build it in such a way to not show undesired files during the staging process. However, since some of the undesired files have already been tracked, you can "untrack" them with the --cached option of git rm. In your case, you could run:

    # untrack the undesired files
    git rm --cached <file1> <file2> ... <fileN>
    
    # add the previous files as entries in your gitignore
    echo <file1> >> .gitgnore
    echo <file2> >> .gitgnore
    ...
    echo <fileN> >> .gitgnore