[ProGit] 3. Git 브랜치

KIM KYUBIN·2022년 9월 8일
0

ProGit

목록 보기
3/3

3.1 브랜치란 무엇인가

  • Git은 데이터를 Change Set이나 변경사항으로 기록하지 않고 일련의 스냅샷으로 기록한다.

  • 커밋하면 Git은 현재 Staging Area에 있는 데이터의 스냅샷에 대한 포인터, 저자나 커밋 메시지 같은 메타데이터, 이전 커밋에 대한 포인터 등을 포함하는 커밋 객체를 저장

  • 이전 커밋 포인터가 존재하기 때문에 현재 커밋이 무엇을 기준으로 바뀌었는지 알 수 있다.

🎲 파일이 3개 있는 디렉토리를 커밋하려고 한다.

  1. 커밋하면 먼저 루트 디렉토리와 각 하위 디렉토리의 트리 객체를 체크섬과 함께 저장소에 저장

  2. 그 다음에 커밋 객체를 만들고 메타데이터와 루트 디렉토리 트리 객체를 가리키는 포인터 정보를 커밋 객체에 넣어 저장

[커밋과 트리 데이터]

  • 다시 파일을 수정하고 커밋하면 이전 커밋이 무엇인지도 저장

[커밋과 이전 커밋]

  • 브랜치 : 커밋 사이를 가볍게 이동할 수 있는 포인터 같은 것

[브랜치와 커밋 히스토리]

3.1.1 새 브랜치 생성하기

$ git branch <브랜치명>
  • 새로 만든 브랜치는 자동으로 마지막 커밋을 가리킨다.

[한 커밋 히스토리를 가리키는 두 브랜치]

  • 지금 작업 중인 브랜치가 무엇인지는 어떻게 알까?
    ▶️ GitHEAD라는 지금 작업하는 로컬 브랜치를 가리키는 특수한 포인터가 있다.

[현재 작업 중인 브랜치를 가리키는 HEAD]

  • git log --decorate 를 사용하면 브랜치가 어떤 커밋을 가리키는지 확인 가능
$ git log --oneline --decorate

3.1.2 브랜치 이동하기

  • git checkout 으로 다른 브랜치로 이동할 수 있다.
$ git checkout <브랜치명>

▶️ HEAD는 지정한 브랜치를 가리킨다.

  • 커밋을 새로 하면 HEAD가 가리키고 있는 브랜치는 새로운 커밋을 가리킨다.

  • 이전 커밋을 가리키고 있는 HEAD가 가리키는 브랜치에서 파일을 수정한 후 커밋하면 기존의 커밋 객체와 합쳐지는 것이 아니라 별도의 커밋 객체가 생긴다.

3.2 브랜치와 Merge의 기초

  • 일반적인 진행 순서
  1. 웹사이트가 있고 뭔가 작업을 진행하고 있다.
  2. 새로운 이슈를 처리할 새 브랜치를 하나 생성
  3. 새로 만든 브랜치에서 작업 진행
  • 도중 문제가 생겨서 그것을 해결하는 Hotfix를 만들어야 할 경우
  1. 새로운 이슈를 처리하기 이전의 운영 브랜치로 이동
  2. Hotfix 브랜치 생성
  3. 수정한 Hotfix 테스트를 마치고 운영 브랜치로 Merge
  4. 다시 작업하던 브랜치로 옮겨서 작업 재진행

3.2.1 브랜치의 기초

🎲 지금 작업하는 프로젝트에서 이전에 master 브랜치에 커밋을 몇 번 했다고 가정

[현재 커밋 히스토리]

  • 이슈 관리 시스템에 등록된 53번 이슈를 처리하려고 한다.
    ▶️ 이 이슈에 집중할 수 있는 브랜치를 새로 하나 만든다.

  • git checkout -b 명령은 브랜치를 만들면서 Checkout까지 한번에 처리

$ git checkout -b <브랜치이름>

# 예시
$ git checkout -b iss53

[브랜치 포인터를 새로 만듦]

▶️ iss53 브랜치를 Checkout 했기 때문에 작업을 하고 커밋하면 iss53 브랜치가 앞으로 나아간다.

[진행 중인 iss53 브랜치]

🎲 만들고 있는 사이트가 문제가 생겨서 즉시 고쳐야 한다.

  • 버그를 해결한 Hotfix에서 iss53이 섞이는 것을 방지하기 위해 iss53과 관련된 코드를 어딘가에 저장해두고 원래 운영 소스로 복구해야 한다.
    ▶️ Git을 사용하면 그냥 master 브랜치로 돌아가면 된다.

  • 하지만, 아직 커밋하지 않은 파일이 Checkout할 브랜치와 충돌 나면 브랜치를 변경할 수 없다.

  • 작업하던 것을 모두 커밋하고 master 브랜치로 옮긴다.
    ▶️ 이때 워킹 디렉토리는 53번 이슈를 시작하기 이전 모습으로 되돌려지기 때문에 새로운 문제에 집중할 수 있는 환경이 만들어진다.

  • 해결해야 할 Hotfix가 생겼을 때 Hotfix 브랜치를 만들고 새로운 이슈를 해결할 때까지 사용한다.

[master 브랜치에서 갈라져 나온 hotfix 브랜치]

  • 운영 환경에 적용하려면 문제를 제대로 고쳤는지 테스트하고 최종적으로 운영 환경에 배포하기 위해 hotfix 브랜치를 master 브랜치에 합쳐야 한다.
$ git merge hotfix
  • A 브랜치에서 다른 B 브랜치를 Merge 할 때 B 브랜치가 A 브랜치 이후의 커밋을 가리키고 있으면 A 브랜치가 B 브랜치와 동일한 커밋을 가리키도록 이동
    ▶️ Fast forward

🎲 hotfix는 master 브랜치에 포함됐고 운영 환경에 적용할 수 있는 상태가 되었다고 가정

[Merge 후 hotfix와 같은 것을 가리키는 master 브랜치]

  • 급한 문제를 해결하고 master 브랜치에 적용하고 나면 다시 일하던 브랜치로 돌아가야 한다.

  • 더 이상 필요없는 hotfix 브랜치 삭제

$ git branch -d hotfix
  • 이제 53번을 처리하던 환경으로 돌아가서 작업 계속 진행

[master와 별개로 진행하는 iss53 브랜치]

3.2.2 Merge의 기초

🎲 53번 이슈를 다 구현하고 master 브랜치에 Merge하려고 한다.

  • git merge 명령으로 합칠 브랜치에서 합쳐질 브랜치를 Merge
$ git checkout master
$ git merge iss53
  • 이 때는 브랜치가 가리키는 커밋이 Merge할 브랜치의 조상이 아니므로 Fast forwardMerge 하지 않는다.
    ▶️ 각 브랜치가 가리키는 커밋 두 개와 공통 조상 하나를 사용하여 3-way Merge를 한다.

[커밋 3개를 Merge]

  • 단순히 브랜치 포인터를 최신 커밋으로 옮기는 게 아니라 Merge의 결과를 별도의 커밋으로 만들고 해당 브랜치가 그 커밋을 가리키도록 이동

[Merge 커밋]

  • iss53 브랜치를 masterMerge 하고 나면 더는 iss53 브랜치가 필요 없기 때문에 브랜치를 삭제하고 이슈 상태를 처리 완료로 표시

3.2.3 충돌의 기초

  • Merge하는 두 브랜치에서 같은 파일의 한 부분을 동시에 수정하고 Merge하면 Git은 해당 부분을 Merge 하지 못한다.

  • Merge 충돌이 일어났을 때 Git이 어떤 파일을 Merge할 수 없었는지 살펴보려면 git status 명령 이용
    ▶️ 충돌이 일어난 파일은 unmerged 상태로 표시

  • 충돌을 해결하려면 둘의 내용 중에서 고르거나 새로 작성하여 Merge 한다.

  • git mergetool이라는 Merge 도구로 충돌 해결 가능

  • Merge 도구를 종료하면 Git은 잘 Merge 했는지 물어본다.
    ▶️ 잘 마쳤다고 입력하면 자동으로 git add가 수행되고 해당 파일이 Staging Area에 저장
    ▶️ 잘 저장됐는지 확인했으면 git commit 으로 Merge 한 것을 커밋

3.3 브랜치 관리

  • 아무런 옵션 없이 git branch를 실행하면 브랜치의 목록을 보여준다.
$ git branch
iss53
* master
testing

▶️ * 기호가 붙어 있는 master 브랜치는 현재 Checkout 해서 작업하는 브랜치를 나타낸다.

  • git branch -v 를 실행하면 브랜치마다 마지막 커밋 메시지도 함께 보여준다.
$ git branch -v
iss53 93b412c fix javascript issue
* master 7a98805 Merge branch 'iss53'
testing 782fd34 add scott to the author list in the readmes
  • git branch --merged로 이미 Merge한 브랜치 목록을 확인한다.
$ git branch --merged
iss53
* master

▶️ * 기호가 붙어 있지 않은 브랜치는 git branch -d 로 삭제해도 되는 브랜치다.
▶️ 이미 다른 브랜치와 Merge 했기 때문에 삭제해도 정보를 잃지 않는다.

  • 현재 Checkout한 브랜치에 Merge 하지 않은 브랜치를 살펴보려면 git branch --no-merged 사용
$ git branch --no-merged
testing

▶️ 아직 Merge 하지 않은 커밋을 담고 있기 때문에 git branch -d로 삭제되지 않는다.

  • Merge 하지 않은 브랜치를 강제로 삭제하려면 -D 옵션으로 삭제

3.4 브랜치 워크플로

3.4.1 Long-Running 브랜치

  • 배포했거나 배포할 코드만 master 브랜치에 Merge 해서 안정 버전의 코드만 master 브랜치에 둔다.
  • 개발을 진행하고 안정화하는 브랜치는 developnext라는 이름으로 추가로 만들어 사용한다.
  • 토픽 브랜치(iss53 브랜치와 같은)에도 적용할 수 있는데, 해당 토픽을 처리하고 테스트해서 안정적이면 그때 Merge 한다.
  • 개발 브랜치는 공격적으로 히스토리를 만들어 나가고 안정 브랜치는 이미 만든 히스토리를 뒤따르며 나아간다.

[안정적인 브랜치일수록 커밋 히스토리가 뒤쳐짐]

[각 브랜치를 하나의 "실험실"로 생각]

  • 프로젝트 규모가 크면 proposed 혹은 pu(proposed updates)라는 이름의 브랜치를 만들고 nextmaster 브랜치에 아직 Merge 할 준비가 되지 않은 것을 일단 Merge 시킨다.
  • 규모가 크고 복잡한 프로젝트일수록 유용성이 빛난다.

3.4.2 토픽 브랜치

  • 어떤 한 가지 주제나 작업을 위해 만든 짧은 호흡의 브랜치
    ▶️ iss53 이나 hotfix 브랜치 같은 것

🎲 master 브랜치를 checkout 한 상태에서 어떤 작업을 한다고 가정

  • 한 이슈를 처리하기 위해서 iss91 브랜치를 만들고 해당 작업을 한다.
  • iss91v2 브랜치를 만들고 다른 방법을 시도해 본다.
  • 확신할 수 없는 아이디어를 적용해보기 위해 다시 master 브랜치로 되돌아가서 dumbidea 브랜치를 하나 더 만든다.

[토픽 브랜치가 많음]

🎲 iss91v2 브랜치가 괜찮아서 적용하기로 결정했다.
dumbidea 브랜치도 괜찮아 보인다.

  • iss91 브랜치는 버리고 두 브랜치를 Merge한다.

[dumbidea 와 iss91v2 브랜치를 Merge 하고 난 후의 모습]

3.5 리모트 브랜치

  • 리모트 트래킹 브랜치 : 리모트 브랜치를 추적하는 레퍼런스이며 브랜치
    ▶️ 로컬에 있지만 임의로 움직일 수 없다.
    ▶️ 리모트 서버에 연결할 때마다 리모트의 브랜치 업데이트 내용에 따라서 자동으로 갱신된다.
    ▶️ 리모트 저장소에 마지막으로 연결했던 순간에 브랜치가 무슨 커밋을 가리키고 있었는지를 나타낸다.

  • 리모트 트래킹 브랜치의 이름은 <리모트저장소명>/<브랜치이름> 형식

🎲 git.ourcompany.com 이라는 Git 서버가 있고 이 서버의 저장소를 하나 clone 하면 Git은 자동으로 origin 이라는 이름을 붙인다.

  • origin 으로부터 저장소 데이터를 모두 내려받고 master 브랜치를 가리키는 포인터를 만든다.
    ▶️ Git은 로컬의 master 브랜치가 origin/master 를 가리키게 한다.
    ▶️ 이제 이 master 브랜치에서 작업을 시작할 수 있다.

[Clone 이후 서버와 로컬의 master 브랜치]

  • 로컬 저장소에서 어떤 작업을 하고 있는데 동시에 다른 팀원이 git.ourcompany.com 서버에 push 하고 master 브랜치를 업데이트한다.
    ▶️ 그러면 이제 팀원 간의 히스토리는 서로 달라진다.
    ▶️ 서버 저장소로부터 어떤 데이터도 주고받지 않아서 origin/master 포인터는 그대로다.

[로컬과 서버의 커밋 히스토리는 독립적임]

  • 리모트 서버로부터 저장소 정보를 동기화하려면 git fetch origin 명령을 사용한다.
    ▶️ 명령을 실행하면 우선 origin 서버의 주소 정보(이 예에서는 git.ourcompany.com)를 찾아서, 현재 로컬의 저장소가 갖고 있지 않은 새로운 정보가 있으면 모두 내려받고, 받은 데이터를 로컬 저장소에 업데이트하고 나서, origin/master 포인터의 위치를 최신 커밋으로 이동시킨다.

[git fetch 명령은 리모트 브랜치 정보를 업데이트]

🎲 리모트 저장소를 여러 개 운영하는 상황을 이해할 수 있도록 개발용으로 사용할 Git 저장소를 팀 내부에 하나 추가해보자.

  • 이 저장소의 주소가 git.team1.ourcompany.com 이며 git remote add으로 현재 작업 중인 프로젝트에 팀의 저장소를 추가한다.
  • 이름을 teamone 으로 짓고 긴 서버 주소 대신 사용한다.

[서버를 리모트 저장소로 추가]

  • 서버를 추가하고 나면 git fetch teamone으로 teamone 서버의 데이터를 내려받는다.
    ▶️ 명령을 실행해도 teamone 서버의 데이터는 모두 origin 서버에도 있는 것들이라서 아무것도 내려받지 않는다.
    ▶️ 하지만, 이 명령은 teamone/master가 teamone 서버의 master 브랜치가 가리키는 커밋을 가리키게 한다.

[teamone/master의 리모트 트래킹 브랜치]

3.5.1 Push 하기

  • 로컬의 브랜치를 서버로 전송하려면 쓰기 권한이 있는 리모트 저장소에 push 해야 한다.
  • 리모트 저장소에 전송하지 않고 로컬 브랜치에만 두는 비공개 브랜치를 만들 수 있다.
  • 다른 사람과 협업하기 위해 토픽 브랜치만 전송할 수도 있다.

🎲 serverfix 라는 브랜치를 다른 사람과 공유할 때도 브랜치를 처음 push하는 것과 같은 방법으로 push

$ git push origin serverfix
Counting objects: 24, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (24/24), 1.91 KiB | 0 bytes/s, done.
Total 24 (delta 2), reused 0 (delta 0)
To https://github.com/schacon/simplegit
 * [new branch]      serverfix -> serverfix
  • 나중에 누군가 저장소를 fetch하고 나서 서버에 있는 serverfix 브랜치에 접근할 때 origin/serverfix라는 이름으로 접근할 수 있다.
$ git fetch origin
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://github.com/schacon/simplegit
 * [new branch]      serverfix    -> origin/serverfix
  • fetch 명령으로 리모트 트래킹 브랜치를 내려받는다고 해서 로컬 저장소에 수정할 수 있는 브랜치가 새로 생기는 것이 아니다.
    ▶️ serverfix 브랜치가 생기는 것이 아니라 수정 못 하는 origin/serverfix 브랜치 포인터가 생기는 것
  • 새로 받은 브랜치의 내용을 Merge하려면 git merge origin/serverfix 사용
  • Merge 하지 않고 리모트 트래킹 브랜치에서 시작하는 새 브랜치를 만들려면 아래와 같은 명령 사용
$ git checkout -b serverfix origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'

▶️ 그러면 origin/serverfix에서 시작하고 수정할 수 있는 serverfix 로컬 브랜치가 만들어진다.

3.5.2 브랜치 추적

  • 리모트 트래킹 브랜치를 로컬 브랜치로 Checkout 하면 자동으로 트래킹 브랜치가 만들어진다.
    ▶️ 트래킹 하는 대상 브랜치를 Upstream 브랜치라고 부른다.

  • 트래킹 브랜치에서 git pull를 하면 리모트 저장소로부터 데이터를 내려받아 연결된 리모트 브랜치와 자동으로 Merge 한다.

  • 서버로부터 저장소를 clone하면 Git은 자동으로 master 브랜치를 origin/master 브랜치의 트래킹 브랜치로 만든다.

  • 트래킹 브랜치를 직접 만들 수 있는데 리모트를 origin이 아닌 다른 리모트로 할 수 도 있고, 브랜치도 master가 아닌 다른 브랜치로 추적하게 할 수 있다.

  • git checkout -b <branch> <remote>/<branch>로 간단히 트래킹 브랜치를 만들 수 있다.
    ▶️ --track 옵션을 사용하여 로컬 브랜치 이름을 자동으로 생성할 수 있다.

$ git checkout --track origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'
  • 입력한 브랜치가 있는 A 리모트가 딱 하나 있고 B 로컬에는 없으면 Git은 트래킹 브랜치를 만들어 준다.
$ git checkout serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'
  • 리모트 브랜치와 다른 이름으로 브랜치를 만들려면 로컬 브랜치의 이름을 다르게 지정한다.
$ git checkout -b sf origin/serverfix
Branch sf set up to track remote branch serverfix from origin.
Switched to a new branch 'sf'
  • 이미 로컬에 존재하는 브랜치가 리모트의 특정 브랜치를 추적하게 하려면 git branch-u--set-upstream-to 옵션을 붙여서 설정
$ git branch -u origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
  • 추적 브랜치가 현재 어떻게 설정되어 있는 확인하려면 git branch -vv 사용
    ▶️ 로컬 브랜치 목록과 로컬 브랜치가 추적하고 있는 리모트 브랜치도 함께 보여준다.
    ▶️ 로컬 브랜치가 앞서가는지 뒤쳐지는지에 대한 내용도 보여준다.
$ git branch -vv
  iss53     7e424c3 [origin/iss53: ahead 2] forgot the brackets
  master    1ae2a45 [origin/master] deploying index fix
* serverfix f8674d9 [teamone/server-fix-good: ahead 3, behind 1] this should do it
  testing   5ea463a trying something new
  • 중요한 점은 명령을 실행했을 때 나타는 결과는 모두 마지막으로 서버에서 데이터를 가져온(fetch) 시점을 바탕으로 계산한다는 점이다.

3.5.3 Pull 하기

  • git pull 은 대부분 git fetch 를 실행하고 나서 자동으로 git merge 를 수행하는 것 뿐이다.
    ▶️ 보통 fetchmerge 로 명시적으로 사용하는 것이 좋다.

3.5.4 리모트 브랜치 삭제

  • git push --delete를 사용하여 리모트 브랜치를 삭제
$ git push origin --delete serverfix
To https://github.com/schacon/simplegit
 - [deleted]         serverfix
  • 서버에서 가비지 컬렉터가 동작하지 않는 한 데이터는 사라지지 않기 때문에 종종 의도치 않게 삭제한 경우에도 커밋한 데이터를 살릴 수 있다.

3.6 Rebase 하기

3.6.1 Rebase의 기초

[두 개의 브랜치로 나누어진 커밋 히스토리]

  • 두 브랜치를 합치는 가장 쉬운 방법은 merge를 사용하는 것이지만 다른 방식으로 rebase가 있다.
    ▶️ C3의 변경사항을 Patch로 만들고 이를 다시 C4에 적용시킨다.
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
  • 일단 두 브랜치가 나뉘기 전인 공통 커밋으로 이동하고 나서 그 커밋부터 지금 Checkout 한 브랜치가 가리키는 커밋까지 diff를 차례로 만들어 어딘가에 임시로 저장해 놓는다.
    ▶️ Rebase할 브랜치가 합칠 브랜치가 가리키는 커밋을 가리키게 하고 아까 저장해 놓았던 변경사항을 차례대로 적용

[C4의 변경사항을 C3에 적용하는 Rebase 과정]

  • 그리고 나서 master 브랜치를 Fast-forward 시킨다.
$ git checkout master
$ git merge experiment

[master 브랜치를 Fast-forward시키기]

  • MergeRebase 둘 다 합치는 관점에서는 서로 다를게 없지만 Rebase가 좀 더 깨끗한 히스토리를 만든다.
    ▶️ 보통 리모트 브랜치에 커밋을 깔끔하게 적용하고 싶을 때 사용
    ▶️ 이렇게 Rebase하는 리모트 브랜치는 직접 관리하는 것이 아니라 그냥 참여하는 브랜치일 것이다.

3.6.2 Rebase 활용

🎲 다른 토픽 브랜치에서 갈라져 나온 토픽 브랜치 같은 히스토리가 있다고 가정

  • server 브랜치를 만들어서 서버 기능을 추가하고 그 브랜치에서 다시 client 브랜치를 만들어 클라이언트 기능을 추가
  • 마지막으로 server 브랜치로 돌아가서 기능을 더 추가

[다른 토픽 브랜치에서 갈라져 나온 토픽 브랜치]

🎲 이 때 테스트가 덜 된 server 브랜치는 그대로 두고 client 브랜치만 master로 합치려는 상황

$ git rebase --onto master server client
  • master 브랜치로부터 server 브랜치와 client 브랜치의 공통 조상까지의 커밋을 client 브랜치에서 없애고 싶을 때 사용
    ▶️ client 브랜치에서만 변경된 패치를 만들어 master 브랜치에서 client 브랜치를 기반으로 새로 만들어 적용

[다른 토픽 브랜치에서 갈라져 나온 토픽 브랜치를 Rebase 하기]

  • 이제 master 브랜치로 돌아가서 Fast-forward 시킬 수 있다.
$ git checkout master
$ git merge client

[master 브랜치를 client 브랜치 위치로 진행 시키기]

  • server 브랜치의 일이 다 끝나면 git rebase <basebranch> <topicbranch>로 checkout 하지 않고 바로 server 브랜치를 master 브랜치로 Rebase 할 수 있다.
$ git rebase master server

[master 브랜치에 server 브랜치의 수정 사항을 적용]

  • 그리고 나서 master 브랜치를 Fast-forward 시킨다.
$ git checkout master
$ git merge server
  • 모든 것이 master 브랜치에 통합됐기 때문에 더 필요하지 않으면 client 나 server 브랜치는 삭제해도 된다.
    ▶️ 브랜치를 삭제해도 커밋 히스토리는 여전히 남아있다.
$ git branch -d client
$ git branch -d server

[최종 커밋 히스토리]

3.6.3 Rebase의 위험성

3.6.3.1 이미 공개 저장소에 Push 한 커밋을 Rebase 하지 마라

🎲 새 커밋을 서버에 Push 하고 동료 중 누군가가 그 커밋을 Pull 해서 작업을 한다고 가정

  • 그런데 그 커밋을 git rebase로 바꿔서 Push 해버리면 동료가 다시 Push 했을 때는 동료는 다시 Merge 해야 한다.
  • 그리고 동료가 다시 Merge한 내용을 Pull하면 내 코드는 엉망이 된다.

[저장소를 Clone 하고 일부 수정함]

  • 이제 팀원 중 누군가 커밋, Merge 하고 나서 서버에서 Push
    ▶️ 이 리모트 브랜치를 Fetch, Merge 한다.

[Fetch 한 후 Merge 함]

  • 그런데 Push했던 팀원은 Merge 한 일을 되돌리고 다시 Rebase한다.
    ▶️ 이후 저장소에서 Fetch하고 나면 아래와 같은 상태가 된다.

[한 팀원이 다른 팀원이 의존하는 커밋을 없애고 Rebase 한 커밋을 다시 Push 함]

  • 망해간다.
  • git pull로 서버의 내용을 가져와서 Merge하면 같은 내용의 수정사항을 포함한 Merge 커밋이 아래와 같이 만들어진다.

[같은 Merge를 다시 한다]

  • git log로 히스토리를 확인해보면 같은 커밋이 두 개 있다. (C4, C4')
  • C4 와 C6은 포함되지 말았어야 할 커밋이다.
    ▶️ 애초에 서버로 데이터를 보내기 전에 Rebase로 커밋을 정리했어야 했다.

3.6.4 Rebase 한 것을 다시 Rebase 하기

🎲 어떤 팀원이 강제로 내가 한 일을 덮어썼다고 가정

[한 팀원이 다른 팀원이 의존하는 커밋을 없애고 Rebase 한 커밋을 다시 Push 함]

  • 이 상황에서 Merge 하는 대신 git rebase teamone/master를 실행하면 Git은 아래와 같은 작업을 한다.
    • 현재 브랜치에만 포함된 커밋을 결정
      (C2, C3, C4, C6, C7)
    • Merge 커밋이 아닌 것을 결정
      (C2, C3, C4)
    • 이 중 Merge할 브랜치에 덮어쓰이지 않은 커밋 결정
      (C2, C3, C4는 C4'와 동일한 Patch)
    • 결정한 커밋을 teamone/master 브랜치에 적용

[강제로 덮어쓴 브랜치에 Rebase 하기]

  • 동료가 생성했던 C4와 C4' 커밋 내용이 완전히 같은 때만 이렇게 동작된다.
    ▶️ 커밋 내용이 아예 다르거나 비슷하다면 커밋이 두개 생긴다.

  • git pull --rebaseRebase 할 수도 있다.

  • Push 하기 전에 정리하려고 Rebase 하는 것과 절대 공개하지 않고 혼자 Rebase 하는 경우도 괜찮다.
    ▶️ 하지만, 이미 공개하여 사람들이 사용하는 커밋을 Rebase하면 틀림없이 문제가 생긴다.

3.6.5 Rebase vs. Merge

  • 로컬 브랜치에서 작업할 때는 히스토리를 정리하기 위해서 Rebase 할 수도 있지만, 리모트 등 어딘가에 Push로 내보낸 커밋에 대해서는 절대 Rebase 하지 말아야 한다.

참조 :
https://git-scm.com/book/ko/v2

profile
상상을 현실로 만들기 위해 노력하는 개발자

0개의 댓글