2 minute read

A Tale of Three Trees는 index를 비롯해 HEAD, wd(working directory)를 쉽게 설명한다. 여기서 index에 대한 이해가 부족했는데, index를 더 자세히 설명한 자료가 있어서 정리.

기본적인 세 가지 속성

  • binary file
  • maps paths to blob ids
  • cached lstat information

index는 이런 세 가지 속성을 가진다.

$ cat .git/index
....... binary. 알아볼 수가 없다 .....

index는 .git/index에 저장한다.

$ git ls-files --stage --abbrev
100644 175d9db 0	hello.txt
100644 13c909c 0	hello2.txt

git ls-files로 index 내용을 볼 수 있다. blob id와 path 매핑 정보를 가지고 있다. 100644는 실행할 수 없는 일반 파일 모드. 0은 slot 번호이다. 이건 뒤에서 다시 설명.

$ git ls-files --debug
hello.txt
  ctime: 1385003406:0
  mtime: 1385004359:0
  dev: 0	ino: 0
  uid: 0	gid: 0
  size: 35	flags: 0
hello2.txt
  ctime: 1385003688:0
  mtime: 1385007769:0
  dev: 0	ino: 0
  uid: 0	gid: 0
  size: 13	flags: 0

debug 옵션을 사용하면 더 많은 정보를 볼 수 있다. git add로 index를 안 해도 정보가 나온다. diff, status와 같은 tree 명령을 빠르게 하려고 정보를 캐시 하는데도 사용하기 때문.

index 파일 삭제 및 재생성

$ rm .git/index

index를 지우면 어떻게 될까?

$ git status
# HEAD detached at cf2ea2a
# Changes to be committed:
#   (use "git reset HEAD ..." to unstage)
#
#	deleted:    hello.txt
#	deleted:    hello2.txt
#
# Untracked files:
#   (use "git add ..." to include in what will be committed)
#
#	hello.txt
#	hello2.txt

위는 index와 HEAD를 비교한 결과고 아래는 index와 wd를 비교한 결과다. index가 없어서 위와 같은 결과가 나온다. HEAD와 빈 index를 비교하니 모두 삭제됐다고 나온다. 마찬가지로 wd와 빈 index를 비교하니 파일 두 개 다 버전 컨트롤이 안 된다고 나온다.

$ git reset --mixed HEAD

mixed 옵션으로 wd는 놔두고 index까지만 HEAD로 reset한다. 즉, HEAD로부터 index를 다시 만든다. 혹은 read-tree 명령으로 tree object를 직접 읽어서 index를 생성할 수도 있다.

slot 번호는 어디에 사용하나?

$ git merge dev
Auto-merging hello.txt
CONFLICT (content): Merge conflict in hello.txt
Automatic merge failed; fix conflicts and then commit the result.

충돌(conflict)났다. 그럼 이 정보는 어디에 저장할까?

$ git ls-files -s --abbrev
100644 3b18e51 1	hello.txt
100644 33d2bc8 2	hello.txt
100644 516583e 3	hello.txt

바로 index. 충돌 났을 때는 slot 1, 2, 3을 사용한다. 평온한 상태면 slot 0을 사용한다.

$ git show 3b18e51
hello world
$ git show 33d2bc8
hello [master] world
$ git show 516583e
hello [dev] world

slot 1에 공통 조상(common ancestor), 2에 ours(target), 3에 theirs(source)를 저장한다.

$ cat hello.txt
<<<<<<< HEAD hello [master] world ======= hello [dev] world >>>>>>> dev
$ vim hello.txt
$ cat hello.txt
hello [master, dev] world
$ git add hello.txt

resolve하고 index 업데이트.

$ git ls-files -s --abbrev
100644 9706440 0	hello.txt

slot 0으로 바뀌었다. 이제 commit을 할 수 있다. git은 slot 번호를 보고 충돌 여부를 판단한다.

$ git checkout -m hello.txt
$ git ls-files -s --abbrev
100644 3b18e51 1	hello.txt
100644 33d2bc8 2	hello.txt
100644 516583e 3	hello.txt
$ cat hello.txt
<<<<<<< ours hello [master] world ======= hello [dev] world >>>>>>> theirs

이 산이 아닌가벼. resolve를 잘못했다면 git checkout -m으로 돌릴 수 있다.

참고