Git

안준성·2024년 7월 15일
0

Git

목록 보기
3/3
post-thumbnail

Gist와 Xcode로 프로젝트를 진행하면서
로컬과 원격을 둘 다 수정하는 일이 있었는데
로컬을 수정하고 push를 하려니 pull을 먼저 받으라고 했다.
pull을 받고 충돌 해결을 하려는데 좀 실수를 해서
코드가 날라가버렸다.

잠시 당황했지만 git reflog를 통해 HEAD의 변경내역을 확인할 수 있어 원하는 지점으로 복구가 가능했다.

이번 일을 겪으면서 기존에 어렴풋이 알고 있던
Git의 자세한 동작원리에 대해 알아보고자 한다.


Git

git은 그 유명한 Linus Torvalds씨가 만든
버전 관리 시스템이다.

역사는 뭐 알 거 없고 바로 동작원리에 대해 배워보자.

기본 개념

  1. 스냅샷
    Git은 파일의 변화를 스냅샷으로 저장한다.
    각 커밋은 파일 시스템의 스냅샷으로, 변경된 파일만 저장하는 방식이다.

  2. 커밋
    작업 내용의 스냅샷을 저장하는 단위이다.
    각 커밋은 고유한 SHA-1 해시를 가지며, 부모 커밋과의 연결고리를 통해 이력을 관리한다.

  3. 브랜치
    브랜치는 독립적인 작업을 위한 포인터이다.
    새로운 기능을 개발하거나 버그를 수정할 때 별도의 브랜치를 생성하여 작업한다.
    브랜치는 매우 가볍고, 쉽게 생성하고 삭제할 수 있다.

  4. 병합
    여러 브랜치의 변경 내용을 하나의 브랜치로 통합하는 과정이다.
    병합 충돌이 발생할 수 있으며, 이를 해결하기 위해 충돌을 수동으로 수정해야 한다.

  5. 분산 저장소
    Git은 중앙 서버가 아닌 분산 저장소를 사용한다.
    각 클라이언트는 전체 저장소의 복사본을 가지고 있으며, 이를 통해 네트워크가 없을 때도 작업을 계속할 수 있다.
    변경 내용을 푸시(push)하거나 풀(pull)하여 다른 저장소와 동기화할 수 있다.

잘 숙지했으면 이제 git repository를 파고
add -> commit -> push의 과정을 거치면서
각각 어떠한 변화가 일어나는지에 대해 알아보자.

동작 원리

git은 자주 공부했는데도 항상 헷갈리는거 같다.
staging area 어쩌구~ working directory 어쩌구~
이놈들은 도대체 뭘까

git init

먼저 git repository를 생성하거나 git init을 입력하면 .git 폴더가 생긴다.

git은 파일의 변경 이력을 관리하기 위해 다양한 메타데이터와 객체를 사용하는데 이들은 모두 .git 폴더 내에 저장된다.

git objects

git은 네 가지 주요 객체 유형을 사용하여 데이터를 저장하며 각 객체는 해시를 통해 고유하게 식별된다.

  1. blob

    • 파일의 내용자체를 저장하는 객체다. 각각의 파일은 블롭 객체로 저장된다.
    • 파일의 내용과 그 파일의 크기를 내용으로 가진다.
    • add 시점에 변경된 파일의 블롭 객체가 생성된다.
  2. tree

    • 디렉토리의 구조를 저장하며, 파일과 다른 트리(디렉토리)를 가리킨다.
      각 항목은 블롭 객체나 다른 트리 객체로 연결된다.
    • 디렉토리 내의 파일과 하위 디렉토리에 대한 정보(파일 이름, 파일 모드, 블롭 또는 트리 객체의 해시)를 내용으로 가진다.
    • commit 시점에 생성된다.
  3. commit

    • 특정 시점의 프로젝트 상태를 나타내는 스냅샷이다.
      트리 객체를 가리키며, 커밋 메시지, 작성자 정보, 부모 커밋 등의 메타데이터를 포함한다.
    • 가리키는 트리 객체의 해시, 작성자 정보, 커밋 메시지, 부모 커밋의 해시(있을 경우)를 내용으로 가진다.
    • commit 시점에 생성된다.
  4. tag

    • 특정 커밋을 가리키는 객체로, 주로 릴리즈 버전을 표시하는 데 사용된다.

예시

이해를 돕기 위해 실제와 비슷한 예시를 가져왔다.

mkdir myproject
cd myproject

# 상위 디렉토리에 파일 생성
echo "Hello, Git!" > hello.txt
echo "This is README." > README.md

# 하위 디렉토리에 파일 생성
mkdir src
echo "main" > src/main.c

# Git 리포지토리 초기화
git init

# 첫 번째 커밋
git add .
git commit -m "first commit"

위와 같은 명령어를 입력했을 때 git에서 일어나는 일은 다음과 같다.

git add .

add 명령어는 작업 디렉토리의 변경 사항을 스테이징 영역에 추가한다.
이 시점에서 Git은 변경된 파일의 블롭 객체를 생성한다.

  • hello.txt의 블롭 객체
  • README.md의 블롭 객체
  • src/main.c의 블롭 객체
git commit -m "first commit"

commit 명령어는 스테이징 영역에 있는 변경 사항을 새로운 커밋으로 저장한다.
이 시점에서 Git은 다음과 같은 객체를 생성한다.

  1. 루트 트리 객체
    hello.txt, README.md 파일과 src 디렉토리를 가리킨다.

    040000 tree (src 디렉토리의 트리 객체 해시)
    100644 blob (hello.txt의 블롭 객체 해시)    hello.txt
    100644 blob (README.md의 블롭 객체 해시)    readme.md

    여기서 040000과 100644는 Unix 파일 시스템의 파일모드를 나타내며, 각각 디렉토리와 일반 파일을 의미한다.

  2. src 트리 객체
    src/main.c 파일을 가리킨다.

     100644 blob (src/main.c의 블롭 객체 해시)   main.c
  3. commit 객체
    루트 트리 객체를 가리킨다.
    작성자 정보, 타임스탬프, 부모 커밋(있을 경우), 커밋 메시지에 대한 정보를 포함한다.

    tree (루트 트리 객체 해시)
    author Your Name <you@example.com> (타임스탬프)
    committer Your Name <you@example.com> (타임스탬프)
    
    first commit

두 번째 커밋

# 파일 수정 및 추가
echo "append to README" >> README.md
echo "header" > src/main.h

# 두 번째 커밋
git add .
git commit -m "second commit"

git add . 를 하면 변경된 파일 README.md, src/main.h의 블롭 객체를 생성한다.

git commit -m "second commit을 실행 시,
새로운 트리 객체와 커밋 객체를 생성한다.

  1. 새로운 루트 트리 객체
    hello.txt, 새로운 README.md 파일과 src 디렉토리를 가리킨다.

    040000 tree (새로운 src 디렉토리의 트리 객체 해시)
    100644 blob (hello.txt의 기존 블롭 객체 해시)    hello.txt
    100644 blob (readme.md의 새로운 블롭 객체 해시) readme.md
  2. 새로운 src 트리 객체
    기존의 src/main.c 파일과 새로운 src/main.h 파일을 가리킨다.

    100644 blob (src/main.c의 기존 블롭 객체 해시)   main.c
    100644 blob (src/main.h의 블롭 객체 해시)        main.h
  3. 새로운 commit 객체
    새로운 루트 트리 객체를 가리킨다.

    tree (새로운 루트 트리 객체 해시)
    author Your Name <you@example.com> (타임스탬프)
    committer Your Name <you@example.com> (타임스탬프)
    
    second commit

이제 위 설명에서 등장한 staging area나 working directory 등과 같은 깃의 area들에 대해 알아보자.

Git의 3가지 영역

git은 파일의 변경 이력을 관리하기 위해
Working directory, Stagin area(=Index), Repository 총 세 가지 영역을 가진다.

각각을 설명하기에 앞서 전체 적인 그림을 한 번 보자.

이렇게 git은 저장 공간을 논리적으로 분리한다.

작업을 하는 공간과(working directory)
임시로 저장하는 공간(stage),
실제로 저장하는 공간(repository)을 통해
git은 파일의 변경 이력을 효율적으로 관리한다.

Working Directory

현재 작업 중인 디렉토리와 실제 파일들이 위치한 곳을 의미한다.

사용자는 이곳에서 파일을 수정하고 개발을 진행한다.

워킹 디렉토리의 파일들은 추적 또는 비추적 상태일 수 있는데 말 그대로 git이 관리 중인 파일과, 새로운 파일이거나 gitignore 되어 아직 관리하지 않는 상태를 뜻한다.

Staging Area

Index라고도 하며,
커밋하기 전에 파일의 스냅샷을 임시로 저장하는 공간이다.

git add 명령어 시 워킹 디렉토리에서 변경된 파일들을 staging area에 올린다.
이는 다음 커밋에 포함될 파일들을 미리 선택하는 과정이다.

Repository

모든 커밋과 메타데이터가 저장되는 곳으로, .git 내부에 존재한다.

앞서 설명한 객체들을 저장하며,
브랜치의 이름과 커밋 해시를 연결한 Refs,
현재 체크아웃된 브랜치를 가리키는 포인터인 HEAD로 구성되어 있다.

위의 그림을 보면 localremote가 있는데
말 그대로 내가 작업 중인 local에서 commit을 하면 local repository에 올라가고
이걸 push 하면 remote repository로 올라가는 것이다.

git checkout

git에서 자주 쓰는 명령어인데

repository의 특정 커밋을 working directory로 체크아웃하는 명령어이다.
이는 repository의 파일들을 working directory로 복사하여 현재 working directory를 업데이트 하는 과정이다.


위와 같이 Git에 대해 알아보았는데
HEAD라던가 브랜치같은 내용들까지 담기엔 무리가 있어
다음에 하고 싶은 마음이 들면 정리하겠다.

profile
안녕하세요

0개의 댓글