깃(Git) 커밋마다 겪는 일
팀을 이루어 소프트웨어 개발을 진행하면서 깃(Git)
으로 버전 관리를 하는 것은 이젠 너무 당연한 일이 되었습니다. 푸시하고 풀 받고 머지하고, 컨플릭트 수정해서 다시 커밋 푸시하고를 하다 보니 개발은 생각보다 많이 진전이 되어 있죠. 그런데 뭔가 찜찜한 것은 깃을 사용하는 방법이 각자 조금씩 다르다는 것입니다.
특히 수정을 하고 커밋을 할때는 조금씩 멈칫하게 됩니다. 지금 커밋을 할까? 아니면 조금 더 수정을 하고 커밋을 할까? 어떤 때는 커밋을 하려고 디프(diff)
를 해 보니 몇가지 다른 이슈를 수정을 한게 섞여 있네요. 커밋 메시지를 적어야하는데 여러개를 다 적으려니 번거롭기도 하고 짦은 문장 하나가 잘 떠오르지도 않습니다.
이때쯤 우리가 기켜야할 규직 하나 정도는 마련하는게 좋겠습니다. 이번 깃 커밋 가이드는 그룹 채팅 오픈 소스 프로젝트인 줄립(zulip)의 Version Constol의 내용을 기반으로 번역과 의역과 첨삭을 자유롭게 한 내용입니다. 조금 더 다듬어서 동료들에게 공유하고 같이 실천하고 지켜나가면 좋습니다.
커밋시 지켜야할 사항
“커밋은 논리적으로 구분이 되고, 일관성이 유지되는 단위로 최대한 작게 쪼개서 되어야합니다.(Each commit is a minimal coherent idea)”. 이 말은 Git 프로젝트내에서 사용하고 있는 커밋 준수사항입니다. 실제 이렇게 운영하려면 일거리가 늘어나는게 사실입니다. 그래도 이렇게 이루어진 커밋은 코드 리뷰를 위해서 볼 때 버그나 사이드 이펙트를 발견하는 도움이 됩니다. 커밋 히스토리가 의미 있는 유용한 자원이 되도록 해 주기도 합니다. 현재 코드가 동작하는 방식이 어떤 과정을 거쳐서 변하게 되어 있는지 커밋 히스토리를 보면서 이해할 수도 있으니까요. 어떤 경우는 히스토리 내용을 보면서 버그의 원인을 찾아낼 수도 있게 되죠.
커밋은 꼭 논리적으로 구분되며 일관성 있어야합니다.
- 커밋은 반드시 테스트를 통과한 후 해야합니다. 이 말은 테스트 중에 발견된 문제가 다 수정된 후에 커밋이 되어야한다는 것이죠. 우선 커밋을 하고 “이전 커밋 후 테스트에서 발견된 문제 수정”이라는 별도의 커밋이 있어서는 안된다는 것입니다.
- 각 커밋은 실 서버에 반영이 되어도 안전한 것이어야합니다. 만일 피치 못 할 사정이 있어 안전하지 않은 커밋을 하게되는 경우에는 꼭 그 이유를 자세히 설명해 두어야합니다. 예를들어 API 엔드 포인트를 하나 개발해서 커밋을 하고 나서 이 API와 관련된 인증 검사는 나중에 커밋에 하는 일은 피해야한다는 거죠. 인증 검사는 처음 API를 커밋할 때 함께 들어 있어야합니다.
- 에러를 트리거하는 코드는 항시 에러 핸들링을 포함한 코드가 같이 커밋되어야합니다.
- TODO 코멘트는 이를 유발하게 수정사항 혹은 추가 작업이 필요한 기능의 커밋과 함께 들어가 있어야합니다.
커밋은 보통 최소 단위별로 되어야합니다.
- 규모가 큰 리펙토링은 기능 수정과는 별도로 이루어져야합니다. 기능 수정과 리펙토링을 동시에 진행하는 것은 피해야합니다.
- 코드를 원래 있던 파일에서 다른 파일로 옮기는 것은 별도의 커밋으로 진행되어야합니다. 기능을 수정하는 것과 동시에 하거나 해당 파일의 리펙토링과 같이 하는 것도 피해야합니다.
- 2개의 다른 리펙토링을 진행했다면 각 리펙토링을 각각 커밋해야합니다.
- 2개의 다른 기능은 가각의 나누어 커밋을 해야합니다.
- 커밋을 하려고 메시지를 작성하고 있는데 몇개의 다른 분류의 내용을 나열하고 있다면 아마도 그 나열된 각각의 내용으로 구분해서 커밋을 해야하는 신호일 겁니다.
너무 작은 단위로 커밋을 하지 않아도 되는 경우도 있습니다.
- 완전히 새로운 기능을 개발하는 중이라면 꼭 서브 기능으로 나누어서 개별적으로 커밋을 할 필요는 없습니다. 만일 새로운 툴을 처음부터 코딩하고 있다면 초기 버전이 나올때 한번에 커밋을 하는 것도 가능합니다. 그렇더라도 한번에 커밋된 2000 라인이 넘는다면 코드를 리뷰하는게 괭장히 어려운 일이 됩니다. 가능하면 커밋 히스토리를 보면서 코드를 리뷰하는 경우를 고려해서 적당한 단위로 커밋을 하는게 좋습니다.
- 백엔드 코드와 프론트엔드 코드를 궂이 구분해서 커밋할 필요는 없습니다. 때로는 백엔드 수정 내용만으로도 논리적인 최소 단위가 되는 경우가 있지만 이 경우도 일부러 구분해서 커밋을 하지는 않아도 됩니다.
다른 고려 사항은…
- 너무 과도하게 세분화된 커밋은 나중에
스쿼시(squash)
를 해서 합칠 수 있습니다. 하지만 그 반대는 불가능 하죠. 그러니까 가능하면 작은 단위로 커밋을 하고 나중에 하나로 합치는게 좋겠다고 생각이 들면 그때 합치는 게 좋습니다. - 테스트를 하다가 문제가 발견되어 수정을 하면 수정 사항은 이전 커밋을
어멘드(amend)
해야합니다. 버그 수정을 별도의 커밋으로 “테스트 이슈 수정”의 코멘트로 생성을 하는 것은 좋지 않습니다.
어떤 경우에도 여러개의 수정사항을 하나의 커밋으로 묶어버리는 일은 해서는 안 됩니다. 깔끔하게 정리된 커밋 히스토리를 유지하는 데는 어느정도 훈련 과정이 필요하게 됩니다. 그 훈련이라는 것으 좀 귀찮더라도 돌아가고 정해진 순서를 따른 것을 의미합니다. 예를들어 기능을 개발하는 과정중에 리펙토링을 필요로 하는 부분을 만나게 된다면 우선은 현재 수정 사항을 스태시(stash)하고 리펙토링을 합니다. 그리고 리펙토링이 완료되면 커밋을 하고 언스태시(unstash)를 하고 나서 기능 개발을 계속 하면 됩니다.
커밋 메시지
우선 좋은 커밋 메시지 예제를 보여드리겠습니다.(good commit message example)
provision: Fix node_modules being owned by root.
A bug in the node_cache.py code resulted in the node_modules symlink
in Zulip development environments being incorrectly owned by root.
This causes that bug to be fixed the next time a user provisions.
커멧 메시지의 첫 라인은 요약된 내용을 담고 있습니다.
- 영어로는 명령문 형식으로 작성을 합니다. 명령문 형식이라서 시제는 현재입니다. (예를 들어 “Fix…”, “Add …” 등)
- 한글로는 “… 수정”, “… 추가” 등의 형식을 사용합니다.
- 어떤 것을 커밋 했는지만 간단하게 작성합니다.
- 코드의 어떤 부분이 변경되었는지 나와야합니다. 서브 시스템이나 모듈 이름에 콜론을 붙어셔 맨 앞에 두면 좋습니다. (“payment: …”, “product: …”)
- 영어인 경우 마침표로 끝나는 완전한 문장이어야합니다.
좋은 요약 메시지 예입니다.
zjsunit: Fix running stream_data and node tests individually.
zjsunit: stream_data 와 node 테스트를 가각 수정.
gather_subscriptions: Fix exception handling bad input.
gather_subscriptions: 잘 못 된 입력 오류 익셉션 수정.
Add GitLab integration.
깃랩 인티그레이션 추가.
나쁜 요약 메시지를 “gather_subscriptions: Fix exception handling bad input.”와 비교해 보겠습니다.
- “gather_subscriptions was broken” : 왜 문제가 발생했는지가 없습니다. 그리고 명령문도 아니고요.
- “Fix exception when given bad input”: 코드의 어떤 부분이 수정되어 있는지를 알 수 없습니다.
- “gather_subscriptions: Fixing exception when given bad input.”: 명령문이 아니네요.
- “gather_subscriptions: Fixed exception when given bad input.”: 역시 명령문이 아닙니다.
요약 내용 다음에는 한줄을 띠고 커밋 메시지의 본문을 적습니다.
- 평문으로 단락을 구분해서 적습니다.
- 본문에는 다음 내용을 포함합니다.
- 수정을 한 이유와 방법
- 자동화된 시험 외에 수행한 매뉴얼 시험
- 뭔가 문제가 있을 것 같은 부분이나 계속 관심을 기울여야할 것 같은 파트
- 성능 향상과 관련된 내용이라면 어느정도 성능이 향상되었는지를 가늠할 수 있는 간략한 테스트 결과를 적습니다.
- 단략별로 라인은 76자 보다 적도록 유지합니다.
git log
를 일반 터미널에서 봤을 때 보기 편하도록하기 위함입니다.
앞서 말씀드린대로 git 커밋 히스토리를 깨끗하게 유지라혀면 어느정도 훈련과 연습 과정을 거쳐야합니다. 생각해 보면 프로그램을 배우는 과정도 시간을 들여 고통을 감내해야하는 훈력과 연습이었습니다. git도 동일한 과정을 겪다보면 어느 순간에는 익숙하게 사용하고 있는 자신을 발견하게 될 겁니다.