팀에서 개발을 git flow 전략으로 개발을 하다보면 많은 feature 브랜치가 생기게되고 커밋 수가 많아지면 과거의 커밋 히스토리를 추적하기 어려워집니다. 따라서 저희 팀은 git squash + Rebase로 commit 히스토리를 깔끔하게 정리하자는 목표를 가지게 되었습니다.
Squash + rebase를 하기에 앞서 Merge의 종류인 Fast Forward Merge와 3-way-merge에 대해 알아보겠습니다.
💡 Fast Forward Merge란?
브랜치 간 병합 시 현재 브랜치의 HeadCommit 과 병합하려는 브랜치의 Base Commit이 일치할 경우, 현재 브랜치의 Head Commit이 병합하려는 브랜치의 Head Commit으로 이동되는 병합 방식이다.
Head Commit은 마지막 커밋, Base Commit은 브랜치를 생성할 때 Base가 됐던 브랜치의 마지막 커밋이다. 즉, 현재 브랜치의 HeadCommit 과 병합하려는 브랜치의 Base Commit이 일치한다는 것은, 현재 브랜치의 마지막 지점과 병합하려는 브랜치의 생성 지점이 동일하다는 것이다.
예를들어 아래와 같이 Main 브랜치로부터 생성된 A, B 브랜치가 있다. Main 브랜치의 Head Commit은 M-C2, A 브랜치의 Head Commit은 A-C2, B 브랜치의 Head Commit은 B-C2 이고, A 브랜치와 B 브랜치 모두 Base Commit이 M-C2 이다.
Main 브랜치에서 A 브랜치를 병합할 경우 Main 브랜치의 Head Commit인 M-C2와 A 브랜치의 Base Commit인 M-C2가 같으므로 이 지점을 연결한 후 Main 브랜치의 Head Commit A-C2 브랜치로 이동시키는 Fast Forward 방식으로 병합된다.
💡 이때에는 3-way-merge 방식으로 병합하게 된다. 병합한 내용은 Merge Commit 에 생성되고 아래와 같은 형태의 커밋 히스토리가 생성된다.

먼저 다음과 같은 브랜치가 있다고 생각해보겠습니다.

현재 Head Commit은 main이고 main에서 dev 브랜치를 생성하고 dev에서 feature/test1 브랜치를 생성한 모습입니다.
여기서 feature/test1 브랜치의 commit을 squash를 통해 하나로 합치고 dev에 rebase를 통해 병합해보겠습니다.





다음은 스쿼시한 feature/test1 브랜치의 커밋을 dev 브랜치에 rebase + Merge를 해보겠습니다.





💡 git merge --no-ff와 같은 역할을 합니다.


💡 만약 로컬 저장소의 커밋을 정리하는 것이 아닌 원격 저장소의 커밋을 정리하는 것이라면 Squash 또는 Rebase를 했을 때 pull 할 수 있는 이력들이 있을 것 입니다.
이는 원격에 있는 commit과 squash or rebase를 했을 때 커밋과 달라졌기 때문인데요.
이 때 push를 하기 위해 pull을 먼저 하게된다면 pull 했을 때의 커밋이 새로 생기게 됩니다. (fetch + merge로 인해)
만약 저희 팀 처럼 커밋 히스토리를 1개로만 하고 싶으시다면 pull & push가 아닌 강제 푸쉬를 해줘야 합니다.
강제 푸쉬 명령어
git push origin <branch명> --force