본문 바로가기
GIT

[GIT] git merge와 git rebase의 차이

by 무사뎀벨레 2021. 7. 16.

 

 

 

git에서는 브랜치를 병합할 때 사용하는 두 가지 방법이 있습니다.

1. git merge
2. git rebase

 

 

 

 

 

1. git merge


//1. master 브랜치로 이동
$git checkout master
//2. 병합할 브랜치를 master브랜치에 merge
$git merge [master에 병합할 브랜치 명]

//위 1,2를 한번에 표현 가능
$git merge [master에 병합할 브랜치 명] master
  • 병합을 하면 합쳐진 브랜치의 커밋 메시지가 중복으로 쌓이게 됩니다.
  • 커밋 순서를 바꾸지 않습니다.
  • 존재하는 브랜치가 변경되지 않습니다.
  • 새로운 merge commit을 생성합니다.
  • git merge --abort : 병합을 취소하는 명령어

 

 

  • 위 이미지는 Main 브랜치에 Feature 브랜치를 병합하는 과정을 나타냅니다.
  • 커밋 순서가 변경되지 않고, 기존 분기는 유지되는 모습을 확인할 수 있습니다.

 

 


1-2. git conflict

충돌 상황 발생


 

//merge 실행
$git merge [master에 병합할 브랜치 명]

//conflict 발생
CONFLICT (content): Merge conflict in [수정한 파일]
Automatic merge failed; fix conflicts and then commit the results.
  • 자동 merge가 실패했으니, 충돌 부분을 수정하고 결과를 다시 커밋 하라는 경고문이 노출됩니다.
  • 충돌이 발생한 이유는 두 브랜치에서 [수정한 파일]의 내용이 서로 다르기 때문입니다.
  • 이와 같이 같은 부분에 서로 다른 부분이 있을 경우, merge conflict가 발생할 수 있습니다.
  • 이를 해결하기 위해 충돌이 발생한 부분을 수정이 필요합니다.

 

//[수정한 파일] 내용 출력
//사용하는 툴이 있다면 툴을 사용하여 conflict가 발생한 부분 수정하여도 됨
//ex : intelliJ, eclipse, vscode 등

$ cat [수정한 파일]
1. 안녕하세요.
2. git merge 중
3. conflict 상황 입니다.
<<<<<<< HEAD
4. 오전에 작성하였습니다.
=======
4. 오후에 작성하였습니다.
>>>>>>>> [병합할 브랜치 명]
  • 위처럼 충돌이 일어난 부분은 <<<<<<<<======= 혹은, =======<<<<<<<< 사이에 표시됩니다.
  • <<<<<<<< 과 ======= 사이에 표시된 내용은 merge를 실행한 브랜치에 있는 내용을 나타냅니다.(HEAD가 가리키고 있는 내용)
  • 반대로 ======= 과 <<<<<<<< 사이에 표시된 내용은 merge 하고자 하는 브랜치(즉, 병합할 브랜치 명)의 내용을 나타냅니다.
  • 이 표시들을 기반으로 원하는 것을 선택하여 수정을 마친 후 충돌이 일어난 파일을 커밋 해주면 merge가 완성됩니다.(위에 표시된 기호들을 없앤 후 커밋)

 

$ cat [수정한 파일]
1. 안녕하세요.
2. git merge 중
3. conflict 상황 입니다.
4. 오후에 작성하였습니다.

- 위와 같이 수정 완료 후

 

$git add [수정한 파일]
$git commit
[master 7bj1583] Merge branch '[병합할 브랜치 명]'

- 성공적으로 수정하였을 경우의 출력 메시지입니다.

 

$git branch -d [병합 완료 된 브랜치 명]

- 병합이 끝난 브랜치를 삭제 처리합니다.

 

 

 

 

 

 

2. git rebase


 

  • Feature 브랜치를 Main 브랜치에 병합을 나타내는 이미지 입니다.
  • 위 그림처럼 Feature 브랜치의 커밋은 Main 브랜치가 가지고 있던 기존의 커밋 뒤에 위치하게 됩니다.

 

//master에 rebase 할 브랜치로 이동
$git checkout [rebase 할 브랜치]
$git rebase master

//rebase 할 브랜치를 master 브랜치에 merge
$git checkout master
$git merge [rebase한 브랜치]
  • 병합을 하면 브랜치의 커밋 메시지가 시간 순서대로 합쳐집니다.
  • 히스토리를 깔끔하게 유지하기 위해 사용합니다.
  • 전체 브랜치를 마스터 브랜치 끝에 위치 시킵니다, 그렇기 때문에 master 브랜치의 기존의 마지막 커밋 뒤에 병합할 브랜치의 커밋들이 합쳐지게 하여 master 브랜치에 재배치(rebase) 하는 것을 말합니다.

 

rebase 하기 전

 

rebase 된 후

 

  • 즉, 위 그림처럼 master 브랜치의 m2 상태에서 다른 브랜치의 f1, f2 커밋을 재배치(rebase) 하면, 두 번째 사진과 같이 m2 이후에 f1, f2 이 재배치됩니다.

 

 


2-1. rebase 진행 과정


 

//feature 브랜치로 체크아웃한 상태
$git checkout feature

 

feature 브랜치로 체크아웃한 상태, head는 feature를 가리키고 있음

 

 

$git rebase master

 

master와 feature의 공통 조상이 되는 base 커밋부터 현재 브랜치까지의 변경 사항(▵1, ▵2)을 구해서 patch로 저장해 둠

 

 

head를 master로 변경

 

 

Applying f1 : head가 현재 가리키고 있는 m2에 변경사항 ▵1 을 적용하여 새로운 커밋 f1'을 생성

 

Applying f2 : f1'에 변경사항 ▵2 을 적용하여 새로운 커밋 f2'을 생성

 

feature가 f2'를 가리키도록 함

 

 

$git merge feature

 

feature를 master로 fast-forward merge 하여 완료

 

 


2-2. rebase 사용법


 

//수정할 커밋들의 리스트 출력
//git rebase -i [수정을 시작할 커밋의 이전 커밋] 형식으로 입력
$git rebase -i HEAD~4

//4개의 커밋 리스트 노출
hint: Waiting for your editor to close the file...
pick 907c451 commit1
pick 1a7c765 commit2
pick v07c952 commit3
pick 40jc438 commit4

//사용할수있는 명령어 리스트
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.

 

pick

-커밋을 사용하겠다는 의미, 이를 이용해 커밋의 순서를 바꿀 수 있고, 커밋의 해시값을 이용해 특정 커밋을 가져올 수 있습니다.

- rebase 명령어를 실행하면 기본적으로 pick으로 설정돼있기 때문에 아무것도 변경하지 않고 종료한다면, 커밋에 대하여 어떠한 변경도 일어나지 않게 됩니다.

- 위 코드에서 commit2와 commit3의 순서를 바꾼다면, 커밋 순서와 커밋 해시값까지 변경됩니다.

reword

- 커밋 메시지를 변경하는 명령어

- 커밋 메시지를 변경할 커밋 앞에 reword 명령으로 수정하고, 저장하면 해당 커밋의 메시지를 다시 작성하는 에디터 창이 열리게 됩니다.

- 커밋 메시지와 커밋의 해시값 또한 변경됩니다.

edit

- reword 명령어는 커밋 메시지만 변경하는 명령이라면, edit 명령어는 커밋 메시지뿐만 아니라 커밋의 작업 내용까지 변경할 수 있습니다.

- commit4라는 커밋을 edit으로 바꾸고 저장 후 종료하면, 변경할 커밋으로 checkout됨, 그 상태에서 변경 작업을 수행하면 됩니다.

- 그 후 변경사항을 저장하기 위해 아래와 같은 명령어 입력

//staged 상태 변경
$git add . 

//변경할 커밋메시지 입력
$git commit --amend -m "commit4-amend"

//rebase처리
$git rebase --continue

- 커밋 내용과 메시지 모두 변경됩니다.

 

squash

- squash 명령어는 해당 커밋을 이전 커밋과 합치는 명령어입니다.

pick 907c451 commit1
pick 1a7c765 commit2
squash v07c952 commit3
pick 40jc438 commit4

- commit3 커밋을 직전 커밋인 commit2 커밋과 합치기 위해 squash로 변경합니다.

- 저장 후 종료하면 커밋 메시지를 수정할 수 있는 에디터 창이 뜹니다.

- 합쳐질 커밋들의 메시지를 확인한 후 그대로 종료하면 아래와 같이 이전 커밋과 하나로 합쳐진 것을 볼 수 있습니다.

pick 907c451 commit1
pick 73dc735 commit2
pick 40jc438 commit4

 

 

fixup

- fixup 명령어는 squash와 동일하게 해당 커밋을 이전 커밋과 합치는 명령어이지만, 커밋 메시지는 합쳐지지 않습니다.

- 결과적으로 이전 커밋의 커밋 메시지만 남게 됩니다.

exec

- exec 명령어를 이용하면, 각각의 커밋이 적용된 후 실행할 shell 명령어를 지정할 수 있습니다.

- 각각의 커밋이 실행된다는 것을 확인하기 위해 중간중간에 메시지를 넣는 행위입니다.

drop

- drop 명령어는 커밋 히스토리에서 커밋을 삭제합니다.

- drop으로 변경 후 저장하면, 해당 커밋이 drop 되는 것을 확인 가능합니다.

pick 907c451 commit1
drop 73dc735 commit2
pick 40jc438 commit4

 

 


2-3. rebase 시 유의사항


- 이전의 커밋 히스토리를 변경하기 때문에 항상 주의가 필요합니다.

- 만약 이미 Github과 같은 원격 저장소에 push까지 한 커밋이라면, 변경한 커밋들은 원격 저장소에 push 되지 않을 것입니다.

- 이때 git push --force  혹은 git push -f 명령으로 강제로 원격 저장소에 커밋 히스토리를 덮어쓸 수 있습니다.

- 만약 이전에 push 한 커밋들을 다른 개발자들과 공유하고 있었다면, 커밋 히스토리의 불일치가 발생해 git이 꼬이는 현상이 발생할 수 있습니다.

 

 

 

 

반응형

'GIT' 카테고리의 다른 글

[GIT] git restore 를 이용한 staging area나 working directory 변화 취소  (0) 2022.01.07
[GIT] git 설치하기  (2) 2021.07.19
[GIT] Git add, commit, push 취소  (2) 2021.07.16
[GIT] Git 명령어  (2) 2021.07.16
[GIT] Git 이란?  (0) 2021.07.15

댓글