git checkout 대신 git switch와 git restore를 써야 하는 이유
브랜치를 옮길 때 습관처럼 git checkout을 쓰다가 git switch를 보고 "뭐가 다르지?" 싶었던 적이 있을 것이다. 브랜치를 바꾸는 동작 자체는 같다. 다만 git switch는 git checkout이 하던 여러 일 중 브랜치 전환만 떼어 낸 명령이고, 그렇게 나눈 데에는 이유가 있다.
checkout은 두 가지 일을 한다
- 브랜치 전환: 작업 트리와 HEAD를 다른 브랜치로 옮긴다.
- 파일 되돌리기: 작업 트리의 파일을 인덱스나 특정 커밋의 내용으로 덮어쓴다.
git checkout develop # 1. develop으로 전환
git checkout -- src/a.ts # 2. src/a.ts의 수정 사항을 버림
첫 번째는 언제든 되돌릴 수 있다. 두 번째는 커밋하지 않은 변경을 경고 없이 지우므로 되돌릴 방법이 없다. 위험도가 전혀 다른 두 작업이 같은 이름을 쓰는 것이 문제의 출발점이다.
어떤 실수가 생기나
git checkout <이름>은 그 이름이 브랜치면 브랜치를 바꾸고, 브랜치가 아니면서 파일 경로와 일치하면 그 경로의 변경을 버린다.
# 'src'라는 브랜치로 가려 했지만 그런 브랜치는 없다
git checkout src
# Updated 1 path from the index
# → src 디렉터리 아래 커밋하지 않은 변경이 전부 사라짐
브랜치 이름을 잘못 썼을 뿐인데 Git은 파일 되돌리기로 해석한다. 출력은 "경로를 갱신했다"는 한 줄뿐이고 확인 절차도 없다.
Git 2.23에서 나뉜 명령
Git 2.23(2019년 8월)은 checkout의 역할을 두 명령으로 나눴다.
| 하는 일 | 기존 명령 | 새 명령 |
|---|---|---|
| 브랜치 전환 | git checkout develop | git switch develop |
| 브랜치 생성 후 전환 | git checkout -b feature/x | git switch -c feature/x |
| 직전 브랜치로 | git checkout - | git switch - |
| 작업 트리 파일 되돌리기 | git checkout -- src/a.ts | git restore src/a.ts |
| 스테이징 취소 | git reset HEAD src/a.ts | git restore --staged src/a.ts |
| 특정 커밋의 파일 가져오기 | git checkout HEAD~1 -- src/a.ts | git restore --source=HEAD~1 src/a.ts |
git switch는 브랜치만 다룬다. 인자가 브랜치가 아니면 파일로 해석하지 않고 에러를 낸다.git restore는 파일만 다룬다.
git switch src
# fatal: invalid reference: src
명령 이름이 의도를 그대로 드러내므로 리뷰나 문서에서 읽는 사람도 무슨 일이 일어날지 바로 안다.
또 하나의 차이: detached HEAD
커밋이나 태그로 이동하면 HEAD가 어떤 브랜치에도 붙지 않은 detached HEAD 상태가 된다. 이 상태에서 만든 커밋은 브랜치를 따로 만들지 않으면 나중에 찾기 어렵다.
git checkout v1.0.0 # 경고만 출력하고 바로 detached HEAD로 이동
git switch v1.0.0 # fatal: a branch is expected, got tag 'v1.0.0'
git switch --detach v1.0.0 # 의도를 명시해야 이동
git switch는 --detach로 의도를 밝혀야만 이동하므로, 실수로 detached HEAD에서 작업하는 일을 막아 준다.
그럼 checkout은 쓰면 안 되나
그렇지는 않다. git checkout은 하위 호환을 위해 남아 있고 계속 동작한다. 오래된 문서, 스크립트, CI 설정에는 여전히 많이 쓰이므로 읽을 줄은 알아야 한다. 다만 새로 명령을 입력하거나 문서를 쓸 때는 다음을 권장한다.
- 브랜치를 옮길 때는
git switch - 파일을 되돌릴 때는
git restore
정리
git switch와git checkout의 브랜치 전환 동작은 같다.git checkout은 브랜치 전환과 파일 되돌리기를 한 명령으로 처리해서, 브랜치 이름을 잘못 쓰면 파일 변경이 경고 없이 사라질 수 있다.- Git 2.23부터 브랜치 전환은
git switch, 파일 되돌리기는git restore로 나뉘었다. git switch는 브랜치가 아닌 대상으로 이동하지 않고, detached HEAD로 가려면--detach를 명시해야 한다.