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 명령도 내부에서는 비슷한 절차를 따른다.
- 브랜치를 Checkout(switch) 하면
- HEAD가 바뀐 브랜치를 가리킨다.
- 변경한 브랜치가 다른 커밋 객체를 바라보고 있는 경우 해당 커밋 객체의 스냅샷을 Index에 넣는다.
- 그리고 Index의 내용을 워킹 디렉토리로 복사한다.
✅ Git Reset Workflow
- reset 명령은 위 동작흐름을 이해하면 쉽게 알 수 있다.
- 일단 예시로 file.txt 파일 하나를 수정하고 커밋 한 후 이것을 세 번 반복한 히스토리(v1, v2, v3)를 가지고 살펴보자.
- reset 명령이 동작하는 방식은 세 단계로 이루어 진다.
≫ 1단계 : HEAD의 이동
- reset 명령이 하는 첫 번째 일은 HEAD 브랜치를 이동시킵니다.
- checkout 명령처럼 HEAD가 가리키는 브랜치를 바꾸지는 않는다. HEAD는 계속 현재 브랜치를 가리키고 있다. - 현재 브랜치가 가리키는 커밋을 바꾼다.
- 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 파일로 돌리는 과정이다.
- HEAD가 가리키는 브랜치를 reset 하고자 하는 커밋객체(v2)를 가리킨다.(--soft 옵션이 붙으면 여기까지 진행)
- 기존 v3 파일의 git commit 명령어를 되돌린다 : v3 파일의 staged 상태 - Index를 HEAD가 가리키는 상태로 만든다. (--mixed 옵션을 붙이거나 아무 옵션도 붙지 않았으면 여기까지 진행)
- 기존 v3 파일의 git add 명령어를 되돌린다 : v3 파일의 unstaged 상태 - 워킹 디렉토리를 Index의 상태로 만든다.(--hard 옵션이 붙으면 여기까지 진행)
- 기존 v3파일을 삭제하고 v2 파일로 변경한다.
v3 파일을 commit 까지 했다면 reflog를 통해 다시 복원 할 수 있습니다.