인덱스 파일

git 기본 명령의 내부 구조

  • add, commit 등의 명령으로 파일이 등록되는 과정은 내부적으로 다음과 같은 고급 명령을 사용한다.
  • 이 중심에는 인덱스 파일이 있다.
순번 git 명령 내부 동작 설명
1 git add 블롭 객체 생성 git hash-object 명령으로 블롭 객체 생성
2 - 인덱스 등록 git update-index 명령으로 인덱스 파일에 스테이징 정보 등록
3 git commit 트리 객체 생성 git write-tree 명령으로 인덱스 파일정보를 이용하여 트리 객체 생성
4 - 커밋 객체 생성 git commit-tree 명령으로 커밋 객체 생성
5 - 브랜치 갱신 git update-ref 명령으로 브랜치 정보 갱신

실습 1: 레포지토리 준비

  • 레포지토리 생성
cd $HOME/lab
rm -rf test_internal_02
git init test_internal_02
cd test_internal_02

Initialized empty Git repository in ~/lab/test_internal_02/.git/

tree .git/objects
.git/objects
├── info
└── pack

3 directories, 0 files

hash-object 명령

  • hash-object 명령은 입력된 데이터를 이용하여 해시값을 계산하고 객체를 생성한다.
  • -w 옵션을 주지 않으면 단순히 해시값만 계산한다.
git hash-object [옵션] 입력파일 경로

hash-object 명령의 옵션

  • -t <유형>
    • 생성할 객체 유형을 지정
    • 기본값: “blob”
    • 가능한 값: “commit”, “tree”, “blob”, “tag”

실습 2: 파일의 블롭 객체 생성

echo "file1 line1" > file1.txt
git status
On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    file1.txt

nothing added to commit but untracked files present (use "git add" to track)
  • hash-object 명령으로 file1.txt 파일에 대한 해시값을 계산
git hash-object file1.txt
0b11cfca50e35a4865e8505f1a108bd23a3f9401
  • -w 옵션이 없어서 실재로 블롭객체 파일을 생성하지 않음
tree .git/objects
.git/objects
├── info
└── pack

3 directories, 0 files
  • -w 옵션을 주면 실재로 블롭객체 파일을 생성함
git hash-object -w file1.txt
0b11cfca50e35a4865e8505f1a108bd23a3f9401
tree .git/objects
.git/objects
├── 0b
│   └── 11cfca50e35a4865e8505f1a108bd23a3f9401
├── info
└── pack

4 directories, 1 file
  • cat-file 명령으로 블롭객체의 내용을 읽을 수 있음
git cat-file -p 0b11cfca50e35a4865e8505f1a108bd23a3f9401
file1 line1

인덱스 파일

  • 인덱스 파일은 다음과 같은 정보를 저장하고 제공하는 바이너리 파일
    • 사용자가 git commit 명령으로 커밋을 만들 때,
    • 이 커밋에 포함될 대상이 되는 모든 파일에 대한 메타 정보와 블롭 객체
    • 즉 블롭 객체는 commit 명령 시점에 만드는 것이 아니라 add 명령 시점에 미리 만들어 놓았다가
    • commit 명령을 하면 인덱스 파일의 정보를 조합하여 빠르게 생성
    • 트리 객체의 정보는 포함하지 않음
      • 대신 파일에 대한 메타정보에서 경로 정보 포함
      • 트리 객체는 커밋 생성시 실시간으로 생성
  • 메타 정보는 다음 포함
    • 파일의 블롭 객체에 대한 포인터
    • 해당 파일의 워크트리 파일 버전의 최신 변경 시간 등에 대한 정보
  • clone, checkout, switch 명령 등으로 헤드 커밋이 바뀌면
    • 헤드 커밋에 포함된 모든 대상 파일에 대한 메타 정보가 일단 인덱스 파일에 들어간다.
  • 이 상태에서 add 명령을 실행하면
    • 스테이지에 올라간 파일의 블롭 객체를 생성하고
    • 해당 파일에 대한 메타정보를 인덱스 파일에 등록한다.
    • 또한 해당 파일의 과거 버전의 정보는 삭제된다.

update-index 명령

  • hash-object 명령은 객체를 만들 뿐이지 스테이지에 넣지는 않는다.
  • 스테이지에 객체를 넣는 것은 update-index 명령을 사용한다.
  • update-index 명령은 파일의 메타정보와 내용을 인덱스 파일에 등록한다.
  • 실행시에 인덱스 파일이 없으면 새로 생성한다.

update-index 명령의 옵션

  • -add <유형>
    • 인덱스 파일에 파일의 블롭객체를 등록
    • –cacheinfo 옵션이 없으면 블롭객체를 직접 생성함
  • --cacheinfo <모드> <파일의 블롭객체의 해시값> <파일 경로>
    • 이미 생성된 블롭객체가 존재할 경우 직접 인덱스 파일에 직접 등록
  • --refresh
    • 파일 경로 정보를 이용하여 실제 파일에 대해 stat() 시스템 콜을 호출
    • 인덱스 파일의 엔트리 헤더(메타정보)가 stat() 시스템 콜 결과와 다르면 갱신
    • 블롭객체는 건들지 않는다.

실습 3: 인덱스 파일 생성

  • 아직 .git/index 파일이 존재하지 않음
ls -al .git
total 24
drwxr-xr-x@  9 joelkim  staff  288 Oct  1 22:48 .
drwxr-xr-x@  4 joelkim  staff  128 Oct  1 22:48 ..
-rw-r--r--@  1 joelkim  staff  137 Oct  1 22:48 config
-rw-r--r--@  1 joelkim  staff   73 Oct  1 22:48 description
-rw-r--r--@  1 joelkim  staff   21 Oct  1 22:48 HEAD
drwxr-xr-x@ 16 joelkim  staff  512 Oct  1 22:48 hooks
drwxr-xr-x@  3 joelkim  staff   96 Oct  1 22:48 info
drwxr-xr-x@  5 joelkim  staff  160 Oct  1 22:48 objects
drwxr-xr-x@  4 joelkim  staff  128 Oct  1 22:48 refs
  • 아까 생성한 블롭 객체를 등록
git update-index --add --cacheinfo 100644 0b11cfca50e35a4865e8505f1a108bd23a3f9401 file1.txt
  • .git/index 인덱스 파일이 생성됨
ls -al .git
total 32
drwxr-xr-x@ 10 joelkim  staff  320 Oct  1 22:48 .
drwxr-xr-x@  4 joelkim  staff  128 Oct  1 22:48 ..
-rw-r--r--@  1 joelkim  staff  137 Oct  1 22:48 config
-rw-r--r--@  1 joelkim  staff   73 Oct  1 22:48 description
-rw-r--r--@  1 joelkim  staff   21 Oct  1 22:48 HEAD
drwxr-xr-x@ 16 joelkim  staff  512 Oct  1 22:48 hooks
-rw-r--r--@  1 joelkim  staff  104 Oct  1 22:48 index
drwxr-xr-x@  3 joelkim  staff   96 Oct  1 22:48 info
drwxr-xr-x@  5 joelkim  staff  160 Oct  1 22:48 objects
drwxr-xr-x@  4 joelkim  staff  128 Oct  1 22:48 refs
  • 인덱스 파일의 내용
hexdump -C .git/index
00000000  44 49 52 43 00 00 00 02  00 00 00 01 00 00 00 00  |DIRC............|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000020  00 00 00 00 00 00 81 a4  00 00 00 00 00 00 00 00  |................|
00000030  00 00 00 00 0b 11 cf ca  50 e3 5a 48 65 e8 50 5f  |........P.ZHe.P_|
00000040  1a 10 8b d2 3a 3f 94 01  00 09 66 69 6c 65 31 2e  |....:?....file1.|
00000050  74 78 74 00 35 4c 42 43  60 45 a6 f6 87 b3 31 4e  |txt.5LBC`E....1N|
00000060  84 73 b8 e3 b9 75 79 c3                           |.s...uy.|
00000068
  • 인덱스 항목 중 워크트리의 메타 정보에 해당하는 헤더 정보가 빠져있음. update-index –refresh 명령으로 갱신
git update-index --refresh
hexdump -C .git/index
00000000  44 49 52 43 00 00 00 02  00 00 00 01 68 dd 31 34  |DIRC........h.14|
00000010  0a 54 b4 f9 68 dd 31 34  0a 54 b4 f9 01 00 00 04  |.T..h.14.T......|
00000020  09 94 d2 c2 00 00 81 a4  00 00 01 f6 00 00 00 14  |................|
00000030  00 00 00 0c 0b 11 cf ca  50 e3 5a 48 65 e8 50 5f  |........P.ZHe.P_|
00000040  1a 10 8b d2 3a 3f 94 01  00 09 66 69 6c 65 31 2e  |....:?....file1.|
00000050  74 78 74 00 1e a9 4b b5  42 08 e0 43 0c f1 51 a0  |txt...K.B..C..Q.|
00000060  a9 00 54 70 3b 03 07 3d                           |..Tp;..=|
00000068

인덱스 파일의 구조

  • 구조

    • 인덱스 파일 헤더 (12 바이트)
    • 파일에 대한 인덱스 항목들 (가변)
    • 확장 영역 (가변)
      • 트리 항목 (커밋이 존재하는 경우)
    • 체크섬 (20 바이트)
  • 인덱스 파일 헤더

    • 총 12 바이트
    • 4바이트 시그너쳐 (‘D’, ‘I’, ‘R’, ‘C’) (“dircache”를 뜻함)
    • 4바이트 버전숫자 (2~4 중 하나)
    • 32비트 인덱스 엔트리 갯수
  • 헤더 예시

    • 44 49 52 43 00 00 00 02 00 00 00 01

    • DIRC, 버전 2, 1개 엔트리

      00000000  44 49 52 43 00 00 00 02  00 00 00 01 __ __ __ __  |DIRC........    |
  • 인덱스 항목 구성

    • 항목 헤더 (파일의 생성일자, 모드, 크기 등 메타정보)
    • 블롭객체 해시값 (파일의 내용)
    • 플래그 + 파일 이름
    • 엔트리 항목 해시값
  • 항목 헤더 (40 바이트)

    • 32 비트 ctime 초 (파일 메터메이터가 최종 변경된 시간)
    • 32 비트 ctime 나노초
    • 32 비트 mtime 초 (파일 본문 데이터가 최종 변경된 시간)
    • 32 비트 mtime 나노초
    • 32 비트 dev
    • 32 비트 inode 아이디
    • 32 비트 모드
    • 32 비트 uid
    • 32 비트 gid
    • 32 비트 파일 크기
    00000000  __ __ __ __ __ __ __ __  __ __ __ __ 68 b2 47 74  |            h.Gt|
    00000010  01 6c 0f fd 68 b2 47 74  01 6c 0f fd 01 00 00 04  |.l..h.Gt.l......|
    00000020  09 65 a3 68 00 00 81 a4  00 00 01 f6 00 00 00 14  |.e.h............|
    00000030  00 00 00 0c __ __ __ __  __ __ __ __ __ __ __ __  |....            |
  • 객체 해시값

    • 0b11cfca50e35a4865e8505f1a108bd23a3f9401
    00000030  __ __ __ __ 0b 11 cf ca  50 e3 5a 48 65 e8 50 5f  |........P.ZHe.P_|
    00000040  1a 10 8b d2 3a 3f 94 01  __ __ __ __ __ __ __ __  |....:?.         |
  • 플래그(2 바이트) + 파일 이름(8 바이트 배수로 패딩)

    00000040  __ __ __ __ __ __ __ __  00 09 66 69 6c 65 31 2e  |.       ..file1.|
    00000050  74 78 74 00 __ __ __ __  __ __ __ __ __ __ __ __  |txt.            |
  • 20 바이트 체크섬

    00000050  __ __ __ __ 8c 8e e0 4a  cb ef 63 04 81 44 a6 22  |    ...J..c..D."|
    00000060  8f cd 09 a2 39 cb 45 1c                           |....9.E.|

ls-files 명령

  • ls-files 명령은 워크트리와 스테이지, 커밋 내부의 파일 목록을 보여주는 명령
  • 워크트리의 파일 목록은 실시간으로 검색하지만
  • 스테이지와 커밋 파일 목록을 찾을 때 내부적으로 인덱스 파일을 사용함

ls-files 명령의 옵션

  • -c
    • 인덱스 파일의 항목 출력
    • 즉, 추적 상태인 파일 목록 출력
    • 아무 옵션이 없는 상태와 동일한 결과
  • --stage, -s
    • 스테이지 영역의 파일만 출력
  • --others, -o
    • 비추적 상태의 파일 목록 출력
  • --ignored, -i
    • 무시되고 있는 파일 목록 출력
    • 반드시 -o 옵션과 같이 사용해야 함
  • --debug
    • 메타 정보까지 출력

실습 4: ls-files 출력

  • 아직 추적되지 않는 신규 파일 생성
mkdir sub
echo "file2 line1" > sub/file2.txt
tree .
.
├── file1.txt
└── sub
    └── file2.txt

2 directories, 2 files
  • 추적 상태의 파일 (스테이지 + 커밋)
git ls-files
file1.txt
  • 스테이지 파일
git ls-files -s
100644 0b11cfca50e35a4865e8505f1a108bd23a3f9401 0   file1.txt
  • 메타 정보 포함
git ls-files --debug
file1.txt
  ctime: 1759326516:173323513
  mtime: 1759326516:173323513
  dev: 16777220 ino: 160748226
  uid: 502  gid: 20
  size: 12  flags: 0
  • 비추적 상태의 파일
git ls-files -o
sub/file2.txt

실습 5: 다른 파일을 스테이지에 추가

git hash-object -w sub/file2.txt
4a8d995103b41f561e547ce3b32e1983d424da22
tree .git/objects
.git/objects
├── 0b
│   └── 11cfca50e35a4865e8505f1a108bd23a3f9401
├── 4a
│   └── 8d995103b41f561e547ce3b32e1983d424da22
├── info
└── pack

5 directories, 2 files
git update-index --add --cacheinfo 100644 4a8d995103b41f561e547ce3b32e1983d424da22 sub/file2.txt
git update-index --refresh
hexdump -C .git/index
00000000  44 49 52 43 00 00 00 02  00 00 00 02 68 dd 31 34  |DIRC........h.14|
00000010  0a 54 b4 f9 68 dd 31 34  0a 54 b4 f9 01 00 00 04  |.T..h.14.T......|
00000020  09 94 d2 c2 00 00 81 a4  00 00 01 f6 00 00 00 14  |................|
00000030  00 00 00 0c 0b 11 cf ca  50 e3 5a 48 65 e8 50 5f  |........P.ZHe.P_|
00000040  1a 10 8b d2 3a 3f 94 01  00 09 66 69 6c 65 31 2e  |....:?....file1.|
00000050  74 78 74 00 68 dd 31 35  31 23 73 59 68 dd 31 35  |txt.h.151#sYh.15|
00000060  31 23 73 59 01 00 00 04  09 94 d3 17 00 00 81 a4  |1#sY............|
00000070  00 00 01 f6 00 00 00 14  00 00 00 0c 4a 8d 99 51  |............J..Q|
00000080  03 b4 1f 56 1e 54 7c e3  b3 2e 19 83 d4 24 da 22  |...V.T|......$."|
00000090  00 0d 73 75 62 2f 66 69  6c 65 32 2e 74 78 74 00  |..sub/file2.txt.|
000000a0  00 00 00 00 72 94 7e e7  5c e7 11 23 2a ed 37 69  |....r.~.\..#*.7i|
000000b0  37 0f c0 82 3b 47 29 5c                           |7...;G)\|
000000b8
git ls-files -s
100644 0b11cfca50e35a4865e8505f1a108bd23a3f9401 0   file1.txt
100644 4a8d995103b41f561e547ce3b32e1983d424da22 0   sub/file2.txt
git ls-files --debug
file1.txt
  ctime: 1759326516:173323513
  mtime: 1759326516:173323513
  dev: 16777220 ino: 160748226
  uid: 502  gid: 20
  size: 12  flags: 0
sub/file2.txt
  ctime: 1759326517:824406873
  mtime: 1759326517:824406873
  dev: 16777220 ino: 160748311
  uid: 502  gid: 20
  size: 12  flags: 0

write-tree 명령

  • write-tree 명령은 인덱스 내의 항목을 기반으로 커밋 등록에 필요한 트리 객체들을 생성하고 인덱스 파일에 추가
  • 생성된 트리 객체 중 루트 트리의 해시값을 출력

실습 6: 트리 객체 생성

git write-tree
4c2cf5eb3d8af11e9fe5f56cb6c853e1559d7166
  • 2개의 트리 객체가 만들어진다.
tree .git/objects
.git/objects
├── 0b
│   └── 11cfca50e35a4865e8505f1a108bd23a3f9401
├── 4a
│   └── 8d995103b41f561e547ce3b32e1983d424da22
├── 4c
│   └── 2cf5eb3d8af11e9fe5f56cb6c853e1559d7166
├── dd
│   └── 62677237dce0946aeffef97910ffc4ec32c3e7
├── info
└── pack

7 directories, 4 files
  • 각 트리 객체의 내용은 다음과 같다.
git cat-file -p 4c2cf5eb3d8af11e9fe5f56cb6c853e1559d7166
100644 blob 0b11cfca50e35a4865e8505f1a108bd23a3f9401    file1.txt
040000 tree dd62677237dce0946aeffef97910ffc4ec32c3e7    sub
git cat-file -p dd62677237dce0946aeffef97910ffc4ec32c3e7
100644 blob 4a8d995103b41f561e547ce3b32e1983d424da22    file2.txt
  • 트리 객체의 경우에는 cat-file -p 명령대신 ls-tree 명령 사용 가능
git ls-tree 4c2cf5eb3d8af11e9fe5f56cb6c853e1559d7166
100644 blob 0b11cfca50e35a4865e8505f1a108bd23a3f9401    file1.txt
040000 tree dd62677237dce0946aeffef97910ffc4ec32c3e7    sub
git ls-tree dd62677237dce0946aeffef97910ffc4ec32c3e7
100644 blob 4a8d995103b41f561e547ce3b32e1983d424da22    file2.txt
  • write-tree 명령은 인덱스 파일에도 생성된 트리 객체의 정보를 넣는다
hexdump -C .git/index
00000000  44 49 52 43 00 00 00 02  00 00 00 02 68 dd 31 34  |DIRC........h.14|
00000010  0a 54 b4 f9 68 dd 31 34  0a 54 b4 f9 01 00 00 04  |.T..h.14.T......|
00000020  09 94 d2 c2 00 00 81 a4  00 00 01 f6 00 00 00 14  |................|
00000030  00 00 00 0c 0b 11 cf ca  50 e3 5a 48 65 e8 50 5f  |........P.ZHe.P_|
00000040  1a 10 8b d2 3a 3f 94 01  00 09 66 69 6c 65 31 2e  |....:?....file1.|
00000050  74 78 74 00 68 dd 31 35  31 23 73 59 68 dd 31 35  |txt.h.151#sYh.15|
00000060  31 23 73 59 01 00 00 04  09 94 d3 17 00 00 81 a4  |1#sY............|
00000070  00 00 01 f6 00 00 00 14  00 00 00 0c 4a 8d 99 51  |............J..Q|
00000080  03 b4 1f 56 1e 54 7c e3  b3 2e 19 83 d4 24 da 22  |...V.T|......$."|
00000090  00 0d 73 75 62 2f 66 69  6c 65 32 2e 74 78 74 00  |..sub/file2.txt.|
000000a0  00 00 00 00 54 52 45 45  00 00 00 35 00 32 20 31  |....TREE...5.2 1|
000000b0  0a 4c 2c f5 eb 3d 8a f1  1e 9f e5 f5 6c b6 c8 53  |.L,..=......l..S|
000000c0  e1 55 9d 71 66 73 75 62  00 31 20 30 0a dd 62 67  |.U.qfsub.1 0..bg|
000000d0  72 37 dc e0 94 6a ef fe  f9 79 10 ff c4 ec 32 c3  |r7...j...y....2.|
000000e0  e7 a8 be e7 89 92 f8 9e  a3 8c 38 5e 31 01 4b 36  |..........8^1.K6|
000000f0  8d a4 65 4a d5                                    |..eJ.|
000000f5

status 명령

  • status 명령은 실행시 인덱스 파일을 이용하여 다음과 같은 정보를 알아낸다.
    1. 워크트리의 파일이 추적 상태인지 아닌지 알아낸다.
    2. 추적 상태의 파일인 경우 변경되었는지 아닌지 알아낸다.
    3. 파일이 워크트리, 스테이지, 커밋 중 어느 영역에 있는지 알아낸다.
  • 방법은 다음과 같다.
    • 추적/비추적 여부
      • 인덱스 항목에 없는 파일은 비추적 파일이다.
      • 인덱스 항목에 있는 파일은 스테이지 또는 커밋 영역의 파일이다. 즉, 추적 상태의 파일이다.
    • 추적 파일의 변경 여부
      • 워크트리 파일의 해시값이 인덱스 항목의 블롭 객체 해시값과 같으면 변경되지 않은 파일이다.
      • 워크트리 파일의 해시값이 인덱스 항목의 블롭 객체 해시값과 다르면 변경된 않은 파일이다.
    • 스테이지/커밋 영역의 구별 여부
      • 인덱스 항목의 해시값과 같은 해시값을 가지는 블롭 객체가 커밋에 있으면 커밋 영역의 파일이다.
      • 인덱스 항목의 해시값과 같은 해시값을 가지는 블롭 객체가 커밋에 없으면 스테이지 영역의 파일이다.

실습 7: 상태 조회

git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
    new file:   file1.txt
    new file:   sub/file2.txt

commit-tree 명령

  • 커밋 객체를 생성

실습 8: 커밋 객체 생성

GIT_AUTHOR_DATE="2025-01-01T12:00:00" \
GIT_COMMITTER_DATE="2025-01-01T12:00:00" \
git commit-tree -m 'first commit' 4c2cf5eb3d8af11e9fe5f56cb6c853e1559d7166
e42955ccb9b2b73c7e81e251cbab0fdc84aed671
tree .git/objects
.git/objects
├── 0b
│   └── 11cfca50e35a4865e8505f1a108bd23a3f9401
├── 4a
│   └── 8d995103b41f561e547ce3b32e1983d424da22
├── 4c
│   └── 2cf5eb3d8af11e9fe5f56cb6c853e1559d7166
├── dd
│   └── 62677237dce0946aeffef97910ffc4ec32c3e7
├── e4
│   └── 2955ccb9b2b73c7e81e251cbab0fdc84aed671
├── info
└── pack

8 directories, 5 files
  • 커밋 객체는 정상적으로 생성되었음
git cat-file -p e42955ccb9b2b73c7e81e251cbab0fdc84aed671
tree 4c2cf5eb3d8af11e9fe5f56cb6c853e1559d7166
author user <user@company.com> 1735700400 +0900
committer user <user@company.com> 1735700400 +0900

first commit
  • 그러나 git status 명령으로는 아직 커밋된 상태가 아니라고 나옴
git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
    new file:   file1.txt
    new file:   sub/file2.txt
  • 로그도 정상 출력되지 않음
git log || true
fatal: your current branch 'main' does not have any commits yet

update-ref 명령

  • 브랜치가 해당 커밋을 가리키지 않으면 커밋이 생성되었다고 인식하지 않음
  • update-ref 명령으로 브랜치가 해당 커밋을 가리키게 만듬

실습 9: 브랜치 참조 파일 생성

  • HEAD는 main 브랜치를 가리키고 있음
cat .git/HEAD
ref: refs/heads/main
  • 그러나 main 브랜치에 대한 참조 파일이 만들어지지 않았음
tree .git/refs/heads
.git/refs/heads

0 directories, 0 files
  • update-ref 명령으로 방금 생성한 커밋을 가리키는 main 브랜치 참조 파일 생성
git update-ref refs/heads/main e42955ccb9b2b73c7e81e251cbab0fdc84aed671
  • 참조 파일이 생성되었음
tree .git/refs/heads
.git/refs/heads
└── main

1 directory, 1 file
  • 참조 파일은 방금 생성된 커밋 객체를 가리킴
cat .git/refs/heads/main
e42955ccb9b2b73c7e81e251cbab0fdc84aed671
  • 이제 정상적으로 커밋되었다고 인식함
git status
On branch main
nothing to commit, working tree clean
  • 로그도 정상적으로 출력
git log
commit e42955ccb9b2b73c7e81e251cbab0fdc84aed671 (HEAD -> main)
Author: user <user@company.com>
Date:   Wed Jan 1 12:00:00 2025 +0900

    first commit

실습 10: add, commit 명령 사용

  • 지금까지 한 작업을 add, commit 명령으로 해본다.

  • 저장소 리셋

cd $HOME/lab
rm -rf test_internal_02
git init test_internal_02
cd test_internal_02

Initialized empty Git repository in ~/lab/test_internal_02/.git/

echo "file1 line1" > file1.txt
mkdir sub
echo "file2 line1" > sub/file2.txt
git add .
hexdump -C .git/index
00000000  44 49 52 43 00 00 00 02  00 00 00 02 68 dd 31 3a  |DIRC........h.1:|
00000010  24 ae 18 fd 68 dd 31 3a  24 ae 18 fd 01 00 00 04  |$...h.1:$.......|
00000020  09 94 d3 a2 00 00 81 a4  00 00 01 f6 00 00 00 14  |................|
00000030  00 00 00 0c 0b 11 cf ca  50 e3 5a 48 65 e8 50 5f  |........P.ZHe.P_|
00000040  1a 10 8b d2 3a 3f 94 01  00 09 66 69 6c 65 31 2e  |....:?....file1.|
00000050  74 78 74 00 68 dd 31 3a  2b 8e 13 1c 68 dd 31 3a  |txt.h.1:+...h.1:|
00000060  2b 8e 13 1c 01 00 00 04  09 94 d3 a4 00 00 81 a4  |+...............|
00000070  00 00 01 f6 00 00 00 14  00 00 00 0c 4a 8d 99 51  |............J..Q|
00000080  03 b4 1f 56 1e 54 7c e3  b3 2e 19 83 d4 24 da 22  |...V.T|......$."|
00000090  00 0d 73 75 62 2f 66 69  6c 65 32 2e 74 78 74 00  |..sub/file2.txt.|
000000a0  00 00 00 00 61 3f 6a 01  88 c3 9b 3e c0 52 da 0e  |....a?j....>.R..|
000000b0  8e b6 ac d3 cb 87 88 5d                           |.......]|
000000b8
tree .git/objects
.git/objects
├── 0b
│   └── 11cfca50e35a4865e8505f1a108bd23a3f9401
├── 4a
│   └── 8d995103b41f561e547ce3b32e1983d424da22
├── info
└── pack

5 directories, 2 files
GIT_AUTHOR_DATE="2025-01-01T12:00:00" \
GIT_COMMITTER_DATE="2025-01-01T12:00:00" \
git commit -m 'first commit'
[main (root-commit) e42955c] first commit
 2 files changed, 2 insertions(+)
 create mode 100644 file1.txt
 create mode 100644 sub/file2.txt
hexdump -C .git/index
00000000  44 49 52 43 00 00 00 02  00 00 00 02 68 dd 31 3a  |DIRC........h.1:|
00000010  24 ae 18 fd 68 dd 31 3a  24 ae 18 fd 01 00 00 04  |$...h.1:$.......|
00000020  09 94 d3 a2 00 00 81 a4  00 00 01 f6 00 00 00 14  |................|
00000030  00 00 00 0c 0b 11 cf ca  50 e3 5a 48 65 e8 50 5f  |........P.ZHe.P_|
00000040  1a 10 8b d2 3a 3f 94 01  00 09 66 69 6c 65 31 2e  |....:?....file1.|
00000050  74 78 74 00 68 dd 31 3a  2b 8e 13 1c 68 dd 31 3a  |txt.h.1:+...h.1:|
00000060  2b 8e 13 1c 01 00 00 04  09 94 d3 a4 00 00 81 a4  |+...............|
00000070  00 00 01 f6 00 00 00 14  00 00 00 0c 4a 8d 99 51  |............J..Q|
00000080  03 b4 1f 56 1e 54 7c e3  b3 2e 19 83 d4 24 da 22  |...V.T|......$."|
00000090  00 0d 73 75 62 2f 66 69  6c 65 32 2e 74 78 74 00  |..sub/file2.txt.|
000000a0  00 00 00 00 54 52 45 45  00 00 00 35 00 32 20 31  |....TREE...5.2 1|
000000b0  0a 4c 2c f5 eb 3d 8a f1  1e 9f e5 f5 6c b6 c8 53  |.L,..=......l..S|
000000c0  e1 55 9d 71 66 73 75 62  00 31 20 30 0a dd 62 67  |.U.qfsub.1 0..bg|
000000d0  72 37 dc e0 94 6a ef fe  f9 79 10 ff c4 ec 32 c3  |r7...j...y....2.|
000000e0  e7 e0 57 20 e1 7c 46 bb  b0 8b 5c f2 55 63 62 9d  |..W .|F...\.Ucb.|
000000f0  8b ec 0c 7f e9                                    |.....|
000000f5
tree .git/objects
.git/objects
├── 0b
│   └── 11cfca50e35a4865e8505f1a108bd23a3f9401
├── 4a
│   └── 8d995103b41f561e547ce3b32e1983d424da22
├── 4c
│   └── 2cf5eb3d8af11e9fe5f56cb6c853e1559d7166
├── dd
│   └── 62677237dce0946aeffef97910ffc4ec32c3e7
├── e4
│   └── 2955ccb9b2b73c7e81e251cbab0fdc84aed671
├── info
└── pack

8 directories, 5 files

실습 11: 인덱스 파일 항목 변경

  • 변경된 파일을 add 하면 인덱스 파일 항목은 기존의 블롭 객체가 아니라 스테이징된 새 항목을 가리킨다.
  • 즉, 기존의 블롭 객체는 인덱스 파일에서 제거된다.
echo "file1 line2" >> file1.txt
git add file1.txt
tree .git/objects
.git/objects
├── 0b
│   └── 11cfca50e35a4865e8505f1a108bd23a3f9401
├── 4a
│   └── 8d995103b41f561e547ce3b32e1983d424da22
├── 4c
│   └── 2cf5eb3d8af11e9fe5f56cb6c853e1559d7166
├── d0
│   └── 37497f0226612d0c089c061a84788a3721f9e9
├── dd
│   └── 62677237dce0946aeffef97910ffc4ec32c3e7
├── e4
│   └── 2955ccb9b2b73c7e81e251cbab0fdc84aed671
├── info
└── pack

9 directories, 6 files
  • 인덱스 파일 내부를 보면 항목은 여전히 2개
  • file1.txt에 대한 해시값이 새로운 값으로 변경되었음
hexdump -C .git/index
00000000  44 49 52 43 00 00 00 02  00 00 00 02 68 dd 31 3b  |DIRC........h.1;|
00000010  2b 97 a8 2f 68 dd 31 3b  2b 97 a8 2f 01 00 00 04  |+../h.1;+../....|
00000020  09 94 d3 a2 00 00 81 a4  00 00 01 f6 00 00 00 14  |................|
00000030  00 00 00 18 d0 37 49 7f  02 26 61 2d 0c 08 9c 06  |.....7I..&a-....|
00000040  1a 84 78 8a 37 21 f9 e9  00 09 66 69 6c 65 31 2e  |..x.7!....file1.|
00000050  74 78 74 00 68 dd 31 3a  2b 8e 13 1c 68 dd 31 3a  |txt.h.1:+...h.1:|
00000060  2b 8e 13 1c 01 00 00 04  09 94 d3 a4 00 00 81 a4  |+...............|
00000070  00 00 01 f6 00 00 00 14  00 00 00 0c 4a 8d 99 51  |............J..Q|
00000080  03 b4 1f 56 1e 54 7c e3  b3 2e 19 83 d4 24 da 22  |...V.T|......$."|
00000090  00 0d 73 75 62 2f 66 69  6c 65 32 2e 74 78 74 00  |..sub/file2.txt.|
000000a0  00 00 00 00 54 52 45 45  00 00 00 22 00 2d 31 20  |....TREE...".-1 |
000000b0  31 0a 73 75 62 00 31 20  30 0a dd 62 67 72 37 dc  |1.sub.1 0..bgr7.|
000000c0  e0 94 6a ef fe f9 79 10  ff c4 ec 32 c3 e7 25 32  |..j...y....2..%2|
000000d0  3a e6 24 a8 d2 dd 3a 75  9c c2 1f d7 55 78 bb f2  |:.$...:u....Ux..|
000000e0  69 17                                             |i.|
000000e2

인덱스 락 파일

  • 인덱스 파일에 두 개 이상의 프로세스가 동시에 쓰기를 허용하면 누락 정보가 발생할 수 있음
  • 따라서 인덱스 파일에 대한 쓰기 작업은 한 번에 하나의 프로세스만 가능
  • 먼저 쓰기를 시도하는 프로세스가 인덱스 파일을 복사한 index.lock이라는 인덱스 락 파일을 생성
  • 만약 인덱스 락 파일이 존재하면 다른 프로세스는 인덱스 쓰기 실패함
  • 쓰기 권한을 가진 프로세스는 인덱스 파일 자체 대신 인덱스 락 파일에 쓰기 시도
  • 쓰기가 완료되면 index.lock 파일의 이름을 index로 바꿔서 한번에 파일 교체
touch .git/index.lock
git add file1.txt || true
fatal: Unable to create '/Users/joelkim/Work/personal/book_git/lab/test_internal_02/.git/index.lock': File exists.

Another git process seems to be running in this repository, e.g.
an editor opened by 'git commit'. Please make sure all processes
are terminated then try again. If it still fails, a git process
may have crashed in this repository earlier:
remove the file manually to continue.
git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    modified:   file1.txt

diff 명령

  • diff 명령은 워크트리, 스테이지, 커밋간의 차이를 비교

diff 명령 사용법

명령 옵션 인수1 인수2 비교대상1 비교대상2
git diff - - - 워크트리 인덱스파일
git diff --cached - - 스테이지 헤드커밋
git diff - <커밋> - 워크트리 커밋
git diff --cached <커밋> - 스테이지 커밋
git diff - <커밋1> <커밋2> 커밋1 커밋2
  • 인덱스 파일은 추후 다루지만
    • 기본적으로는 다음 번에 commit 명령을 내릴 때 포함될 내용을 뜻한다.
    • 추적중이고 아직 변경되지 않은 헤드커밋의 파일 + 추적중이고 변경되어 add 된 파일
    • 따라서 추적중인 파일이 워크트리에서 변경되면 아직 add를 하지 않은 상태, 즉 스테이징을 하지 않아도 diff 명령으로 비교가 가능

실습 6: diff 명령

  • 레포지토리 생성
cd $HOME/lab
rm -rf test_diff_01
git init test_diff_01
cd test_diff_01

Initialized empty Git repository in ~/lab/test_diff_01/.git/

  • 신규파일 생성 후
echo "file1 line1" >> file.txt
git status
On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    file.txt

nothing added to commit but untracked files present (use "git add" to track)
  • 비추적 상태의 신규 파일은 워크트리에 있어도 스테이지와 비교하지 않는다
git diff
  • 아직 add 전이므로 스테이지와 커밋도 동일
git diff --cached
  • add 스테이징 후
git add file.txt
git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
    new file:   file.txt
  • add된 직후이므로 워크트리와 스테이지는 동일
git diff
  • 스테이지에는 신규파일이 있고 커밋에는 없으므로 –cached 옵션에는 표시됨
git diff --cached
diff --git a/file.txt b/file.txt
new file mode 100644
index 0000000..0b11cfc
--- /dev/null
+++ b/file.txt
@@ -0,0 +1 @@
+file1 line1
  • 커밋 후
git commit -m c1
[main (root-commit) 44f2dfd] c1
 1 file changed, 1 insertion(+)
 create mode 100644 file.txt
git status
On branch main
nothing to commit, working tree clean
  • 커밋 직후에는 워크트리, 스테이지, 헤드커밋이 동일
git diff
git diff --cached
  • 추적 상태의 파일이 변경된 경우
echo "file1 line2" >> file.txt
git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   file.txt

no changes added to commit (use "git add" and/or "git commit -a")
  • 추적 상태의 파일이 변경되면 인덱스와 달라짐
git diff
diff --git a/file.txt b/file.txt
index 0b11cfc..d037497 100644
--- a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
 file1 line1
+file1 line2
  • 이는 헤드커밋과 직접 비교한 것과 같음
git diff HEAD
diff --git a/file.txt b/file.txt
index 0b11cfc..d037497 100644
--- a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
 file1 line1
+file1 line2
  • 아직 스테이지에는 변경이 반영되지 않았기 때문에 스테이지와 헤드커밋은 동일함
git diff --cached
  • 스테이징
git add file.txt
git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    modified:   file.txt
  • 스테이징 되면 인덱스(스테이지)가 워크트리와 같아지므로 diff에 나타나지 않음
git diff
  • 이제는 스테이지와 헤드커밋이 달라지므로 diff –cached 명령에 나타남
git diff --cached
diff --git a/file.txt b/file.txt
index 0b11cfc..d037497 100644
--- a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
 file1 line1
+file1 line2
  • 워크트리와 헤드커밋을 직접 비교해도 같은 결과임
git diff HEAD
diff --git a/file.txt b/file.txt
index 0b11cfc..d037497 100644
--- a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
 file1 line1
+file1 line2
  • 일단 커밋하면 모든 상태가 같으므로 어떤 diff 명령도 차이를 보이지 않음
git commit -m c2
[main 4bbe6cc] c2
 1 file changed, 1 insertion(+)
git diff
git diff --cached
git diff HEAD
  • 추적중인 파일 삭제
rm -f file.txt
git status
On branch main
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    deleted:    file.txt

no changes added to commit (use "git add" and/or "git commit -a")
git diff
diff --git a/file.txt b/file.txt
deleted file mode 100644
index d037497..0000000
--- a/file.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-file1 line1
-file1 line2
git diff HEAD
diff --git a/file.txt b/file.txt
deleted file mode 100644
index d037497..0000000
--- a/file.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-file1 line1
-file1 line2
git diff --cached
git add .
git diff
git diff --cached
diff --git a/file.txt b/file.txt
deleted file mode 100644
index d037497..0000000
--- a/file.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-file1 line1
-file1 line2
git diff HEAD
diff --git a/file.txt b/file.txt
deleted file mode 100644
index d037497..0000000
--- a/file.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-file1 line1
-file1 line2
git commit -m c3
[main 79971a0] c3
 1 file changed, 2 deletions(-)
 delete mode 100644 file.txt
git diff
git diff --cached
git diff HEAD