Git
전략에 따라서 작업하는 방식은 여러가지가 존재하지만, 대부분은 local
에서 특정 브랜치를 생성하고 작업이 완료되면 최종적으로 master
나 develop
같은 특정 브랜치로 merge하게 된다.
이런 경우 조금만 작업이 많아지면 local
에 브랜치가 무한정 쌓이게 되는데, 아래처럼 git-prune.sh
라는 스크립트로 만들어서 사용하면 특정 로컬 브랜치 기준으로 merge된 브랜치를 깔끔하게 삭제할 수 있다.
#!/bin/bash
cbranch=$(git rev-parse --abbrev-ref HEAD); git checkout -q $cbranch && git for-each-ref refs/heads/ "--format=%(refname:short)" | while read branch; do mergeBase=$(git merge-base $cbranch $branch) && [[ $(git cherry $cbranch $(git commit-tree $(git rev-parse $branch"^{tree}") -p $mergeBase -m _)) == "-"* ]] && git branch -D $branch; done
스크립트를 이해하기 위해서는 아래처럼 3단계로 접근하면 된다.
(1) 먼저 cbranch=$(git rev-parse --abbrev-ref HEAD); git checkout -q $cbranch
는 현재 HEAD
의 브랜치 이름을 cbranch
에 저장(앞으로 기준이 될 브랜치)하고 해당 브랜치로 checkout
한다. 그리고 git for-each-ref refs/heads/ "--format=%(refname:short)"
명령어를 통해 현재 로컬의 브랜치 이름을 모두 가져온다.
(2) (1) 마지막에서 가져온 로컬 브랜치들을 순회한다. 각각 브랜치의 mergeBase(commit 해쉬값)를 설정(mergeBase=$(git merge-base $cbranch $branch)
)하면서 동시에 [[ $(git cherry $cbranch $(git commit-tree $(git rev-parse $branch"^{tree}") -p $mergeBase -m _)) == "-"* ]]
을 만족하는 브랜치는 git branch -D $branch
명령어를 통해 삭제한다.
(3) (2)의 명령어를 조금 더 깊게 살펴보면 git rev-parse $branch"^{tree}"
은 브랜치 tree 자체의 commit 해시값을 의미하고, $(git commit-tree $(앞에서 구한 트리 commit 값) -p $mergeBase -m \_)
을 통해서 $mergeBase를 부모로하는 commit object를 생성한다. 그리고 마지막 [[ $(git cherry $cbranch $(앞에서 생성된 commit 해시값) == "-"\* ]]
은 앞에서 생성한 commit object를 기준으로 현재 브랜치(기준 브랜치)에 이미 merge가 완료된(git cherry에서 (-)은 이미 merge가 완료된 것을 의미) 브랜치를 확인한다.
솔직히 스크립트만 보면 숨이 턱 막히는 느낌일 수 있다. 하지만 시간을 좀 투자해서 차근차근 명령어 하나씩 살펴보면 이해 할 수 있다. 물론 굳이 이해하지 않고 유용하게만 사용해도 그게 어디인가. 아래는 스크립트에서 나온 명령어들의 공식 문서 링크이다. 관심있다면 참고하면 좋을 것 같다.
git-scm.com/docs/git-merge-base
git-scm.com/docs/git-rev-parse