Search code examples
gitgit-index

Git: How does git remember the index for each branch?


For example, I create file a in the repo(suppose I'm on the master branch), then I git add a and git commit. After that I git branch copy and git checkout copy. Finaly I create file b in the word directory then git add b.

Git seems to be smart when I checkout back to the master branch, and git ls-files, file b is not listed.

So I'm confused, since we only have one index file in a repo, how can git maintain different staging area for branches at the same time?

EDIT:

How to explain the files which are staged but not commited, is still remembered per branch?


Solution

  • I've not dived into the implementation in detail but when you switch a branch, the index file is manually updated to reflect the content of the new HEAD.

    For example, I have to branches here master (with one file) and test (with two files).

    noufal@sanitarium% git branch
      master
    * test
    noufal@sanitarium% file .git/index
    .git/index: Git index, version 2, 2 entries
    noufal@sanitarium% git checkout master
    Switched to branch 'master'
    noufal@sanitarium% file .git/index
    .git/index: Git index, version 2, 1 entries
    

    It's changed the index when the branch switching happened.

    Also, if you "manually" switching branches, git doesn't update the index and gets confused. Continuing from above.

    noufal@sanitarium% more .git/HEAD
    ref: refs/heads/master
    noufal@sanitarium% echo "ref: refs/heads/test" > .git/HEAD
    noufal@sanitarium% file .git/index
    .git/index: Git index, version 2, 1 entries
    noufal@sanitarium% git status
    # On branch test
    # Changes to be committed:
    #   (use "git reset HEAD <file>..." to unstage)
    #
    #       deleted:    b
    #
    

    In other words, the index has a missing file which is there in the current repository so it's "staged for delete".

    As for switching branches after staging, the index is a separate area which doesn't change.

    noufal@sanitarium% git branch
    * master
      test
    noufal@sanitarium% ls
    x
    noufal@sanitarium% git status
    # On branch master 
    nothing to commit (working directory clean)
    noufal@sanitarium% git checkout test
    Switched to branch 'test'
    noufal@sanitarium% ls
    x
    noufal@sanitarium% echo "Something" > b
    noufal@sanitarium% git add b
    noufal@sanitarium% git status
    # On branch test   
    # Changes to be committed:
    #   (use "git reset HEAD <file>..." to unstage)
    #
    #       new file:   b
    #
    noufal@sanitarium% git checkout master
    A       b
    Switched to branch 'master'
    noufal@sanitarium% git status                    # Also there in index on master branch.
    # On branch master 
    # Changes to be committed:
    #   (use "git reset HEAD <file>..." to unstage)
    #
    #       new file:   b
    #
    noufal@sanitarium% git commit -m "Added b in master"
    [master 41d0c68] Added b in master
     1 file changed, 1 insertion(+)
     create mode 100644 b
    noufal@sanitarium% git status
    # On branch master 
    nothing to commit (working directory clean)
    noufal@sanitarium% git checkout test
    Switched to branch 'test'
    noufal@sanitarium% ls                           # Missing in the test branch although it was `git add`ed here. 
    x
    noufal@sanitarium%