Git

[Git] 실제 git 동작을 통해 깃 내부 변화 살펴보기

동구름이 2024. 8. 1. 20:24

 

 

[Git] Git의 내부 구조와 동작 원리를 알아보자

git 명령어는 자주 사용했지만, 실제 동작 원리에 대한 이해는 부족했던 것 같다. 특히 깃 충돌을 겪을 때, 부족한 개념지식으로 해결이 꼬이는 경우가 간혹가다 있었는데 오늘도 작은 규모의 커

dcloud.tistory.com

 

 

깃의 내부 구성요소에 대해서는 지난 포스팅에서 다루었다. 하지만 개념만 이해하고, 내부 요소가 구체적으로 어떻게 동작하는지는 이해하지 못했다는 것을 느꼈다.

 

그래서 이번 포스팅에서는 실제로 깃 파일을 만들고 각 동작마다 어떻게 내부 요소가 변경되는지를 살펴보겠다.

 

1. git init()

GitTest라는 디렉토리를 만들고 명령을 수행했다.

그리고 이전 포스팅에서 살펴보았듯이, git init 명령어를 실행하면, .git 파일이 해당 디렉토리에 생성된다.

( .파일은 `command + shift + . ` 누르면 보임)

 

그리고 /objects내에는 아직 object 파일이 존재하지 않는다.

 

 

2. git add()

해당 디렉토리에 test.txt파일을 추가하고 add()를 실행해보자

echo "TEST1" > test.txt

 git add test.txt

 

 

add를 하면 이전에 없던 object와 .git/index 파일이 생성된다.

 

 

object 파일 열어보기

blob 오브젝트

오브젝트는 아래 명령어로 읽어들일 수 있다.

git cat-file -p <object>

 

해당 명령어를 실행해보면, test.txt안의 내용이 출력되는 것을 볼 수 있다.

 

지난 포스팅에서 다루었듯 파일의 내용만 담고 있는 오브젝트는 blob 오브젝트이다.

 

 

/index 파일

여기서 index는 무엇일까?

 

index는 헤더와 트리 엔트리, 스테이지 엔트리가 포함된다.

 

파일에 대한 메타데이터, 전체 디렉토리에 대한 정보, 수정 상태를 추적할 수 있는 정보가 깃 인덱스 파일 포맷으로 저장되어있다.

 

 

+) 파일 한번 열어서 눈으로 보기 실패 과정..(결론부터 말하면 실패함)

더보기

 

이 파일을 읽기 위해 간단한 코드를 작성해보았다

 

그러면 위와 같이 출력되는데, 이것은 index파일이 바이너리 포맷으로 저장되어 있기 때문이다.

 

해당 내용이 매우 복잡하기 때문에, nodegit 라이브러리를 설치해서 열어보려고 했다.

npm install nodegit

그런데 더이상 지원하지 않는 라이브러리라 설치가 되지 않았다..

 

그래서 하나하나 파싱할 수는 없을까 싶었는데 이것도 너무너무 복잡해서 일부만 출력이 되기는 하는데 금방 메모리가 털려버림

const indexBuffer = fs.readFileSync(indexPath);

// 헤더 읽기
const header = indexBuffer.toString('utf8', 0, 12);
console.log('Header:', header);

const numEntries = indexBuffer.readUInt32BE(12);
console.log('Number of Entries:', numEntries);

// 파일 항목 읽기
let offset = 12 + 4; // 헤더 + numEntries (4 bytes)
for (let i = 0; i < numEntries; i++) {
  const nameLength = indexBuffer.indexOf('\\0', offset) - offset;
  const fileName = indexBuffer.toString('utf8', offset, offset + nameLength);
  offset += nameLength + 1;

  console.log('File Name:', fileName);
}

이게 천 줄 이상 나와버림..

그리고 얼마있다가 메모리릭 발생

 

포기함.

 

 

왜 편하게 읽을 수 없게 바이너리 포맷으로 저장해두었을까?

여러 이유가 있지만, 가장 큰 이유는 대규모 저장소를 다루기 때문에 파일 접근, 검색 수정 속도를 빠르게 하도록 설계된 것이라고 한다.

 

위에서 index 파일을 읽는 것은 실패했지만, 대신 git이 관리하는 전체 디렉토리를 출력할 수 있는 명령어가 있다.

 git ls-files

git 을 add하면 깃이 관리하는 디렉토리에 추가된다. 정확히 말하면 staged Area에 올라가게 된다.

 

 

 

3. git commit()

이제 commit을 해보고 어떤 변화가 일어나는지 보자.

git commit -m"feat: test1추가"

 

눈에 띄는 것은 COMMIT_EDITMSG라는 커밋 메시지를 저장하는 파일이 추가되었고, index가 새롭게 업데이트 되었다.

 

그리고 objects에 두 개의 오브젝트가 새롭게 추가되었다.

 

추가된 object 열어보기

오브젝트를 하나씩 열어보자

 

커밋 오브젝트

첫번째 오브젝트를 열었더니 위와 같은 값이 나온다. 이것은 커밋 오브젝트이다.

 

커밋 오브젝트에는 아래와 같은 내용이 담기는데,

tree 25d09f68bf24155b74631c00fb83bf555551392b
author 정동교 <jdk8467@naver.com> 1722602621 +0900
committer 정동교 <jdk8467@naver.com> 1722602621 +0900

feat: test1추가

커밋이 가르키는 tree가 첫 줄에,그리고 author committer 가 이어 나오고 커밋 메시지가 출력된다.

 

 

그리고 여기서 커밋이 가르키는 tree가 새롭게 추가된 두 개의 오브젝트 중 나머지 하나의 오브젝트이다.

트리 오브젝트

100644 blob bb83be4809706149a305be37fd727cb683120cc2	test.txt

커밋 트리가 가르키는 오브젝트는 트리 오브젝트이다.

 

저장된 오브젝트가 blob인지 tree인지를 가리는 인자가 나오고, 뒤에는 오브젝트와 파일이름(혹은 디렉토리이름)이 나온다.

 

그리고 여기서 가르키는 bb83be4809706149a305be37fd727cb683120cc2 가 add 에서 추가되었던 blob 오브젝트이다.

 

 

이렇게 지난 포스팅 에서 정리했던 오브젝트의 세 종류(blob, tree, commit)가 각각 어디서 생성되고, 어떻게 생겼는지 직접 눈으로 확인할 수 있었다.

 

 

정리를 해보면, obect 객체에 대해서 크게 세 가지를 알 수 있다.


1. blob 오브젝트는 add() 시에 생성된다.

2. commit 오브젝트에서는 tree 오브젝트를 가르킨다.

3. tree 오브젝트에서는 파일명(혹은 디렉토리명)을 관리하고 blob 오브젝트(혹은 Tree 오브젝트) 가르킨다.


 

 

위 예시에서 tree 오브젝트가 파일명과 blob 오브젝트를 가르켰는데, 디렉토리명과 Tree 오브젝트도 가르킬 수 있다는 것은 어떤 의미일까?

 

 

 

추가적인 동작을 통해 살펴보자!

 

새로운 디렉토리 하위 파일 git에 커밋해보기

이제 깃에 폴더를 만들고 하위 파일을 커밋해보자

 

1. git add()

git add directory/test2.txt

 

마찬가지로 add는 blob 오브젝트를 생성한다.

 

 

2. git commit()

git commit -m"feat:test2.txt"

 

 

commit 을 보면 이전에 파일을 올렸을 때 2개가 생성되었던 것과는 다르게 3개가 생성되었다.

 

 

전체 내용을 열어서 살펴보면, tree 객체가 두 개가 생긴 것을 알 수 있다.

 

 

그리고 두 개의 트리 객체의 내용을 자세히 살펴보면

트리 객체가 하위의 트리 객체를 담고 있는 것을 볼 수 있다.트리 객체는 이런 방식으로 작업 폴더의 디렉토리 구조를 나타낸다.

 

어렵게 생각할 것은 아니고 깃의 디렉토리 구조라고 생각하면 이해가 쉽다. 트리 - 하위 트리 디렉토리 - 하위 디렉토리 구조와 일치된다고 생각하면 된다.

 

 그래서 트리 객체에는 blob 객체 뿐 아니라 tree 객체도 들어간다. 디렉토리에서 디렉토리 안에 파일 뿐 아니라 디렉토리가 추가될 수 있는 것처럼!