🐱

Git! 브랜치 관리 전략 및 버전 관리 환경 개선 사례

Created
2021/02/20 07:46
Tags
GIT
Subtitle
Git Flow 를 더 잘 사용하는 방법

1년 동안 앓은 고질병

입사한 이래로 Git을 사용하면서 끊임없이 문제가 발생했었다.
릴리즈 날짜가 있는게 아니라 매일매일 급박하게 배포되어야 하는 회사의 특성상 브랜치들이 일사분란하게 작동되어야 하는데, 별다른 전략없이 대처하다보니 그럴 수 밖에 없었다.
히스토리는 망가져서 사실상 사용할 수 없었고, 심지어 심한 경우는 테스트 환경의 브랜치와 프로덕션 환경의 브랜치의 코드가 다른 경우도 생겼다.
더 이상 테스트 서버를 테스트 서버라 부르지 못하게 되버렸다고 한다.

머지 전략

< 우리의 Develop 브랜치 히스토리 >
< React팀의 Develop 브랜치 히스토리 >
좌측의 Develop 브랜치 히스토리는 다음과 같은 문제점을 가지고 있다.
1.
사이사이에 존재하는 Merge Commit는 어떤 작업을 했는지 알 수 없다.
2.
작업 커밋들이 모두 히스토리로 남아있다. ( 예시에선 "콘솔 로그 제거 등") 이는 어떤 feature를 작업했는지 아는데, 도움도 안될 뿐더러 히스토리를 복잡하게 만든다.
3.
Develop브랜치에서 반영한 기능을 롤백해야 할 때, 복잡한 과정을 거쳐야 한다.
우측의 Develop 브랜치 히스토리는 좌측에 비해 다음과 같은 이점을 가지고 있다.
1.
기능 단위의 Commit 히스토리로 한 눈에 어떤 작업이 진행되는지 알 수 있다.
2.
작업 커밋들은 모두 Description으로 숨겨두었다. 이로 인해 깔끔한 히스토리를 가질 수 있게 되었다.
3.
기능 단위의 Commit이기 때문에 문제가 되는 기능을 쉽게 제거 할 수 있다.

Develop 브랜치와 Create a merge commit VS Squash and Merge

Pull Request에서 Merge 할 때 세가지 머지 방식을 선택할 수 있다.
기본적으로 많이 사용하는 것은 Merge pull request이나, React 팀에서는 master브랜치에 Squash Merge를 사용한다.

Create a merge commit

작업 커밋 모두와 하나의 머지 커밋을 생성 한다.
히스토리를 파괴하는 일이 없기 때문에 가장 안전하다.
하지만 히스토리 로그가 복잡해져서 사실상 히스토리를 참고하기 힘든 방식이다.
develop 혹은 hotfix 브랜치에서 master 브랜치로 반영시에 사용!

Squash and merge

작업 커밋을 하나의 커밋으로 만든다.
기능 단위로 커밋을 정리한 후 반영하기 때문에
히스토리가 깔끔해진다.
머지 이후에도 기능 단위로 작업할 수 있게 된다.
feat 혹은 fix 브랜치에서 develop 브랜치로 반영시에 사용하는 것을 추천!

Rebase and merge

머지 커밋없이 작업한 커밋만을 반영한다.
이 때, 반영 브랜치의 히스토리를 바꾼다.
그 때문에 중간 커밋없이 가장 자연스러운 히스토리를 가질 수 있으나 여러 작업자와 협업시에, 충돌이 발생할 수 있다.
작업 커밋만 남아 있기 때문에 어느 시점에서 기능이 머지 됬는지 확인하기 힘들다.
develop 브랜치에서 feat fix 등 개인 브랜치로 반영시에 사용하는 것을 추천!
우리팀은 이 중 feat혹은 fix에서 develop을 머지시에는 Squash and merge 를 develop에서 master로 반영시에는 Create a merge commit 전략을 이용하도록 하였다.

아직 마스터에 배포하면 안되요...!!

머지 전략은 히스토리를 깔끔하게 만들어주었지만, 여전히 가장 큰 문제가 남았다.
예를 들면 다음 상황에서 문제가 발생했다.
1.
feature를 develop으로 배포 후 테스트 중
2.
오늘까지 반영되어야 할 fix를 develop에 배포 후 테스트 중
3.
1번에서 feature 테스트 중 추가 요구사항으로 작업이 미뤄짐
4.
2번은 1번이 프리징 되므로써 배포할 수 없는 상황에 놓임
Master에 #55번을 반영하려면 #54 번 반영사항을 Develop에서 제거해야 한다.

Rebase Interactive

첫번째로는 Rebase를 이용하여 Develop의 히스토리 자체를 변경할 수 있다. ( 하지만 비추 )
git rebase --interactive b660e50e3c3f84e3345af... ( 작업해야할 커밋 바로 이전 커밋까지 )
Shell
위 명령어를 통해 rebase를 —interactive 모드로 실행 시킨다. 이 때, 작업해야할 커밋 바로 이전 커밋까지 rebase 한다.
지워야 할 커밋 히스토리에 pick을 지우고 drop으로 수정하고 반영하면 해당 커밋이 사라진다.

하지만 문제점은...

rebase는 히스토리를 파괴한다. 깔끔하게 새로운 히스토리를 만든다는 장점이 있다.
하지만 그렇기 때문에 모든 반영(git pull 등)은 -- force 를 통해서 이루어 져야 한다.
이로 인해 히스토리를 잃을 뿐만 아니라 위험부담도 따른다.

Revert

우리팀이 선택한건 Revert를 사용하는 방식이다.
Revert는 반영하기 전 코드 상태로 새로운 커밋 히스토리를 만든다.
git revert bddf709d218a8e08cb6ab0dccb8cdf5198e78de6 ( 되돌릴 기능의 커밋 )
Shell
해당 명령어는 다음과 같은 히스토리를 생성한다.
마스터에 해당 히스토리로 반영후에
Develop브랜치에 해당 기능을 다시 develop브랜치로 옮긴다.
이러한 전략을 통해서 상황에 맞춰 유연하게 기능들을 배포할수 있게 되 더 이상 특정 기능의 배포가 다른 배포 일정에 영향을 미치지 않게 되었다!

번외편 - 이쁜 버전 히스토리 만들기!

기왕 하는 김에 Commit명 같은 것도 보기 좋게 쓰면 좋겠다는 이야기가 나와서 gitmoji를 도입하게 됬다.
gitmoji는 commit시에 메시지 앞에 아이콘을 붙혀서, 좀 더 쉽게 커밋 히스토리를 확인 할 수 있게 해준다.
gitmoji에서 의미있는 아이콘들을 정해줘서, 아예 표준화 시켜서 github 라벨에도 똑같이 적용했다.
전자는 커밋 히스토리 후자는 PR에 붙은 라벨 // 통일해서 쓰니 어떤 라벨을 쓸지 고민하지 않아도 된다.
이렇게 라벨을 붙이고 나니, GITHUB API를 이용하여 나중에 회고할 때 유용하게 쓰일수 있을거 같았다.
누가 어떤 작업을 많이 했는지 또 리뷰는 얼마만큼 했는지, 핫픽스는 얼마나 자주 발생하는지 등 말이다.

마지막으로

이번에 별거 아니지만 오랜 시간 골치덩이였던 문제를 해결해야 했는데, 생각보다 쉽게 문제가 해결됬다.
그 중 가장 큰 요인은 팀원들이 모두 공감하고 있어서, 적극적으로 시험해보고 반영해보려고 했던 것이 빨리 이 문제를 해결 한 것 같다.