PR을 머지하고 나면 로컬에 쓸모없어진 브랜치가 쌓인다. 몇 달 지나면 git branch 출력이 한 화면을 넘기고 작업 중인 브랜치를 찾기 어려워진다. 정리 명령은 간단하다. 무엇을 지워도 안전한지 구분하는 것이 관건이다.
-d는 안전하고 -D는 위험하다
로컬 브랜치 삭제 명령은 두 가지다. git branch -d는 이미 병합된 브랜치만 지운다. 병합 안 된 커밋이 남아 있으면 거부한다.
$ git branch -d feature/login
Deleted branch feature/login (was 13d794b).
$ git branch -d feature/experimental
error: the branch 'feature/experimental' is not fully merged
hint: If you are sure you want to delete it, run 'git branch -D feature/experimental'
feature/login은 main에 병합됐으니 바로 지워졌고, feature/experimental은 고유 커밋이 남아 있어 거부됐다. 이 거부가 작업을 실수로 날리지 않게 막는다.
git branch -D는 병합 여부를 무시하고 강제로 지운다. 위 에러를 무시하고 진짜 버릴 때 쓴다.
git branch -D feature/experimental
평소엔 -d를 쓴다. Git이 거부하면 아직 끝나지 않은 작업이 있다는 신호다. -D는 실험용 브랜치를 폐기할 때만 의식적으로 쓴다.
지우기 전에 무엇이 안전한지 미리 보려면 다음 두 명령으로 확인한다.
$ git branch --merged # 현재 브랜치에 병합 완료 -> 삭제 안전
feature/login
* main
$ git branch --no-merged # 고유 커밋 남음 -> 삭제 주의
feature/experimental
원격 브랜치 삭제
원격 브랜치는 push --delete로 지운다.
git push origin --delete feature/user-authentication
원격에서 지워도 다른 사람과 내 로컬에는 origin/feature/...라는 추적 참조가 그대로 남는다. 이것이 다음 문제로 이어진다.
‘gone’ 브랜치
원격에서 머지되고 삭제된 브랜치는 로컬에 흔적을 남긴다. git branch -vv로 각 브랜치의 추적 상태를 보면 드러난다.
$ git branch -vv
bugfix/typo 884c27d [origin/bugfix/typo: gone] Fix typo
* main 24e0729 [origin/main]
: gone]은 추적하던 원격 브랜치가 사라졌다는 뜻이다. PR이 머지되고 원격 브랜치가 자동 삭제된 경우가 대부분이다. 로컬에 쌓이는 잔여 브랜치 대부분이 여기에 해당한다.
먼저 fetch -p로 원격에서 사라진 참조를 정리한 뒤, gone으로 표시된 로컬 브랜치만 골라 지운다.
git fetch -p && git branch -vv | grep ': gone]' | awk '{print $1}' | xargs git branch -D
Deleted branch bugfix/typo (was 884c27d).
매번 -p를 붙이기 번거로우면 fetch할 때 자동으로 prune하도록 설정한다.
git config --global fetch.prune true
이 명령에서 -D를 쓰는 것은 의도적이다. gone 브랜치는 원격에서 이미 머지됐지만, 머지 방식(특히 squash 머지)에 따라 로컬에선 병합되지 않은 것으로 보여 -d가 거부할 수 있다.
병합된 로컬 브랜치 일괄 삭제
gone 표시가 붙지 않는 경우(로컬끼리 머지했거나 자동 삭제를 안 켠 경우)는 --merged 목록을 그대로 넘긴다. 현재 브랜치(*)와 보호할 브랜치는 제외한다.
git branch --merged | grep -Ev '^\*|main|master|develop' | xargs -n 1 git branch -d
-d를 쓰므로 병합 안 된 게 섞여 있어도 Git이 건너뛴다. 실행 전에 무엇이 지워질지 보려면 뒤의 xargs만 떼고 돌린다.
git branch --merged | grep -Ev '^\*|main|master|develop'
reflog로 복구
-D로 병합 안 된 브랜치를 날렸어도 reflog에 마지막 커밋이 남아 있다. reflog는 HEAD가 거쳐 간 모든 위치를 90일간 기록한다.
$ git reflog
4fc8d7b HEAD@{0}: checkout: moving from feature/user-auth to main
e723b01 HEAD@{1}: commit: Add user authentication feature
559cee6 HEAD@{2}: commit: Create login form
4fc8d7b HEAD@{3}: checkout: moving from main to feature/user-auth
지워진 브랜치의 마지막 커밋 해시(e723b01)를 찾아 그 지점에서 새 브랜치를 만든다.
git checkout -b feature/user-auth e723b01
$ git log --oneline
e723b01 Add user authentication feature
559cee6 Create login form
4fc8d7b Initial commit
커밋이 돌아왔다. reflog는 로컬에만 있고 90일 뒤 정리되므로, 오래 보관할 작업은 지우기 전에 태그로 박아둔다.
git tag archive/feature-user-auth feature/user-authentication