GIT

[Git] Git 제대로 알기(2) - [Commit / Reset] Work Flow

미도반 2023. 3. 29. 14:56

 

해당 글의 이해를 돕기 위해 이전 글(Git의 핵심 및 상태)을 꼭 읽고 보는걸 추천한다.
[Git] Git 제대로 알기(1) - Git의 핵심 및 상태

 


 

 Git이 관리하는 것


Git은 서로 다른 세가지 트리를 관리하는 관리자이다. 여기서 "트리" 란 "파일의 묶음" 이라고 이해하면 된다.
세 가지  트리는 HEAD, Index, Working Directory 이다.

  • 세 가지  트리의 역할 
    • HEAD : 마지막 커밋 스냅샷, 다음 커밋의 부모 커밋
    • Index : 다음에 커밋할 스냅샷
    • Working Directory : 샌드박스

 

 

 


 

 

HEAD


  • HEAD는 현재 활성화된 브랜치를 가리키는 포인터이다. 파일의 형태로 존재하며, 위치는 .git/HEAD 에 있다.
  • .git/HEAD 파일을 Editer로 열어보면 현재 어떤 브랜치를 참조하고 있는지를 확인할 수 있다.
ref: refs/heads/develop

 

  • 위 내용을 보면 HEAD는 현재 develop 브랜치를 참조하고 있다.
  • HEAD가 참조(ref)하는 'refs/heads/develop' 파일을 열어보면 아래와 같이 특정 '스냅샷의 체크섬'(이하 스냅샷)을 확인할 수 있다.

  • 각 브랜치의 heads에서 참조하는 스냅샷은 해당 브랜치의 마지막 커밋의 스냅샷 이다.
  • 즉, 위 체크섬은 develop 브랜치의 마지막 커밋의 스냅샷이다.

 

  • git log 명령어를 통해 확인을 해보자.
  • 아래 commit 된 이력을 보면 가장 최신 커밋의 체크섬과 'refs/heads/develop' 파일에 저장된 체크섬이 같다.
  • 또한 체크섬 옆에 '(HEAD -> develop)'에서 보이는듯 HEAD가 가리키는 브랜치는 develop 인 것 또한 확인 된다.

 

결론, HEAD는 "현재 (활성화된) 브랜치 마지막 커밋의 스냅샷" 이다.

 

 


 

 

Index( = Staging Area )


  • 용어적으로 혼란을 막기 위해 Index는 staging area와 같다고 생각하면 된다.
  • Index는 바로 다음에 커밋할 것들을 말한다.
  • 커밋할 것들을 모아 놓은 단계를 Staging Area 단계라고 한다.
  • 사용자가 git commit 명령을 실행했을 때 Git이 처리할 것들이 있는 곳이다.
  • 실제 index는 디렉토리 구조는 아니고 파일 형태로 존재한다.
  • 위치는 .git/index 에 있다.

 

 


 

 

 워킹 디렉토리( = Work Tree)


  • 용어적으로 혼란을 막기 위해 워킹 디렉토리는 작업트리(Work Tree)와 같다고 생각하면 됩니다.
  • 실제 의미는 다르지만 git을 사용하는 사람 입장에서 큰 의미가 없다.
  • 워킹 디렉토리는 실제 프로젝트의 파일들이 있는 곳이다.
  • 파일 및 디렉토리를 추가하거나 변경한 내용들이 존재한다.  
  • 모든 변경 사항은 스테이징 영역에 추가할 때까지 작업 디렉토리에 남아 있다.

 

 


 

 

Git Commit Workflow

Commit 한 파일을 Git에서는 어떻게 저장하는가?


≫ 파일 추가 → index Commit 되는 과정

1. 파일을 추가(version1 = v1)

  • 파일을 추가하면 워킹 디렉토리에 파일이 추가 된다.
  • HEAD는 아직 존재하지 않는 브랜치를 가리킨다.(master 는 아직 없음)

 

 

2. git add 명령어 실행

  • 워킹 디렉토리의 내용을 Index로 복사한다.

 

 

3. git commit 명령어 실행

  • Index의 내용을 스냅샷으로 영구히 저장하고 그 스냅샷을 가리키는 커밋 객체를 만든다.
  • 그리고 master 가 그 커밋 객체를 가리키도록 한다.

** 이때 git status 명령을 실행하면 아무런 변경 사항이 없다고 나온다. 세 트리 모두가 같기 때문이다.

 


 

≫ 파일 변경 → index  Commit 되는 과정

1. 기존 파일 변경(version 2 = v2)

  • 기존 파일을 변경하면 워킹 디렉토리에 있는 파일이 수정된다.
  • 이 시점에 git status 명령을 실행하면 "Changes not staged for commit" 문구 아래에 빨간색으로 된 파일을 볼 수 있다.
  • Index와 워킹 디렉토리가 다른 내용을 담고 있기 때문이다.

 

 

2. git add 명령어 실행

  • git add 명령어를 실행하면 워킹 디렉토리에 있는 변경 사항을 Index에 올려준다.
  • 이 시점에서 git status 명령을 실행하면 “Changes to be committed” 아래에 파일 이름이 녹색으로 변한다.
  • Index와 HEAD의 다른 파일들이 여기에 표시된다.
  • 즉 다음 커밋할 것과 지금 마지막 커밋이 다르다는 뜻이다.

 

 

3. git commit 명령어 실행

  • git commit 명령을 실행하면 Git은 새로운 커밋을 생성하고 HEAD가 가리키는 브랜치가 새로운 커밋을 가리키도록 업데이트한다.
  • 이제 git status 명령을 실행하면 아무것도 출력하지 않습니다. 세 개의 트리의 내용이 다시 같아졌기 때문이다.

 

 

** 브랜치를 바꾸거나 Clone 명령도 내부에서는 비슷한 절차를 따른다.

  1. 브랜치를 Checkout(switch) 하면
  2. HEAD가 바뀐 브랜치를 가리킨다.
  3. 변경한 브랜치가 다른 커밋 객체를 바라보고 있는 경우 해당 커밋 객체의 스냅샷을 Index에 넣는다.
  4. 그리고 Index의 내용을 워킹 디렉토리로 복사한다.

 

 


 

 

 Git Reset Workflow


  • reset 명령은 위 동작흐름을 이해하면 쉽게 알 수 있다.
  • 일단 예시로 file.txt 파일 하나를 수정하고 커밋 한 후 이것을 세 번 반복한 히스토리(v1, v2, v3)를 가지고 살펴보자.

 

  • reset 명령이 동작하는 방식은 세 단계로 이루어 진다.

 

1단계 : HEAD의 이동

  1. reset 명령이 하는 첫 번째 일은 HEAD 브랜치를 이동시킵니다.
    - checkout 명령처럼 HEAD가 가리키는 브랜치를 바꾸지는 않는다. HEAD는 계속 현재 브랜치를 가리키고 있다.
  2. 현재 브랜치가 가리키는 커밋을 바꾼다.
    - git reset 9e5e6a4 명령은 master 브랜치가 9e5e6a4 를 가리키도록 한다.

 

  • 위 결과는 git reset --soft HEAD~ 명령어 실행의 결과와 동일하다.
    • HEAD~는 HEAD의 부모 커밋을 의미한다.
    • git reset --soft HEAD~ 명령어를 실행하면 딱 여기까지 진행하고 동작을 멈춘다.
    • Index워킹 디렉토리는 그대로 놔두고 브랜치가 가리키는 커밋만 이전으로 되돌린다.
    • git commit 명령을 되돌리는 것이다.

 


 

2단계: Index 업데이트

  • 여기서 git status 명령을 실행하면 Index와 reset 명령으로 이동시킨 HEAD의 다른 점이 녹색으로 출력된다.
  • reset 명령은 Index를 현재 HEAD가 가리키는 스냅샷으로 업데이트 한다.

 

  • 위 결과는 git reset [--mixed] HEAD~ 명령어 실행의 결과와 동일하다.
    • --mixed 옵션을 주고 실행하면 reset 명령은 여기까지 동작하고 멈춘다. 그리고 Staging Area를 비운다.
    • reset 명령을 실행할 때 아무 옵션도 주지 않으면 기본적으로 --mixed 옵션으로 동작한다.
    • HEAD~ 옵션은 가장 최근의 커밋으로 되돌린다.
    • git commit 명령도 되돌리고 git add 명령까지 되돌리는 것이다.

 


 

3단계: 워킹 디렉토리 업데이트

  • reset 명령은 세 번째로 워킹 디렉토리까지 업데이트 한다.

 

  • 위 결과는 git reset --hard HEAD~ 명령어 실행의 결과와 동일하다.
    • --hard 옵션을 사용하면 reset 명령은 이 단계까지 수행한다.
    • git add 명령과 git commit 명령 그리고 워킹 디렉토리의 내용까지 되돌린다.
    • --hard 옵션은 reset 명령을 위험하게 만드는 유일한 옵션이다.
    • Git에는 데이터를 실제로 삭제하는 방법이 별로 없지만. 이 방법이 그 중 하나이다.
    • reset 명령을 어떻게 사용하더라도 쉽게 결과를 되돌릴 수 있지만, --hard 옵션은 되돌리는 것이 불가능하다.
    • 이 옵션을 사용하면 워킹 디렉토리의 파일까지 강제로 덮어쓴다.
    • 이 예제는 파일의 v3버전을 아직 Git이 커밋으로 보관하고 있기 때문에 reflog 명령어를 이용해서 다시 복원할 수 있지만 만약 커밋한 적 없다면 Git이 덮어쓴 데이터는 복원할 수 없다.

 

 


 

 

 Reset Workflow 정리


  • reset 명령은 정해진 순서대로 세 개의 트리를 덮어써 나가다가 옵션에 따라 지정한 곳에서 멈춘다.
  • v3 파일을 v2 파일로 돌리는 과정이다.
  1. HEAD가 가리키는 브랜치를 reset 하고자 하는 커밋객체(v2)를 가리킨다.(--soft 옵션이 붙으면 여기까지 진행)
    - 기존 v3 파일의 git commit 명령어를 되돌린다 : v3 파일의 staged 상태
  2. Index를 HEAD가 가리키는 상태로 만든다. (--mixed 옵션을 붙이거나 아무 옵션도 붙지 않았으면 여기까지 진행)
    - 기존 v3 파일의 git add 명령어를 되돌린다 : v3 파일의 unstaged 상태
  3. 워킹 디렉토리를 Index의 상태로 만든다.(--hard 옵션이 붙으면 여기까지 진행)
    - 기존 v3파일을 삭제하고 v2 파일로 변경한다.

v3 파일을 commit 까지 했다면 reflog를 통해 다시 복원 할 수 있습니다.