그림으로 이해하는 merge --no-ff, squash, rebase 그리고 pull request(PR)

ChanYoung Kim·2022년 5월 31일
4

git

목록 보기
1/1
post-thumbnail

pull request(PR)을 할 때 merge 옵션이 있다
image

여기서 옵션들은 merge의 옵션들과 같다
Create a merge commit -> git merge <branch> --no-ff
Squash and merge -> git merge <branch> --squash
Rebase and merge -> git rebase <branch>

세가지를 비교를 비교해보기 위해 상황이 다음과 같다고 가정하자
image
위와 같은 상황에서 긴급한 버그 수정을 위해 HotfixMaster로부터 분기하여 작업을 두개(Commit 2개) 진행했다고 가정
image

master > git log
* 03c0e57 (Hotfix) fix bugB
* 1693ca1 fix bugA
* a7ee0c1 (HEAD -> master, origin/master) base commit

1. git merge --no-ff, --ff

git merge --no-ff를 알기 위해선 --ff를 알아야한다
-ff는 fast-forward 의 줄임말로써 빨리감기라는 뜻이다


git merge --ff를 할 경우

master > git merge Hotfix --ff 
Updating a7ee0c1..03c0e57
Fast-forward
master > git log
* 03c0e57 (HEAD -> master, Hotfix) fix bugB
* 1693ca1 fix bugA
* a7ee0c1 (origin/master) base commit
image

Master의 log에는 Hotfix의 커밋들만 추가되고 따로 추가되는 commit은 없다

-ffgit merge 의 기본 옵션이기때문에 git mergegit merge --ff 는 같은 뜻이다

초기화

다른 옵션도 테스트하기 위해 git reset --hard <돌아가려는 workspace>

master > git reset --hard origin/master 
HEAD is now at a7ee0c1 base commit
master > git log
* 272c460 (Hotfix) fix bugB
* 252a393 fix bugA
* 498325c testC.txt
* a7ee0c1 (HEAD -> master, origin/master) base commit

git merge --no-ff를 할 경우

master에 변경이력이 없어도 fast-farward를 하지 않고 no-fast-farward를 한다

no-fast-farward: master의 log에는 Hotfix의 작업내역 commit 2개와 병합commit(Merge ~ from)이 기록

master > git merge Hotfix --no-ff  // merge내용 작성
Merge made by the 'recursive' strategy.
master > git log
*   842db67 (HEAD -> master) Merge branch 'Hotfix'
|\  
| * 03c0e57 (Hotfix) fix bugB
| * 1693ca1 fix bugA
|/  
* a7ee0c1 (origin/master) base commit
image

이 때, 만들어진 commit(Merge branch 'Hotfix')는 2개의 Parent(Hotfix, master)를 갖는다


--ff를 적용이 안되고 --no-ff로 적용되는 경우

하지만 --ff로 옵션을 주어도 --no-ff로 적용이 되는 경우가 있다

  • hotfix가 작업을 하는동안 master에 변경 내역이 있는 경우

master 작업 추가

master > vi testC.txt
master !1 > git add .
master +1 > git commit -m "testC.txt"  
master > git log
* | 00c9de9 (HEAD -> master) testC.txt
| * 03c0e57 (Hotfix) fix bugB
| * 1693ca1 fix bugA
|/  
* a7ee0c1 (origin/master) base commit
image

merge

master > git merge Hotfix --ff   
// Merge branch 'Hotfix'
master > git log 
*   1925dd0 (HEAD -> master) Merge branch 'Hotfix'
|\  
* | 00c9de9 testC.txt
| * 03c0e57 (Hotfix) fix bugB
| * 1693ca1 fix bugA
|/  
* a7ee0c1 (origin/master) base commit
image

--no-ff로 적용이 된다

2. git merge --squash

squash란 영단어 뜻 그대로 뭉개다 라는 뜻이다
masterHotfix의 commit들이 반영되지않고 새 commit만 반영된다

master > git merge --squash Hotfix
Updating a7ee0c1..03c0e57
Fast-forward
Squash commit -- not updating HEAD
 testA.txt | 2 +-
 testB.txt | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
 
// squash는 commit이 생성되지않고 Staged상태만 된다
// commit 추가

master +2 > git add .
master +2 > git commit -m "fix bugA,B"
[master 48975f6] fix bugA,B
 2 files changed, 2 insertions(+), 2 deletions(-)
master > git log
* 48975f6 (HEAD -> master) fix bugA,B
| * 03c0e57 (Hotfix) fix bugB
| * 1693ca1 fix bugA
|/  
* a7ee0c1 (origin/master) base commit
image

이 떄, fix bugA,B의 parent는 한개(master)이다

3. git rebase

rebase란 모든 commit이 합쳐지지 않고 각각 master에 추가된다

rebase의 기능을 알기 위해서 masterbase commit으로 reset한뒤 작업변경을 하고 testC.txtcommit을 추가한다

master > git reset --hard origin/master
HEAD is now at a7ee0c1 base commit
master > vi testC.txt
master !1 > git add .
master +1 > git commit -m "testC.txt"
[master 498325c] testC.txt
 1 file changed, 1 insertion(+), 1 deletion(-)
master > git log
 498325c (HEAD -> master) testC.txt
| * 03c0e57 (Hotfix) fix bugB
| * 1693ca1 fix bugA
|/  
* a7ee0c1 (origin/master) base commit
image

현재상태


rebase 작업 진행

master > git checkout Hotfix
Switched to branch 'Hotfix'
Hotfix > git rebase master
First, rewinding head to replay your work on top of it...
Applying: fix bugA
Applying: fix bugB
Hotfix > git checkout master
Switched to branch 'master'
master > git merge Hotfix
Updating 498325c..272c460
Fast-forward
 testA.txt | 2 +-
 testB.txt | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
master > git log
* 272c460 (HEAD -> master, Hotfix) fix bugB
* 252a393 fix bugA
* 498325c testC.txt
* a7ee0c1 (origin/master) base commit
image

fix bugA, fix bugBHotfix에서 작업한 commit의 내용이지만 commit의 id를 보면 다른 것을 확인 할 수 있다

4. 무엇을 선택해야하는가

  • rebase의 경우 자칫 잘못하면 엉켜버릴수가 있다.
  • sqaush : masterHotfix이 commit 2개(commit A,commit B)를 PR요청하고 squash로 merge했다고 가정
    • merge 이후에, Hotfix 가 추가작업(commit C, commit D)을 하고 또 PR을 보내면 4개의 커밋(commit A, commit B, commit C, commit D)이 PR내역에 포함되어 보내진다. 과거에 했던 commit (commit A, commit B)이 포함되는 문제가 있다
  • merge: 그냥 merge를 사용할 경우 sqaush의 위 문제점이 발생하지않고 작업했던 내역들만 PR 요청을 할 수 있다. 하지만 Hotfix의 commit(commit C, commit D)들이 전부 master의 log의 올라간다.
    • 반면에 -squash는 한개의 commit(위 예제경우 fix bugA,B)만 master에 올라간다. commit A, commit B는 PR 내역을 들어가면 볼 수있다.

결론

  • 일회성 branch의 경우 :squash
  • 지속적인 branch의 경우 : merge --no-ff
    • ex) git flow 모델에서 develop
profile
수원호랭이주먹

1개의 댓글

comment-user-thumbnail
2023년 11월 13일

와 진짜 너무 감사합니다! 복받으실겁니다

답글 달기