tech.junha.kr

노트/Git

Reset Demystified

작성

  1. git reset <옵션> <돌아가고싶은커밋>
  2. git checkout <브랜치> <옵션>
  • reset을 통해 HEAD, Index, Working Directory을 관리한다.

세 개의 트리

TreeRole
HEAD
(Repository)
마지막 커밋 스냅샷, 다음 커밋의 부모 커밋
Index
Stage
Staging Area
다음에 커밋할 스냅샵
Working Directory
Local
Sandbox

1. HEAD

  • 현재 브랜치의 마지막 커밋을 가리키는 포인터
  • 현재 커밋은 다음에 올 커밋의 부모 커밋이 된다.

HEAD가 가리키는 스냅샷 보기

$ git cat-file -p HEAD
tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
author Scott Chacon  1301511835 -0700
committer Scott Chacon  1301511835 -0700

initial commit

$ git ls-tree -r HEAD
100644 blob a906cb2a4a904a152...   README
100644 blob 8f94139338f9404f2...   Rakefile
040000 tree 99f1a6d12cb4b6f19...   lib

2. The Index

  • Proposed next commit.
  • git commit을 할 때, Git이 바라보는 공간이며 Index는 새로운 커밋으로 변환된다.

3. The Working Directory

  • Staging Area에 커밋하기전에 자유롭게 수정하는 공간
  • HEAD와 Index는 효율적이지만 불편한 방식으로 .git 안에 저장한다.
  • The working directory unpacks them into actual files, which makes it much easier for you to edit them.

The Workflow

Git’s typical workflow is to record snapshots of your project in successively better states, by manipulating these three trees.

reset-workflow

The Role of Reset (1)

주어진 상황

reset-start

Step 1: Move HEAD (--soft)

  • HEAD 브랜치를 이동시킴
  • checkout 처럼 HEAD가 가리키는 브랜치를 이동시키지는 않음
  • HEAD는 계속 현재 브랜치를 가리키고 있고, 현재 브랜치가 가리키는 커밋을 바꿈
  • git reset 9e5e6a4 will start by making master point to 9e5e6a4.

reset-soft

  • git commit의 역할
    • 새로운 커밋 생성
    • HEAD가 가리키는 브랜치가 새로운 커밋을 가리키도록 업데이트
  • git reset
    • 가장 최근의 git commit을 되돌림
  • git reset HEAD~
    • HEAD~: HEAD의 부모 커밋, 직전의 커밋으로 되돌림
  • git reset --soft HEAD~
    • Index, Working Directory는 그대로 놔두고, 브랜치가 가리키는 커밋만 이전으로 되돌림

Step 2: Updating the Index (--mixed)

2

  • git reset [--mixed] HEAD~
    • git reset HEAD~ 디폴트 값
    • Index를 현재 HEAD가 가리키는 스냅샷으로 업데이트
    • git commit과 git add 명령을 되돌리는 것과 같은 효과다.

Step 3: Updating the Working Directory (--hard)

reset-hard

  • git reset --hard HEAD~
    • git add, git commit, 워킹 디렉토리의 내용까지 되돌림
    • Git에는 데이터를 삭제하는 방법이 별로 없지만 그 중 하나의 명령어이니 유의한다.
    • 이 예제에서는 v3을 커밋했기 때문에, reflog를 이용해 복원할 수 있지만, 한 번도 커밋하지 않았다면 Git이 덮어쓴 데이터를 복원할 수 없다.

정리

  • --soft: HEAD가 가리키는 브랜치를 옮긴다.
  • [--mixed]: Index를 HEAD가 가리키는 상태로 만든다.
  • --hard : 워킹 디렉터리를 Index의 상태로 만든다.

The Role of Reset (2)

  • 경로를 주고 reset하기
  • 경로를 주면 HEAD의 브랜치를 옮기는 단계는 생략되어, 현재 HEAD가 가리키는 커밋에서 적용된다.
    • HEAD는 포인터이기 때문에 경로에 따라 파일별로 기준이 되는 커밋을 부분적으로 적용하는 것이 불가능하기 때문이다.
  • Index, 워킹 디렉토리의 일부분 갱신할 수 있다.

1

  • git reset file.txt
    • file.txt을 HEAD에서 Index로 복사
    • file.txt를 Unstaged 상태로 만듬

2

  • git add와 정확히 반대의 역할

3

  • 특정 커밋을 명시하면 Git은 HEAD에서 파일을 가져오는 것이 아니다.
  • 그 커밋에서 파일을 가져온다.

합치기 (Squash)

  • reset + commit 으로 커밋들을 하나로 합칠 수 있다.

주어진 상황

  • 두번째 커밋의 b파일은 아직 작업중이기 때문에 세번째 커밋과 합치고 싶다.

reset-squash-r1

git reset --soft HEAD~2

reset-squash-r2

  • HEAD 포인터를 이전 커밋으로 되돌리기

git commit

reset-squash-r3

  • a파일이 있는 v1커밋이 하나 그대로 있고, 두번째 커밋에는 v3버전의 a파일과 추가된 b파일이 있다. v2에는 더는 히스토리가 없다.

checkout

  • checkout도 reset과 마찬가지로 세 트리를 조작한다.

  • 명령어에 파일 경로의 작성 여부와 동작이 다르다.

  • git reset --hard와는 달리 워킹 디렉토리를 안전하게 다룬다.

    • 저장하지 않는 것이 있으면 확인해서 날려버리지 않는다.
    • git reset --hard는 head를 업데이트 하지만, checkout은 HEAD 자체를 다른 브랜치로 옮긴다.
  • reset은 HEAD가 가리키는 브랜치의 포인터를 옮기고, checkout은 HEAD자체를 옮긴다.

reset-checkout

요약

캡처