본문 바로가기

카테고리 없음

Git merge,rebase,cherry-pick 작동에 대해 (자세한 그림설명 有)- professional git 발췌

728x90

professional git 9챕터 내용을 발췌한 포스팅이다.

 

THE BASICS OF MERGING

 

The Merge Command

git merge <target-branch> # 현재 branch로 target-branch를 merge 

git merge --abort

 

merge가 성공하면 target branch의 내용이 현재 branch에 반영된 것이다. 현재 branch의 위치는 바뀌고 target branch의 위치는 그대로다.  

Preparing for a Merge

commit 되지 않은 변경이 있으면 merge 할 수 없다. 

commit 하거나 git stash 명령으로  working directory와 staging area를 'clean' 상태로 만든다.

 

Types of Merges

Fast-Forward–an Optimization

 

target-branch가 가리키는 commit이 현재 branch가 가리키는 commit 의 후손일 경우, 즉 두 branch가 갈라지지 않은 경우 ,  fast forward 를 할 수 있다.

이 경우 현재 branch 를 target branch의 위치로 옮기는 일만 일어난다.

파일의 내용을 통합하는 새로운 commit을  만들 필요가 없다. 따라서 merge conflict도 일어나지 않는다.   

 

 

 

 

 

 

 


merge 후에 두 branch에 대한 log는 아래처럼 동일하다.

 

git push를 실행하면 remote는 fast forward로 remote branch에 local branch를 merge 할 수 있을 때만 push를 받아준다.
즉 다른 사용자가 이전에 해당 remote branch에 대해 push한 내용이 없어야 한다. 

 

 

Three-Way Merge

 

 

현재 branch 가 master인 상황에서 아래 명령을 실행하면,

$ git merge feature

 

  1. master와 feature의 common ancestor를 구함 (C2)
  2. C2와 feature (C5) 사이의 delta(diff)를 구함
  3. 위에서 구한 delta를 master(C4) 에 반영하는  새로운 merge commit 생성   

 

 

 

 

 

 

Rebasing—Merging with History

 

git rebase <target branch>

git rebase --continue | --skip | --abort 

 

 

 

 

 

rebase는 common ancestor 다음 commit (C3) 부터 현재 branch(feature) 가 가리키는 commit 을 rewriting 한다.

rewriting 되는 commit 들은 target branch (master) 의 다음 commit 으로 붙는다. 

rewriting 되는 commit들은 원본 commit 이 만들어낸 변경(delta) 를 새로운 부모 commit 에 적용(replay) 한다. 

 

현재 branch가 feature인 상태에서 아래 명령을 실행한 예다. 

(feature) $ git rebase master

 

 

 

 

 

 

 

 

 

 

 

 

 

 

rebase 이후에 master가 현재 branch인 상태에서 git merge feature를 하면 fast-forward merge가 일어나고 master와 feature가 동일한 commit을 가리키게 된다.

이 commit은 3-way merge를 했을 때 만들어진 merge commit과 동일한 컨텐츠를 가지게된다.

 

현재 branch가 master인 상태에서 git rebase feature를 실행하는 것도 가능하다.이렇게 하면 이어서 merge 명령 (fast forward를 위한) 을 실행하지 않아도 되기 때문에 더 간단하다.
하지만 이렇게 하면 master 를 rewriting하는 것이기 때문에 이미 push된 commit이 rewriting되지 않도록 주의해야 한다. 

 

merge대신 rebase를 하는 이유는 rebase가 더 깔끔한 history 를 남기기 때문이다.

3-way merge를 하면 두 개의 branch가 갈라졌다 합쳐지는 모양의 history를 갖게되지만 rebase 는 linear history를 만들어준다. 결과적으로 history 를 읽고 파악하기가 더 쉬워진다. 

 

그 외에 merge에 비해 아래와 같은 차이가 의미있다.

  1. merge commit을 만들지 않는다.
  2. 기존 commit을 rewrite해서 새로운 commit으로 대체하는 일이 일어난다. (interactive rebase 는 이 과정에서 사용자가 좀 더 제어를 할 수 있는 것임) 

 

Cherry-Picking 

 

다른 branch에서 일부의 commit 들이 만든 변경만 현재 branch에 반영하고 싶다면  git cherry-pick 을 쓰면된다.

 

 

 

 

$ git cherry-pick d9e8b2c

 

 

 

commit message가 C5 인 commit이 master에 생겼지만 feature에 있는 해당 commit과는 SHA1이 다르다.


Specifying a Range of Commits

단일 commit 뿐 아니라 commit의 범위를 cherry-pick의 argument로 줄 수 있다.

$ git cherry-pick <starting-commit>..<ending-commit>

starting-commit 의 다음 commit 부터 ending commit까지 cherry-pick한다. starting commit부터 시작하지 않는다는 것을 유의할 것

Merge Operations

regular merging , rebasing , cherry-picking 을 통틀어 merge operation이라고 하자.

이어지는 설명에서 'merge' 나 'merge operation' 이라고 하면 위의 세가지를 공통으로 가리키는 말이다. 

 

Undoing Merge Operations

 

$ git reset --hard <SHA1 value that was current before the merge operation>

 

Finding the Right SHA1 Value

 

ORIG_HEAD

merge 전 HEAD가 가리키던  commit 을 저장하는 reference. .git/ORIG_HEAD 파일로 존재한다.

 

 

 

 

 

 

Reflog

 

Log

 

변경이 최근에 일어났고 단순하다면 git log로 찾을 수도 있다.

 

Tag

reset 할 tag를 미리 만들어 놓을 수도 있다.

 

$ git reset before_merge

 

DEALING WITH CONFLICTS

merge opertaion 에서 conflict 발생하는 것은 일상적인 일이다. 

Git이 이런 경우를 어떻게 처리하는지, 사용자가 conflict를 어떻게 resolve하는지 알아본다.

Merging Is a State

Git은 merge operation 을 수행할 때 해당하는 merge state에 들어간다 .

별문제없이 끝난 경우에는 사용자는 이 state 변화를 알아차리기 힘들지만, merge conflict가 발생하면 명확히 알 수 있다. 

이 state에 들어가면 사용자가 conflict를 resolve할 때까지 branch를 변경하는  작업등을 할 수 없게된다.

 

Developer@DESKTOP-80SLL4U MINGW64 ˜/cpick (master)$ git cherry-pick c7a2be5
error: could not apply c7a2be5… C5Developer@DESKTOP-80SLL4U MINGW64 ˜/cpick (master|CHERRY-PICKING)$ git checkout feature
file1.txt: needs merge
error: you need to resolve your current index first

 

conflict가 전혀 발생하지 않으면 Git은 commit 까지 완료하고 merge state에서 빠져나온다.

conflict가 발생하면 conflict가 발생한 파일을 제외한 나머지 파일들은 staging 한다.

사용자가 conflict를 resolve한 후 그 것을 staging 하면 Git에게 resolve되었다고 알리는게 된다. 

 

Error Messages for Conflict 

 

 

rebase는 현재 branch 에만 속하는 여러 개의 commit을 target branch 다음에 붙여서 rewriting하는 것이다.

각각의 commit 마다 rewriting하는 작업이 따로따로, 순서대로 진행되는 것이다.

하나의 commit을 rewriting할 때마다  delta를 계산해서 적용해야 하는데 이 때 merge conflict가 일어날 수 있다.

즉 각각의 commit을 rewriting하는 것이 개별적인  'merge operation' 인 것이다.

conflict를 resolve한 후 아래 명령을 실행하면 다음 commit을 rewriting하는 단계로 넘어간다.

$ git rebase --continue


아래는 이번 commit의 delta를 적용하는 것을 건너뛰는 것이다.

$git rebase –-skip


아래는 rebase 자체를 취소하는 것이다.

$git rebase --abort #  git merge에서도 사용할 수 있음.

 

Aborting the Operation

 

$ git merge --abort

$ git rebase --abort

$ git cherry-pick --abort

 

Dealing with Conflicts—the Workflow

 

conflict가 발생하면 Git 은 아래와 같은 일을 한다.

  1. 성공적으로 merge된 파일은 staging area에 add 한다.
  2. conflict가 발생한 파일은 conflict marker를 내용에 삽입한다.  working directory 에서만 수정하고 staging 하지 않는다.
  3. 사용자에게 에러 메시지로 어떤 파일이 conflict가 발생했는지 알린다.

 

사용자는 아래와 같이 해야한다.

  1. conflict가 발생한 파일이 무엇인지 확인한다. merge 명령의 에러 메시지에도 내용이 있고 이후에 git status를 실행해도 알수있다.
  2. 파일을 편집기로 열어서 conflict marker가 있는 부분을 참고해서 원하는 내용으로 편집한다. conflict marker는 제거해야 한다.
  3. resolve가 끝난 파일은 staging 한다 (add) . 이렇게 해서 Git에게 resolve가 끝났다는 것을 알려주는 것이다.
  4. commit 한다. 모든 conflict를 resolve하기 전에는 commit이 불가능하다.

 

commit까지 완료되면 Git은 MERGING  state 에서 빠져나온다.

 

Visualizing the Merging Workflow with Conflicts

 

$ git checkout master

$ git merge feature

 

Which Files?

 

$ git status

 

 

ADVANCED TOPICS

 

Interactive Rebasing

 

일반적인 rebase는 대상 commit들을  rewrite한다. interactive rebase 는 각각의 commit을 rewrite할 때 사용자가 개입해서 원하는 변형을 할 수 있게 해주는 것이다. 즉 기본적으로는 동일한 일을 한다고 할 수 있다.

하지만 용도는 다르다. 일반적인 rebase는 merge의 대안으로 쓰이지만 interactive rebase는  기존 commit 들을 rewriting 하기 위한 목적으로만 쓰인다.  따라서 interactive rebase는 보통 target branch를 지정하지 않고 current branch 내에서 commit 들을 rewriting 한다. 

'rebase' 라는 용어가 commit 들의 뿌리(base) 를 다른 branch로 변경한다는 어감이 있는데, 그렇다면 보통의 interactive rebase 는  'rebase'를 하지 않는 셈이다. 

 

$ git rebase -i <starting commit> 

starting commit 의 다음 commit부터 rewriting을 한다.