8

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?

Determinant
  • 3,886
  • 7
  • 31
  • 47

2 Answers2

4

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%        
Noufal Ibrahim
  • 71,383
  • 13
  • 135
  • 169
2

In order to understand this, you need to digg a little deeper into git internals.

Git stores all kinds of information as objects. There are mainly three kinds of objects.

  • blob

    Store acctural file content in git.

  • tree

    Store the information of a tree structure, may contain references to other blob objects and tree objects.

  • commit

    Store the information about a commit, contain a reference to a tree object and other informaion, eg, author, committer, committing message and so on.

An index file is accturally a tree object which presents the information about the current working tree.

Each object is identified by a unique sha1 hash of its content. Under .git/refs or in .git/packed_refs, git holds the relationship between the branch and the sha1 hash of the commit object it points to.

Each time you checkout a new branch, git just extract the files according to the tree object associated with the commit of that branch and generate the new index file.

Git Internals could help.

weynhamz
  • 1,968
  • 18
  • 18
  • Well.if you look carefully in objects folder you will find out there's no index object. – Determinant Aug 23 '12 at 09:19
  • 1
    @ymfoi There is no such thing as index object, index is just a file located at `.git/index` present the current state of the working tree and get updated each time you `git add` or generated when you `git checkout` a new branch. When you `git commit`, the index will enventrually became a tree object of the new commit. – weynhamz Aug 23 '12 at 09:44
  • How to explain the files which are staged but not commited, is still remembered per branch? – Determinant Aug 25 '12 at 04:42
  • @ymfoi When you run `git add`, files get staged, they are just be added into the index file(`.git/index`), at this time, the tree that index file presents is different form the tree of the HEAD associated, until you commit it to a new commit `git commit` or unset it `git reset HEAD`, you can not switch branches. `git stash` is another thing, if I understand you correctly. – weynhamz Aug 25 '12 at 05:19