주제별 모험기/Build Your Own X

[Build your own git] #1 git init 과 .git 폴더의 구조

Kwon_Ori 2025. 1. 1. 13:00

가장 먼저 구현한 기능은 'git init' 이었다.

https://git-scm.com/docs/git-init

 

Git - git-init Documentation

-q --quiet Only print error and warning messages; all other output will be suppressed. --bare Create a bare repository. If GIT_DIR environment is not set, it is set to the current working directory. --object-format= Specify the given object (hash algorithm

git-scm.com

 

git init 은 git 에 사용할 로컬 저장소를 만드는 기능으로 특정 위치에 .git 이라는 폴더를 생성하여 해당 위치가 git 저장소로 활용되도록 한다. .git 폴더에는 버전 관리를 위한 폴더 및 파일들이 포함되어 있는데, 대표적으로 objects 폴더, refs 폴더, HEAD 파일이 있다.

 

objects 폴더

버전 관리되는 파일들의 정보가 담겨있는 Git Object 파일들이 저장되는 폴더이다.

Git Object 는 blob, tree 등 용도에 따른 종류가 있던데, 차차 진행해나가면서 하나씩 정리할 것이다.

 

refs 폴더

외우기 힘든 SHA-1 값을 외우기 쉬운 이름으로 된 포인터로 연결한 것을 Git Refs 라고 하는데, 쉬운 이름과 연결된 SHA-1 값을 저장하는 폴더이다.

 

예)  특정 커밋을 구분하는 해시값인 1a410efbd13591db07496601ebc7a059dd55cfe9 대신 .git/refs/heads/master 에 해시값을 저장하여 head/master 라는 이름만으로 해당 커밋을 찾을 수 있도록 만들어준다. 실제로 해당 경로에서 master 파일을 열어보면 40자의 SHA-1 해시값이 기록되어 있다.

 

HEAD 파일

Git 이 마지막 커밋의 SHA-1 을 값을 알기 위한 정보가 담겨있는 파일이다.

위의 refs 와 다르게 간접(symbolic) refs 라서 직접 특정한 SHA-1 값을 가리키는게 아닌 다른 Refs 를 가리키므로 고유한 SHA-1 값이 존재하지 않는다. 이 파일을 열어보면 다음과 같이 마스터 브랜치의 마지막 커밋을 가리키는 Refs 가 명시되어 있으며 기본적으로는 다음과 같이 마스터의 마지막 커밋의 refs를 가리키고 있다. (이 값은 git init 구현에 사용될 예정) 

ref: refs/head/master 


구현할 기능은 다음과 같다.

1) .git 폴더를 생성한다.

2) 그외 필요한 폴더(objects, refs) 를 생성한다.

3) .git 폴더 안에 HEAD 파일을 생성하고 마스터의 헤드를 가리키는 간접 레퍼런스를 기록한다.

 

특별한 알고리즘이나 라이브러리 없이 대부분 언어에서 표준으로 지원하는 파일 입출력으로 구현할 수 있다.

codecrafters 에서는 처음에 이걸 굳이 고민하면서 구현할 가치가 없다고 생각했는지, 초기 코드에서 주석만 해제하도록 만들어뒀다.

 

아래는 C++ 로 작성된 코드이며, codecrafters 서비스 특성상 bash, cmake, vckpkg 를 사용하여 빌드와 테스트를 하므로 

사용법에 익숙해져야 할 것 같다.

 

    std::string command = argv[1];
    
    if (command == "init") {
        try {
            std::filesystem::create_directory(".git");
            std::filesystem::create_directory(".git/objects");
            std::filesystem::create_directory(".git/refs");
    
            std::ofstream headFile(".git/HEAD");
            if (headFile.is_open()) {
                headFile << "ref: refs/heads/main\n";
                headFile.close();
            } else {
                std::cerr << "Failed to create .git/HEAD file.\n";
                return EXIT_FAILURE;
            }
    
            std::cout << "Initialized git directory\n";
        } catch (const std::filesystem::filesystem_error& e) {
            std::cerr << e.what() << '\n';
            return EXIT_FAILURE;
        }
    }

 

(다음 글에 계속)