git pull 명령어는 git fetch 명령어와 git merge 명령어를 순서대로 실행하는 명령어다. git pull 명령어는 로컬 브랜치와 원격 브랜치의 이력 차이를 정확히 파악하고 있는 상황이 아니라면 함부로 실행해서는 안 된다. 가능하면 git fetch 명령어로 원격 브랜치의 이력 상태를 파악한 후 git merge 명령어로 병합하는 것이 좋다.
새로운 브랜치 my-branch 가 main 브랜치로부터 분기된 이후 main 브랜치에 새로운 커밋이 올라오지 않았다면, my-branch 가 main 와 비교하여 최신의 브랜치라고 할 수 있다. 이런 경우 my-branch 의 변경 이력을 그대로 main 으로 가져올 수 있는데, 이를 Fast-Forward Merge 라고 한다.
병합 명령에서 --squash 옵션이 없으면 Git이 병합 전략을 판단하여 fast-forward 병합이 가능하면 ff병합을 수행한다. 또는 명시적으로 --ff-only 옵션을 줄 수 있다. (단 이때, ff병합이 불가능하면 abort 가 된다.)
Squash는 여러개의 커밋을 하나의 커밋으로 합치는 것을 의미한다. Squash Merge는 병합할 브랜치의 모든 커밋을 하나의 커밋으로 Squash한 새로운 커밋을 Base 브랜치에 추가하는 방식으로 병합하는 것을 의미한다.
$ git checkout main
$ git merge --squash my-branch
$ git commit -m "squash & merge"
이때, git merge 명령어에 --squash 옵션을 사용하는 squash 병합은 병합 결과를 커밋하는 것이 아니라 변경 사항을 파일에 반영해 Staging Area에 등록된 상태로 둔다.
따라서, 명시적으로 커밋을 실행해 병합을 완료한다.
my-branch 가 main 브랜치에서 분기되고, main 브랜치에 새로운 커밋이 생겼다면, my-branch 를 최신이라고 간주할 수 없다. 따라서 my-branch 와 main 을 공통 부모로 한 새로운 Merge Commit 을 생성하게된다. 이런 방법을 Recursive Merge라고 한다.
또한, Fast-Forward Merge가 가능한 상태에서 git merge 명령에 --no-ff 옵션을 주면 강제로 Merge Commit을 생성하게 할 수 있다.
recursive 전략은 3방향 병합(3-way merge) 알고리즘을 기반으로 2개 브랜치의 마지막 커밋을 병합한다.
3방향 병합(3-way merge) 알고리즘은 병합할 두 파일과 두 파일의 공통 원본 파일을 비교해 병합하는 방법이다. 3방향 병합 알고리즘에 관한 더 자세한 내용은 Wikipedia의 "Merge (version control)"를 참고한다.
fast-forward 병합이 불가능할 때에도 --squash 옵션으로 병합을 실행하면 이력을 가지가 없는 하나의 선으로 정리할 수 있다. 이력을 한 줄로 정리하는 또 다른 방법으로 리베이스(rebase)가 있다. 그 결과는 fast-forward 병합과 유사하다.
Rebase는 무엇일까? 말 그대로 Base를 다시 설정한다는 의미이다. 그럼 Base를 어디로 다시 설정할까? my-branch 가 분기된 main 브랜치의 최신 커밋이다.
$ git checkout my-branch
$ git rebase main
$ git checkout main
$ git merge my-branch
명령 순서를 보면 알겠지만, 결과적으로 Fast-Forward Merge를 사용하는 것을 확인할 수 있다.
참고링크
https://hudi.blog/git-merge-squash-rebase/
https://d2.naver.com/helloworld/1859580