[CS] Git 내부에서 어떻게 동작하는거에요?

어흥·2024년 8월 5일

Computer Science

목록 보기
24/28

Git!! 은 버전 관리 시스템으로서 코드의 변경 사항을 추적해 협업에서 필수적으로 사용되는 SW 중 하나입니다.

Git을 사용할 때 기본적인 명령어만 알고 사용할 줄 알고 내부 동작원리에 대해 알지 못해서 이번 기회에 내부 동작 원리에 대해서 작성해보려고 합니다! 이 포스팅에서 Git이 어떻게 작동하는지, 그리고 Git의 내부에 어떤 객체들이 존재하며 어떻게 관리되는지를 알아보겠습니다.

Git 폴더 구조

git 폴더 구성을 살펴봅시다! 프로젝트 폴더에 .git 디렉토리의 구성요소는 다음과 같습니다. index 파일, objects, refs 폴더를 살펴보도록 하겠습니다.

.git 디렉토리는 Git 저장소의 핵심 데이터를 관리하는 폴더로, Git의 모든 버전 관리 정보가 이 디렉토리에 저장됩니다. 이 디렉토리 안에는 다양한 파일과 폴더가 있으며, 그중에서 index 파일, objects 폴더, refs 폴더는 중요한 역할을 합니다.

1. index 파일

Git의 index 파일(스테이지 영역 또는 캐시라고도 함)은 작업 디렉토리에서 변경된 파일들의 정보를 추적합니다. 이 파일은 커밋하기 전에 어떤 파일들이 스테이지에 올라와 있는지 관리하며, 다음 커밋에 포함될 파일들을 결정합니다.

  • encoding이 잘 안되있지만 현재 staging 영역에 어떤 파일이 있는지 확인할 수 있습니다.

2. objects 폴더

Git의 모든 데이터는 objects 폴더에 저장됩니다. 이 폴더는 Git의 데이터베이스로, 저장된 모든 파일, 디렉터리 구조, 커밋 등은 이곳에 저장됩니다. Git은 데이터의 내용에 따라 고유한 SHA-1 해시를 생성하고, 이 해시를 기반으로 objects 폴더에 데이터를 저장합니다.

  • 구성: objects 폴더는 수많은 하위 폴더와 파일들로 구성됩니다. 각 폴더는 SHA-1 해시의 앞 두 글자를 이름으로 하며, 해당 폴더 내에 해시의 나머지 부분을 파일 이름으로 하여 데이터를 저장합니다.
    블롭(blob), 트리(tree), 커밋(commit)로 구성되어 있습니다.

3. refs 폴더

refs 폴더는 Git에서 브랜치, 태그, 그리고 기타 참조(refs)들을 관리합니다. 이 폴더는 저장소의 브랜치와 태그가 특정 커밋을 가리키도록 하여, 작업을 쉽게 추적하고 관리할 수 있도록 합니다.

  • 구성:
    heads: 로컬 브랜치들을 저장합니다. 각 파일은 브랜치의 이름이며, 파일 내용은 해당 브랜치의 마지막 커밋의 해시입니다.
    tags: 태그들을 저장합니다. 각 파일은 태그의 이름이며, 파일 내용은 태그가 가리키는 커밋의 해시입니다.
    remotes: 원격 저장소의 브랜치들을 추적합니다. 원격 저장소의 브랜치 정보는 여기에서 관리되며, origin/main과 같은 참조를 포함합니다.

이제 git 폴더에 주요 파일 및 폴더의 역할에 대해서 알아봤습니다.
그러면 파일을 어떤 형태로 저장하고 버전 별로 파일의 형태를 어떻게 기록할까요?

SHA 해시

Git은 데이터를 저장할 때 SHA-1 해시를 사용해 160비트(40자)의 체크섬을 생성하고, 이를 고유 식별자로 사용했습니다(그럼 지금은..? 👀👀👀👀👀)
이 해시는 File, Directory tree, commit 등 모든 데이터를 식별하고 관리하는 데 활용됩니다.

Git은 내용을 주소로 활용하는 파일 시스템입니다.
데이터를 키-밸류 데이터베이스 형태로 저장합니다.파일 내용을 기준으로 해시값을 생성하고 해당 해시 값을 활용하여 파일을 저장합니다.

구체적으로, SHA-1 해시를 키로 삼아 .git/objects/ 디렉터리 아래에 해시의 앞 두 자로 하위 디렉터리를 만들고, 나머지 38자를 파일명으로 사용해 데이터를 저장합니다. 이렇게 저장된 데이터는 나중에 동일한 해시로 찾아낼 수 있어, 버전 관리 시스템의 기반을 이룹니다.

  • 아래 그림은 objects 폴더 구조입니다. 앞 두글자로 하위 디렉토리로 만들고 나머지 38자를 파일명으로 데이터를 저장하는 것을 알 수 있습니다.

Git은 SHA-1을 사용하였으나 2017년에는 실제로 SHA-1 충돌을 발견하는 것이 가능해졌습니다. 따라서 Git은 이러한 문제에 대응하기 위해 SHA-1와 SHA-256를 병행하고 있습니다.

SHA-256은 SHA-1보다 더 긴 해시 값을 생성하며, 현재까지 알려진 공격 방법들에 대해 더 강력한 보안을 제공합니다. (더 자세한 내용은 여길 클릭하세요!)

zlib

Git은 .git/objects/에 저장하는 파일들은 zlib으로 압축합니다.

Git의 객체(블롭, 트리, 커밋 등)는 SHA-1 해시를 기반으로 생성된 후 zlib을 통해 압축되어 .git/objects/ 디렉터리에 저장됩니다. 각 객체는 고유한 SHA-1 해시를 키로 가지며, 이 해시를 통해 파일을 찾고, 압축을 해제하여 데이터를 복원할 수 있습니다.

  • Swift에서도 NSData를 활용하여 zlib로 압축, 압축해제할 수 있습니다.

Object

Git에서 object는 저장소 내의 데이터 및 메타데이터를 관리하는 기본 단위입니다. Git의 객체로는 blob, tree, commit가 있습니다. 이 객체들은 Git의 버전 관리 기능의 핵심을 이루며, 각각 파일의 내용, 디렉터리 구조, 그리고 커밋 히스토리를 관리합니다.

1. Blob (Binary Large Object)

Blob 객체는 Git에서 특정 시점의 파일의 내용을 저장하는 데 사용됩니다.
파일 자체에 대한 정보만을 담고 있으며, 파일 이름이나 경로는 포함하지 않습니다.

  • blob 객체는 단순히 파일의 내용을 zlib으로 압축한 후 SHA-1 또는 SHA-256 해시를 계산하여 고유한 해시 값으로 저장됩니다.
  • 이 해시는 파일 내용이 동일하다면 동일한 값을 가지므로, 동일한 파일이 여러 번 추가되더라도 Git은 중복 저장을 방지합니다. 파일의 내용이 blob으로 저장되고, 이 blob 객체는 다른 Git 객체(예: tree)에서 참조됩니다.

2. Tree

Tree 객체는 Git에서 특정 시점의 디렉터리의 구조를 표현합니다.

하나의 tree 객체는 특정 시점의 디렉터리를 나타내며, 해당 디렉터리 안에 포함된 파일과 서브 디렉터리를 기록하고 디렉터리 내 파일(혹은 하위 디렉터리)에 대한 참조(즉, blob 또는 다른 tree 객체에 대한 포인터)를 포함합니다.

각 tree 객체는 해당 디렉터리 내의 파일 및 하위 디렉터리의 목록을 포함하며, 각각의 항목에 대해

  • 파일 이름
  • 파일 모드(권한)
  • 참조하는 객체(즉, blob 또는 tree 객체)의 해시 값

을 저장합니다.

3. Commit

Commit 객체는 Git의 버전 관리에서 가장 중요한 객체로, 특정 시점에서의 프로젝트 상태(즉, 스냅샷)를 기록합니다. commit 객체는 프로젝트의 히스토리를 추적하고, 각 커밋은 이전 커밋들과의 관계를 나타내며, 브랜치와 태그의 기반이 됩니다.

구성

  1. 트리
    커밋이 참조하는 tree 객체로, 이 tree는 해당 시점의 프로젝트 전체 디렉터리 구조와 파일 내용을 나타냅니다.
  2. 부모 커밋
    이전 커밋에 대한 참조로, 이를 통해 Git은 커밋 히스토리를 연결하고 추적할 수 있습니다. 병합 커밋의 경우 부모 커밋이 둘 이상일 수 있습니다.
  3. 메타데이터
    작성자(author), 커밋 작성 시각, 커밋 메시지 등의 정보를 포함합니다.
  • 예시: 새로운 기능을 개발하고 git commit을 실행하면, Git은 현재 디렉터리 구조와 파일 상태를 나타내는 tree 객체를 생성하고, 이를 참조하는 commit 객체를 만듭니다. 이 commit 객체는 이전 커밋과 연결되어 프로젝트 히스토리의 일부분이 됩니다.

이렇게 Git의 object는 각각 파일의 내용, 디렉터리 구조, 커밋 히스토리를 관리하는 역할을 합니다. 이 객체들이 서로 참조 관계를 형성하여 Git이 효율적으로 데이터를 관리하고, 복잡한 버전 관리를 가능하게 해줍니다.

0개의 댓글