git checkout

2023. 6. 28. 17:01Git

git checkout 명령어는 단순한 브랜치 전환 이상의 복합적인 Git 내부 작업을 수행하며, Git의 워킹 디렉토리, 인덱스(Index), HEAD, 그리고 리퍼런스(refs) 사이의 동작 원리를 이해해야만 그 진면목을 파악할 수 있습니다.

 

✅ 개요: git checkout은 무엇인가?

git checkout은 Git에서 HEAD 포인터를 이동하거나, 특정 커밋 또는 브랜치의 스냅샷을 워킹 디렉토리에 복원하는 데 사용됩니다. 이것은 다음과 같은 두 가지 유형의 동작으로 구분됩니다:

  1. 브랜치 기반 checkout: HEAD 포인터를 다른 브랜치로 이동하고, 그 브랜치가 가리키는 커밋을 워킹 디렉토리와 인덱스에 반영합니다.
  2. 파일/트리 기반 checkout: 특정 커밋이나 브랜치로부터 특정 파일이나 디렉토리를 선택적으로 복원합니다. HEAD는 바뀌지 않습니다.

 

🧠 Git 내부의 세 가지 핵심 개체

Git은 다음의 세 구조 사이를 오가며 작업합니다:

HEAD ---------> 커밋 (브랜치 또는 해시)
  |
  |         git checkout은 이 세 가지에 영향을 미친다
  v
Index (스테이징 영역)
  |
  v
Working Directory (작업 디렉토리)

 

🧱 내부 동작 구조 해부

1️⃣ 브랜치 전환: git checkout <branch>

🔹 수행 작업

  • HEAD<branch>로 변경
  • 인덱스(index) ← <branch>가 가리키는 커밋의 트리로 초기화
  • 워킹 디렉토리 ← 인덱스와 동일하게 복원

🔍 내부적으로는 다음 순서로 진행:

[1] HEAD 이동           : HEAD → refs/heads/<branch>
[2] Index 초기화        : Index ← Tree(<branch>)
[3] Working Directory  : 파일 변경/삭제/추가 → Tree와 동기화

💥 실패 조건

  • 현재 작업 디렉토리 또는 인덱스에 uncommitted 변경 사항이 존재하고, 그것이 새 브랜치의 상태와 충돌할 경우 checkout 실패

2️⃣ 새 브랜치 생성 후 전환: git checkout -b <new-branch> [<start-point>]

🔹 내부 동작

  • refs/heads/<new-branch> 생성 → <start-point> 커밋을 가리킴
  • HEAD → <new-branch>
  • index, working directory ← <start-point> 트리

start-point는 생략 시 HEAD가 가리키는 커밋을 기본값으로 사용

 

3️⃣ 특정 커밋/브랜치 기준으로 파일 복원: git checkout <commit-ish> -- <path>

🔹 수행 작업

  • <commit-ish>는 커밋 해시, 브랜치명, 태그명, HEAD, HEAD~n 등 모두 가능
  • 해당 커밋의 트리에서 지정된 <path> 파일만 가져와서:
    • 인덱스(index) 업데이트
    • 워킹 디렉토리 덮어쓰기

🔍 내부적 흐름

[1] 지정 커밋의 트리에서 <path> 파일 읽기
[2] Index ← 덮어쓰기
[3] Working Directory ← 덮어쓰기

HEAD 포인터는 이동하지 않음
전체 트리를 덮어쓰는 것이 아니라 선택적 파일 복원

 

4️⃣ 특정 커밋으로 HEAD를 직접 이동: git checkout <commit-hash>

🔹 결과: Detached HEAD 상태

  • HEAD가 브랜치를 참조하지 않고, 커밋 해시를 직접 가리킴
  • 이후 커밋은 브랜치가 아닌 "익명 체인"으로 생성됨
$ git checkout 9fceb02
# HEAD now points to 9fceb02 (detached HEAD)

 

🔎 내부 동작

HEAD → 9fceb02 (커밋 해시)
Index ← Tree(9fceb02)
Working Directory ← Tree(9fceb02)

❗ 이 상태에서 커밋한 내용은 브랜치에 연결되지 않으며, 브랜치 생성 없이 checkout하거나 reset하면 유실 위험

 

5️⃣ 강제 checkout: git checkout -f <branch>

  • -f 또는 --force 옵션은 변경 사항이 있어도 강제로 워킹 디렉토리와 인덱스를 덮어씀
  • 매우 위험하며, 실제 파일이 사라질 수 있음

 

📁 실전 예제 정리

명령어 의미
git checkout main main 브랜치로 HEAD 이동
git checkout -b dev 새 브랜치 dev 생성 및 전환
git checkout HEAD~1 -- src/App.java 한 커밋 전의 App.java 복원
git checkout -- README.md 마지막 커밋 기준으로 README.md 복원
git checkout 9fceb02 해당 커밋으로 Detached HEAD 전환
git checkout -f feature/test 강제로 feature/test 브랜치로 전환

 

🧬 git checkout vs git switch / git restore

Git 2.23 이상에서는 명령의 목적을 분리하기 위해 다음과 같은 명령어로 대체 가능:

목적 예전 현재 권장
브랜치 전환 git checkout main git switch main
브랜치 생성 git checkout -b feat git switch -c feat
파일 복원 git checkout HEAD -- file git restore file

 

git checkout은 백워드 호환을 위해 여전히 존재하지만 기능이 지나치게 많고 오류 발생 여지가 높기 때문에 분리 권장

 

⚙️ 내부 설정 파일과 연관

HEAD 파일

  • .git/HEAD는 현재 브랜치 또는 커밋 해시를 가리킴
ref: refs/heads/main

 

또는 Detached 상태에서는:

9fceb02c3a0db56a786f4e8ebd4eacf1c69c3251

 

🧷 실수 방지를 위한 전문가 팁

상황 대응 전략
checkout 시 변경사항 충돌 git stash, git commit, git restore로 해결
Detached HEAD 상태로 실수 git switch -c recovery-branch로 브랜치 생성하여 복구
브랜치 전환 안전하게 수행 git switch 권장
특정 파일만 복원 시 git restore 사용

 

📌 결론

  • git checkoutHEAD, index, working directory 세 곳을 동시에 조작하는 복합적 명령
  • 브랜치 전환, 커밋 전환, 파일 복원이라는 세 가지 역할을 수행
  • 사용이 편리하지만, 위험성과 복잡성이 있어서 최신 Git에서는 switch, restore로 분리됨
  • Git 내부 구조와 연동된 HEAD, refs, 트리 객체, 인덱스를 완전히 이해해야 의도하지 않은 데이터 유실을 방지할 수 있음

'Git' 카테고리의 다른 글

recursive merge strategy  (0) 2023.06.28
ort merge strategy  (0) 2023.06.28
git rebase 시, --continue 옵션 사용  (0) 2023.06.28
merge class : index.html  (0) 2023.06.26
Detached HEAD  (0) 2023.06.26