본문 바로가기

Study/Git & Github

Branch & Merge

What is branch & merge? 

over view

branch and merge

 

깃 프로젝트를 생성하면 기본적으로 "master"라는 branch가 생성된다. 

새롭게 만들어지는 commit은 각각 이전 commit을 가리키고 있다(pointer). 서로를 가리키면서 이어진 commit들을 "branch"라고 하고, branch와 branch를 하나의 branch로 병합하는 것을 "merge" 라고 한다.

 

+) HEAD 

  • 모든 commit을 보여주는 git log를 이용하면 head -> 라는 것이 표시됨 
  • "HEAD" => current branch
  • current branch => 가장 최근에 한 커밋, 또는 가장 최근에 working directory로 불러온 커밋

 

Branch 

git branch | local repo에 저장되어 있는 branch와, 현재 branch(*) 확인

  • git branch -v | branch와 각 branch의 최신 commit을 함께 확인
  • git branch -d 이름 | branch 삭제

 

git branch 이름 | 새로운 branch가 생성된다. 새로 만들어진 branch와, 이 명령어를 입력한 working area, 즉 현재 branch의 상태는 똑같다. 

 

git switch 이름 | 해당 branch로 이동한다

  • 이 branch에서 수정하고 commit하는 내용들은, 이전의 branch에는 등록되지 않는다. 
  • git switch -C 이름 | 새로운 branch를 만들고 그 branch로 바로 이동
    • switch is same with checkout
    • git checkout branch-name or 해쉬코드
    • git checkout -b 이름 | 새로운 branch를 만들고 그 branch로 바로 이동

 

branch와 branch 사이의 차이점 확인하기

  • git log | 현재 branch의 모든 commit이 출력되는데, 어떤 commit이 어떤 branch에서 파생된 것인지는 확인할 수 없다. 
  • git log --branches --decorate --graph --oneline
    • branches 옵션은 현재 branch 뿐만 아니라 repo에 저장된 모든 branch를 보여준다. ???) --all과 차이점? 
    • decorate 옵션은 각 brach의 이름을 확인할 수 있고, 이 이름은 그 branch의 최신 커밋 옆에 출력된다. 
    • graph 옵션은 branch가 어떤 커밋에서 갈라져 나왔는지, 직관적으로 확인할 수 있게 만들어주는 옵션이다.

 

  • git log branch1..branch2 | branch 2에는 있고, branch 1에는 없는 commit을 출력, -p옵션과 함께 이용하면 커밋 내용의 차이까지 확인해볼 수 있다.
  • git diff branch1..branch2 | branch 2에는 있고, branch 1에는 없는것, branch 2에는 없고 branch1에는 있는 것을 출력한다. -p 옵션을 이용하지 않아도 내용의 차이를 확인할 수 있다. 

 

  • git log --pretty=format '~' | config --global alias.hist 또는 config --global -e 에서 log format을 커스터마이징 하고 "hist"라는 alias에 등록할 수 있다. 
    • git hist branch1..branch2 | 기본적으로 log 명령어 이기 때문에 branch..branch2 라고 입력할 수 있다
    • hist = log --graph --all --pretty=format:'%C(yellow)[%ad]%C(reset) %C(green)[%h]%C(reset) | %C(white)%s %C(bold red){{%an}}%C(reset) %C(blue)%d%C(reset)' --date=short
    • 나는 hist에 git log --branches --decorate --graph --oneline를 등록해 놨다 

 

git branch --move 이름 이름2 | branch 이름을 이름2로 수정

git branch --merged | current branch에 merge된 branch를 확인

git branch --no-merged | current branch에 merge되지 않은 branch를 확인

git branch --all | remote repo에 저장되어 있는 branch 까지 모두 확인

 

+) 

 

git push origin --delete 이름 | 로컬 뿐만 아니라 깃허브 서버에서도 branch 삭제

git push --set-upstream origin 이름 | 깃허브 서버에 branch 등록

 

Merge 

merge는 branchB에서 독립적으로 개발한 commit들을 branchA에 포함시켜서, branchA가 B에서 개발한 commit들을 history에 기록하는 것을 말한다. 즉 branchA가 B에서 발생한 모든 변경사항들을 알게 되는 것이다. 

 

fast-forward merge 

ff merge는 branch A에서 파생된 branch B를 다시 A에 병합(merge)할 때, A의 pointerB의 최신 commit을 참조하게 만들기만 하면 될 때를 말한다. B가 파생된 이후 branch A에 아무런 커밋이 추가되지 않았을 때 ff가 가능하다.

다만 log에 merge commit을 남기지 않기 때문에, commit 기록을 남기고 싶다면 --no-ff 옵션을 이용해서 "three-way merge"를 이용할 수 있다. 

 

git merge branch1 | 현재 branch에 branch1를 병합, 즉 branch1에 있는 내용을 현재 branch도 갖게 만든다. 이 때 merge commit이 생길 수도있고, 안생길 수도 있다.

  • 현재 브랜치가 병합된 브랜치의 모든 commit들을 알고 있으므로, 보통 병합된 브랜치를 branch -d 이름을 이용해서 삭제한다. 

 

git merge --abort | merge 취소

 

three-way merge

log를 남기고 싶을 때

git branch --no-ff

 

git merge --no-ff 이름 | no fast-foward, branch를 no-ff 옵션으로 merge, log에 남길 수 있음 

  • branch -d 이름을 이용해서 branch를 삭제한 뒤에도, "Merge branch 'feature-c'" 라는 커밋 메세지가 자동으로 남겨지기 때문에 log에서 확인할 수 있다

 

fast-foward가 불가능할 때

three way merge

Your Work branch가 master branch에서 파생된 이후에, master branch에서 새로운 커밋(a, b)이 발생한 경우에는 ff merge가 불가능하다. 왜? 

현재 master의 포인터는 최신 commit인 b를 가리키고 있는데, ff merge는 이 포인터가 병합될 branch의 최신 commit인 3️⃣을 참조하게 만든다. 즉 ff merge에서 pointer는 b가 아니라 3️⃣을 가리킨다. 그렇게 되면 master는 a와 b의 변경 사항을 잃게 되기 때문에 ff merge가 불가능하다.

이럴 때에는 git에서 자동으로 three-way merge를 하게 되는데, 3️⃣과 ⓑ 커밋을 가리키고 있는, 즉, 두 개의 부모를 가지고 있는 ⓒ 커밋을 이용하는 방법이다. 정리하자면 three way merge는 master의 최신 commit c를 만든 뒤, master의 최신 커밋, 즉  커밋의 포인터가 work의 최신 commit3️⃣과 ⓑ를 동시에 가리키게 만든다. 

 

git merge 이름 -m "merge comit message" | fast-foward가 불가능할 때, git은 자동으로 merge commit(c)를 만든다

 

깔끔하게 merge : rebase | three-way ➡ fast-forward merge 

https://www.atlassian.com/git/tutorials/rewriting-history/git-rebase

 

rebase는 feature의 첫번째 commit인 ①을 ⓑ가 아니라, master의 최신 commit인 ⓓ를 참조하도록 만들어서, 새로운 merge commit을 만들지 않고 merge하는, 즉 three way가 아니라 ff merge를 가능하게 만드는 방법이다. 

 

+) 하지만 여러 개발자가 같은 branch에서 코드를 수정하고 있다면 주의해야 한다

  • feature 포인터가 참조하는 커밋을 변경하면(b->d), feature의 기존 커밋(1, 2, 3)과 내용은 같지만 다른 커밋(1`, 2`, 3`)으로 바뀐다. 즉, rebase는 항상 기존의 커밋을 새로운 커밋으로 만든다! 
  • ???) 이것을 push를 이용해서 서버에 업로드하면, 다른 개발자가 작업 중인 커밋(1, 2, 3)과 다른 커밋을 업로드하는 것이기 때문에, 이후에 merge conflict이 발생할 수 있다.
  • 따라서 서버에 업로드 되어있는 commit은 절대 rebase하지 않는다. 예를 들어 1, 2, 3이 서버에 업로드되어 있다면 이 커밋을 rebase하지 않도록 주의한다. 
  • 서버에 업데이트하지 않는 local의 commit에 한해서는 자유롭게 rebase를 할 수 있다. 

 

rebase

 

rebase 과정 | feature의 첫번째 커밋이 master의 최신 커밋을 참조하도록 만들기

  • git checkout feature | feature branch로 이동
  • git rebase master | feature를 master branch에 rebase(feature의 포인터를 d=>f)
  • git checkout master | master branch로 이동
  • git merge feature | master에 feature branch를 병합
  • git branch-d feature | feature branch 삭제

 

rebase 결과

 

branch위에 또다른 branch가 chaining 되어있을 때 rebase --onto 옵션을 유용하게 사용할 수 있다.

 

rebase --onto

git rebase --onto master branchA branchA-1 | branchA에서 파생된 A-1의 첫번째 커밋의 포인터를 master branch의 최신 커밋을 참조하도록 만든다(rebase).

git checkout master | master로 이동(current branch === master) 

git merge branchA-1 | current branch(master)에 branchA-1을 ff-merge 

git branch -d branchA-1

 

master에서 파생된 profile, profile에서 파생된 profile-ui
rebase --onto

 

Merge-Conflict

merge conflict

merge conflict이란?

A merge conflict is an event that takes place when Git is unable to automatically resolve differences in code between two commits. Git can merge the changes automatically only if the commits are on different lines or branches

 

깃은 두 브랜치에서 서로 다른 파일을 수정한 뒤에, 하나의 브랜치를 다른 브랜치에 merge하는 것을 자동으로 할 수 있다. 또는 같은 파일을 수정하더라도, 서로 다른 line을 수정했다면 두 변경사항을 모두 반영하는 merge를 한다. 즉 conflict을 발생시키지 않는다.

그런데 위의 그림에서는, 서로 다른 브랜치에서 같은 파일의 같은 line을 수정한 뒤 각각 커밋했다. 그런 다음 두 브랜치를 합병(merge)하려고 하자, 깃은 어떤 변경 사항을 반영해야 할지 자동으로 결정할 수 없어 conflict이 발생했다는 메세지를 출력했다.

 

how to resolve merge conflict?

- 수동으로 해결

  • conflict을 발생시킨 파일(main.txt)을 열어서 직접 수정한다
  • git add main.txt | 수정한 파일을 staging area로 이동
  • git merge --continue | ff merge가 아니기 때문에 commit이 만들어지면서 re-merge

 

- vscode로 해결

[merge]
	tool = vscode
[mergetool "vscode"]
	cmd = code --wait $MERGE
  • git config --global -e에서 merge tool을 설정한다
  • git merge feature에서 conflict이 발생했을 때 git mergetool 명령어를 이용한다
  • git mergetool | 4 options  
    • accept current change | current branch에서 수정한 사항을 반영
    • accept incoming change | 병합될 branch에서 수정한 사항을 반영
    • accept both changes | 두 개 branch에서 수정한 사항 전부 반영 
    • compare changes | 두 개 branch의 수정 사항을 비교

 

  • main.txt 파일이 선택한 옵션에 따라 업데이트되고, conflic이 발생했을 때 내용이 담겨있는 main.txt.orig라는 파일이 생성된다. 
    • git config --global mergetool.keepBackup false | orig 파일이 생기지 않도록 옵션을 off

 

  • git add main.txt | 수정한 파일을 staging area로 이동
  • git merge --continue | ff merge가 아니기 때문에 commit이 만들어지면서 re-merge

 

- p4merge로 해결

많은 개발자들이 merge tool로 p4merge를 이용한다. 

 

cherry pick

what is cherry pick?

git cherry-pick is a powerful command that enables arbitrary Git commits to be picked by reference and appended to the current working HEAD.
Cherry picking is the act of picking a commit from a branch and applying it to another. git cherry-pick can be useful for undoing changes.

 

임의의 branch에서 특정한 commit만 골라서(pick), 다른 branch에 연결하고 싶을 때 유용하게 이용할 수 있다.

 

 

git cherry-pick 해쉬코드 | current branch에 picked commit을 연결

 

git cherry-pick

'Study > Git & Github' 카테고리의 다른 글

Undo  (0) 2022.04.14
Stash  (0) 2022.04.13
Basic  (0) 2022.04.11
over view  (0) 2022.04.11
What is Git & VCS?  (0) 2022.04.09