Git은 서로 다른 세 개의 트리를 관리하는 컨텐츠 관리자라고 할 수 있다. 트리(tree)란 실제로 파일의 묶음을 의미한다. 블롭(blob)은 깃에서 개별 파일을 일컫는 단어이다.


세 개의 트리

트리 역할
HEAD 마지막 커밋 스냅샷, 다음 커밋의 부모가 되는 커밋
index 다음에 커밋할 스냅샷
워킹 디렉토리 샌드박스


(1) HEAD

: 현재 브랜치를 가리키는 포인터를 HEAD라고 한다. 이 때의 브랜치는 가장 최신의 커밋을 가리키고 있다. HEAD가 가리키는 커밋은 바로 다음 커밋의 부모라고도 부른다.

  • HEAD의 스냅샷
    • plumbing 명령(저수준 명령)을 사용하여 HEAD의 위치를 볼 수 있다. - cat-file, ls-tree등의 명령어를 사용하여 HEAD의 주소값을 출력해볼 수 있다.
      <git cat-file -p HEAD>
      tree b70c5e654741b75f33318eff6e01cc0b4bb99292
      author juliahwang-imac <[email protected]> 1494936369 +0900
      committer juliahwang-imac <[email protected]> 1494936369 +0900
    
      <git ls-tree -r HEAD>
      100644 blob f748449c30d29e135e94854872d8948ae2688a9a stash1.txt
      100644 blob e1e398af1512dbb124ca069d9ca543f0edf337ea stash2.txt
    


(2) Index

: 바로 다음에 커밋할 파일들을 의미한다. 즉, Staging area에 있는 파일들이다.

워킹 디렉토리에서 마지막으로 체크아웃한 브랜치의 파일목록과 파일 내용으로 채워진다. 인덱스는 트리가 아니라 평평한 구조로 되어있다(엄밀히 트리구조는 아니다.)

  • 여기서 파일 내용을 수정한 후 commit하면 index는 새로운 커밋으로 변환된다.
  • 커밋을 시키고 남은 것이 없으므로…
100644 9cd125baa1b6833ae1e9743d6ce7a7811eb93d64 0	.gitignore
100644 1e58f894f8d97f10a1d37a6f0b6bea71c7314ed7 0	stash1.txt
100644 85e147a0dbaae5dff433a5116ed4781359204871 0	stash2.txt


(3) 워킹 디렉토리

: HEAD와 Index가 .git에 저장하는 형태는 알아보기 어려우므로 tree로 구조화해서 보여준다.

워킹 디렉토리는 실제 파일로 눈에 보이기 때문에 편집하기 수월하다. 샌드박스. 커밋하기 전에는 staging area에 올려놓고 수정, 변경이 가능한 상태다.

샌드박스?

실험적 의미의 프로그램을 연결하는 곳을 지칭하기도 한다. 따라서, 실제 적용 이전에 먼저 샌드박스로 연결하여 테스트하라는 것은 작동은 실제와 같으나 금융상 또는 법률상의 실제행위는 일어나지 않는 것을 뜻한다.

tree
.
├── stash1.txt
└── stash2.txt

0 directories, 2 files


워크플로우

워크플로우


(1) 워킹 디렉토리에 파일 생성

  • v1의 파일을 생성하고 git init하면 git 저장소가 생긴다.
  • 이 때, HEAD는 아직 생성되지 않은 브랜치를 가리킨다.

워크플로우1


(2) staging area로 워킹 디렉토리 내용을 index로 복사

  • 워킹디렉토리에서 git add로 staging area로 이동하면 index에 저장.

워크플로우2


(3) 커밋

  • git commit을 통해 index의 내용을 스냅샷으로 영구히 저장.
  • 스냅샷을 가리키는 커밋 객체를 만든다.
  • 이 때, ==브랜치 ‘master’가 커밋 객체를 가리키게 된다==
  • git status 를 확인하면 아무런 변경사항이 없다. 세 트리가 같은 내용이므로.

워크플로우3


(4) 파일 수정(Modified)

  • 워킹 디렉토리 파일을 고친다.(v2)
  • 이 경우 워킹디렉토리의 내용이 index의 내용과 달라졌기 때문에
  • changes not staged for commit으로 출력된다.

워크플로우4


(5) 수정파일 commit하기

  • git add를 하면 상태가 Changes to be commited로 바뀐다.
  • 즉, 다음 커밋할 것과 마지막 커밋이 다르다는 말.
  • git commit 해준다.

워크플로우5


(6) 브랜치를 바꿀경우? clone할 경우?

  • 같은 원리로, 브랜치를 checkout하면
  • HEAD가 새로운 브랜치를 가리키고
  • Index에는 새로운 커밋 스냅샷이 놓인다.
  • 그리고 Index의 내용을 워킹디렉토리로 복사한다.

워크플로우6


RESET의 역할

파일하나를 수정하고 커밋하는 일을 3번 반복했을 때 아래와 같다.

리셋의역할


(1) HEAD 이동 git reset --soft HEAD~

  • reset은 제일먼저 HEAD 브랜치를 이전 커밋으로 이동시킨다.
  • 단, 브랜치가 이동하는 것은 아니다(checkout 아님)
  • git reset 9e6e6a4 -> master가 9e6e6a4체크썸을 가진 커밋으로 이동한다.
  • git reset --soft HEAD~를 이용하면 HEAD 이동만 하고 멈춘다.

리셋의역할1


(2) Index 업데이트 git reset --mixed HEAD~

  • git status 명령을 실행하면 index와 HEAD의 다른 점이 녹색으로 출력된다.
  • git reset --mixed HEAD를 쓰면 index에 HEAD의 스냅샷을 업데이트한다.
  • ==즉, commit을 되돌리고 staging area를 비운다는 말은 git add까지 reset 한다는 뜻이다.==

리셋의역할2


(3) 워킹 디렉토리 업데이트 git reset --hard HEAD~

  • git reset --hard HEAD~ 명령은 디렉토리의 내용까지 되돌릴 수 있다.
  • git에서 데이터를 삭제시킬 수 있는 명령이므로 되돌릴 수 없으니 주의해야한다.

리셋의역할3


reset 명령은 정해진 순서대로 세 개의 트리를 덮어써 나가다가 옵션에 따라 지정한 곳에서 멈춘다.

  1. HEAD가 가리키는 브랜치를 옮긴다. (–soft 옵션이 붙으면 여기까지)
  1. Index를 HEAD가 가리키는 상태로 만든다. (–hard 옵션이 붙지 않았으면 여기까지)
  1. 워킹 디렉토리를 Index의 상태로 만든다.


경로를 주고 reset하기

reset할 때 경로를 지정하면 HEAD 이동을 건너뛰고 정해진 경로의 파일에만 reset을 적용한다. index나 워킹디렉토리에 일부분만 갱신할 수 있다. 따라서 특정 파일만 HEAD에서 index로 복사하는 기능이다.

git reset file.txt = git reset --mixed HEAD file.txt

  1. 브랜치를 옮긴다. (건너뜀)
  2. index를 HEAD가 가리키는 상태로 만든다. (여기서 멈춤)
  3. 이 명령은 워킹디렉토리의 파일을 unstaged 상태로 만든다. (가져와서 staging area에 붙여넣었으므로 최신 파일은 unstaged되는 것)

경로주고reset


$ git reset eb43bf -- file.txt`
  1. 커밋에서 v1파일을 가져와 인덱스에 붙여넣는다.
  2. index에 있는 파일을 커밋하면 head에는 v1의 정보가 저장된다.(=이전으로 복원된다.)

경로주고reset1


git reset --patch

  1. 패치옵션으로 Hunk단위로 unstaged 상태를 만들 수 있다.


커밋 합치기(squash)

: develop파일과 production파일을 합치고 싶을 때.

git reset --soft HEAD~2

  1. 2단계 이전의 커밋으로 HEAD가 이동한 상태.
  2. 최신버전의 워킹 디렉토리 파일을 add, commit한다.
  3. master브랜치는 최신 내용과 첫 v1이 합쳐진 파일을 가지게 된다.
  4. v2는 더이상 히스토리에 없다.

squash

squash


Checkout

(1) 경로가 없을 때

git checkout [브랜치명] : 브랜치로 이동한다.

차이 1

  • reset --hard와 달리 매우 안전하게 워킹디렉토리를 다룬다.
  • 워킹디렉토리에서 merge를 해보고 변경안된 파일만 업데이트하기 때문.

차이 2

  • HEAD 자체를 다른 브랜치로 옮긴다.
  • reset은 HEAD가 가리키는 브랜치를 옮긴다.

차이 3

  • HEAD 와 브랜치가 함께 이동하는가? -> reset
  • HEAD가 가리키는 브랜치가 바뀌는가? -> checkout
  • 아래 그림 참고

예를 들어 각각 다른 커밋을 가리키는 masterdevelop 브랜치가 있고 현재 워킹 디렉토리는 develop 브랜치라고 가정해보자(즉 HEAD는 develop 브랜치를 가리킨다). git reset master 명령을 실행하면 develop 브랜치는 master 브랜치가 가리키는 커밋과 같은 커밋을 가리키게 된다. 반면 git checkout master 명령을 실행하면 develop 브랜치가 가리키는 커밋은 바뀌지 않고 HEAD가 master 브랜치를 가리키도록 업데이트된다. 이제 HEAD는 master 브랜치를 가리키게 된다.

그래서 위 두 경우 모두 HEAD는 결과적으로 A 커밋을 가리키게 되지만 방식은 완전히 다르다. reset 명령은 HEAD가 가리키는 브랜치의 포인터를 옮겼고 checkout 명령은 HEAD 자체를 옮겼다.

checkout


(2) 경로가 있을 때

reset 과 동일하게 HEAD를 움직일 수도 없고 안전하지 않다.

git reset --hard [branch] file 과 동일


**reset 또는 checkout하기 전에 안전한가? 살펴보기


Julia Hwang

디발자를 꿈꾸는 웹개발자의 블로그입니다.