5. I suppose that you know …
커밋을 어떻게 하는지, staging은 어떻게 하는지, 브랜치를 어
떻게 만드는지, 나눠진 브랜치를 어떻게 병합하는지, 원격 브랜
치와 로컬 브랜치가 어떻게 진행 되는지, 코드가 어떻게/언제
충돌 나는지, 충돌난 코드를 어떻게 해결하는지, 남들이랑 작업
하면서 프로젝트가 어떻게 망가지는지, Pull-Request가 뭔지,
Pull-Request를 어떻게 하는지, 등등…
다른 사람이랑 git으로 프로젝트를 해봤다고 가정합니다.
12. ^ & ~
• 부모 커밋을 가리키는 연산자
• ^ (caret) : 한 세대 앞을 가리키는 연산
• HEAD^, HEAD^^, HEAD^^^ …
• ~n (tilde) : n 세대 앞을 가리키는 연산
• HEAD~1, HEAD~2, HEAD~3 …
13. ^ & ~
B DCA HEAD
HEAD~1,
HEAD^
HEAD~2,
HEAD^^
HEAD~3,
HEAD^^^
14. ^ & ~
B D
E
CA HEAD
HEAD~1,
HEAD^
HEAD~2,
HEAD~1^1
HEAD~3
HEAD~2,
HEAD~1^2
15. A commit must be …
• 적절한 크기의 하나의 작업만 해야 합니다
잠수함 패치 하면 안됨
• 컴파일 가능해야 합니다 ,빌드 가능해야 합니다
커밋 하나가 완벽해야 합니다
• 커밋 메시지가 잘 쓰여져 있어야 합니다.
생판 모르는 사람이(미래의 내가) 봐도 이해 가능해야
42. Has upstream?
만약 chanwoo/feature의 upstream이 존재한다면?
그냥 push 했을경우 reject당함 (충돌나니까)
현재 이력을 강제로 올려야 합니다.
$ git push --force
# Be very careful with this command!
43. Before Rebasing
A B C D
E F
master
chanwoo
/feature
origin
/chanwoo
/feature
44. After Rebasing
A B C D
E F
master
chanwoo
/featureE’ F’
origin
/chanwoo
/feature
45. git push --force
A B C D
E F
master
chanwoo
/featureE’ F’
origin
/chanwoo
/feature
46. Merge vs. Rebase
• Merge
• 장점
• 이전 커밋들을 들쑤지진 않음, (이전 커밋들이)잘못될 가능성이 없음.
• 단점
• 다른 브랜치들이 매우 활발하다면 그 때문에 머지 커밋이 많이 발생할 수 있고,
때문에 커밋 로그들이 지저분해 보일 수 있음.
47. Merge vs. Rebase
• Rebase
• 장점
• 프로젝트 히스토리가 훨씬 깔끔해질 수 있음.
• 선형적인 커밋 구조를 만들 수 있음.
• 단점
• 이전 커밋들을 들쑤시고 다니기 때문에, 충돌이 발생하면 merge commit을 만
드는 방법보다 해결이 귀찮음.
• 원격의, 남이 건드릴 수 있는 브랜치(public branch)는 절대 리베이스 하면 안됨.
51. B != B’
B와 B’는 다른 커밋
chanwoo/feature에서 master로 rebase 하면 B도 쌓아 올린다.
만약 B같은 커밋이 여러개라면?
다른 개발자들도 B에서 브랜치를 쳤다면?
C, D에서도 다른 개발자들이 브랜치를 쳤다면?
이런 상황을 전문용어로 개판이라고 한다.
원격의, 남이 건드릴 수 있는 브랜치는 절대 리베이스 하면 안됨!
52. Discuss with your colleagues
Merge를 해도 되고 Rebase를 해도 됩니다.
어떤 방법으로 커밋들을 병합할지는
팀원들과 상의하세요
(아니면 PM이 까라는대로 까야지)
53. Conflict!
• $ git rebase --continue
• $ git rebase --skip
• $ git rebase --abort
아직 rebase가 끝난게 아닙니다.
55. git rebase --continue
A B C D
E F
master
chanwoo
/feature
E’ F’--continue
Resolve하고 난 후,
--continue
56. git rebase --skip
• 충돌난 상황에서, 현재 커밋을 쌓아올리길 포기하고 건너뜁니다.
• 잘 안씀, 커밋 하나하나가 중요하다면.
• 종종 resolve 다 하고 보니 git add할게 없을때 사용함.
• sourcetree의 ‘resolve with theirs/mine’만 사용할때 발생할 수 있음.
57. git rebase --abort
• 현재 rebase 세션을 포기하고 아예 없던일로 합니다.
쌓아올리는 커밋을 하나하나 resolve 하며 --continue 한 두번 하다가 나
중에 화나서 --abort할때 주로 사용함.
58. git reset ORIG_HEAD
rebase를 성공적으로 마치고 난 직후 ,
‘아 이 리베이스 하면 안됐는데’ 라는 생각이 들때 사용함
HEAD를 rebase 직전의 커밋을 가리키게 해 준다.
뭐 이것저것 만졌으면 reflog 찾아봐야지..
68. git rebase -i
• Interactive
• 리베이스 세션을 열고, 어떻게 할지 다시 물어봅니다
• 뭘 어떻게?
• 원래 순서대로 할건지, 이 커밋을 사용할건지, 말건지, 직전 커밋과
합칠지, 커밋메세지를 새로 작성할지…. 등등
69. git rebase –i HEAD~5
A B C D
E F
chanwoo
/featureG H
70. git rebase –i HEAD~5
pick 781915a I’m hungry (D)
pick 23158ce I want to sleep (E)
pick c94880e I want to be fixed up (F)
pick d173cc0 Hello! (G)
pick daf497f I’ll change this commit log later (H)
71. pick, reword, squash and fixup
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
72. git rebase –i HEAD~5
pick 781915a I’m hungry (D)
squash 23158ce I want to sleep (E)
fixup c94880e I want to be fixed up (F)
reword daf497f I’ll change this commit log later (H)
pick d173cc0 Hello! (G)
74. Squash E
# This is a combination of 2 commits.
# The first commit's message is:
I’m hungry
# This is the 2nd commit message:
I want to sleep
# Please enter the commit message for your changes. Lines starting # with '#' w
ill be ignored, and an empty message aborts the commit.
# Explicit paths specified without -i nor -o; assuming --only paths... # Not cu
rrently on any branch. # Changes to be committed:
A B C D+E
I’m hungry and I wanna sleep
75. Fixup F
A B C D+E+F
I’m hungry and I wanna sleep
86. Somebody: ?!
somebody가 작업한 C,D가 auto mergeable이면 괜찮은데,
E,F때문에 conflict가 떠서 자동 머지가 불가능 하다면?
somebody 입장에서는 갑자기 다른놈이 훼방
하지만 먼저 PR날리고 merge된 놈이 승자.
이 때 somebody가 할 수 있는 행동은?
89. Rules
unit test는 사전에 미리 통과가 됐었어야 push를 할 수 있다
push 된 커밋들은 Travis 빌드 테스트를 통과해야 pull-request를 보낼 수 있다
pull-request가 별 다른 문제점이 없다면 merge 되지만
커밋로그가 불충분 하거나, 풀리퀘 로그가 불충분 하거나,
하나의 커밋에 너무 많은 일이 들어가 있거나,
너무 적은 일이 들어가 있거나,
쓸모 없는 기능으로 판단된다면?
REJECT
92. Fix problems, Send PR again.
A B
E F
master
chanwoo
/feature
chanwoo/
feature-v2F’
93.
94.
95. Summarize
• 하나의 커밋은
• 적당한 크기, 연관성 있는 변경점, 상세한 커밋 메세지
• 여러개의 커밋은
• 삽질은 커밋 로그에, 하나의 기능은 하나의 커밋으로 줄인다
• --amend, rebase -i
• 브랜치는
• master는 건들지 말고, 기능별로 따 와서, Pull-Request
102. END
누구나 쉽게 이해할 수 있는 git입문 – 브랜치 전환 :
https://backlogtool.com/git-guide/kr/stepup/stepup1_3.html
Atlassian Tutorial – merging vs. rebasing :
https://www.atlassian.com/git/tutorials/merging-vs-rebasing/
Git 브랜치 rebase하기 :
https://git-scm.com/book/ko/v1/Git-%EB%B8%8C%EB%9E%9C%EC%B9%98-Rebase%ED%95%98%EA%B8%B0
Git 도구 히스토리 단장하기 : https://git-scm.com/book/ko/v1/Git-%EB%8F%84%EA%B5%AC-%ED%9E%88%EC
%8A%A4%ED%86%A0%EB%A6%AC-%EB%8B%A8%EC%9E%A5%ED%95%98%EA%B8%B0
소마 1차 프로젝트 : https://github.com/swmaestro06-apus/apus
Notas del editor
우리는 커밋을 병합하고, 뜯어 고치는법을 배울겁니다.
다른 사람이랑 git으로 프로젝트를 관리 해봤으면서 해볼만한건 다 해봤다고 생각할께요.
얘는 kermit
어떤 커밋도 sha id만 알고 있으면 참조 가능합니다.
보통 feature마다 브랜치를 따서 작업하고, 이를 master에 쌓아 올리는 방법으로 작업을 합니다.
뭐 이렇게 복잡하게는 사실 잘 안 써봤네요. 이런건 그냥 알아두고 쓸때마다 찾아보시길 바랍니다.
어떤 커밋을 추가 했을 때 연관된 기능의 코드만 추가되고, 어떤 커밋을 삭제했을때, 연관된 기능의 코드만 삭제되어야 합니다.
Git관련 내용은 아니지만, 곁다리로 말씀드리자면,
어떤 기능을 구현했을 때, 기존에 있던 다른 기능이 틀어지는 일을 막을 수 있음.
이것도 곁다리입니다. CI를 쓰면 빌드가 안되는 코드가 올라갈 수 없죠.
태그를 안 붙이는 곳도 많음. 50/72 스타일을 쓰기도 함. 하지만 타이틀만 지키고 본문은 그냥 쓰기로 했다.
그리고 머릿말만 쓰는게 아니라 본문도 쓰셔야 합니다.
예시
예시
우선 머지하는 방법중의 하나로, 머지에 대해서 또 간단하게 짚고 넢어갑시다.
병합하는 기본적인 명령어 (이름도 merge)
근데 단점으로 merge commit을 만든다. 안 만들 수도 있음
변경 사항이 ‘있으면’ 머지 커밋이 새로 생김 ( 대다수가 생김 )
D가 가지지 못한 F의 변경점을 메꾸는 커밋
충돌이 나면 여기서 해결합니다.
D가 E,F의 변경점을 가지고 있지 않았기 때문에 머지 커밋이 생김.
암튼, 이렇게 머지 커밋이 생길 수도 있고 안 생길 수도 있습니다.
많이 만들지 않는 것이 좋습니다. 왜?
이유는 간단해요. 머지 커밋이 보기 싫으니까.
어디서 보기 싫으니까? 커밋 로그에서 보기 싫으니까.
극단적인 예를 들어볼께요
‘나는 저기 저 커밋에 있는 기능이 필요해’ 하면서 가져다 쓰면 이렇게 엉망이 됩니다.
마스터 입장에서 마지막에 있는 저 M 커밋을 pull-request를 날리면 과연 받아주고 싶을까요
여러분이 풀 리퀘스트를 받았는데 변경내역이 이런식이면 별로 마음에 들진 않겠죠
선형적인 구조를 유지할 수 있습니다. master는 FFWD 머지로 쉽게 이동 가능
어느 커밋에서 갈라져 나왔는지, 공통 조상을 찾습니다.
어떤 커밋들을 리베이스 해야하는지 결정합니다.
그리고 리베이스 세션을 시작합니다.
E의 변경점을 가지고 E’를, F의 변경점을 기반으로 F’를 쌓아올린다.
마지막으로 브랜치 레퍼런스를 옮기면 세션 종료
E’와 F’를 다시 만든다는점. 그리고 E와 F는 저장소에서 사라지지 않는다
Upstream브랜치는 원격 브랜치를 말합니다.
로컬 브랜치에서 어떤 커밋들을 따라가면 f 가 나와야 하는데, 그런게 없기 때문에 이 상태라면 리젝당합니다.
강제로 푸쉬
선형적인 구조일때, 커밋을 거꾸로 타고 올라가는 경우 편리
아까와는 달라요, 마스터를 리베이스 할껍니다. feat브랜치가 아니라
이 상태에서 feature브랜치와 master 를 머지를 하던 리베이스를 하던 합친다고 해봅시다
B와 B’는 다른 커밋이죠, 그래서 공통 조상이 A로 잡힙니다. 이러면 chanwoo/feature에서 master를 rebase 하려면 B, E, F 부터 쌓아 올려야 하겠죠, 그럼 같은 내용의 커밋 B가 여러번 생기게 됩니다. 히스토리가 더 개판이 되겠죠. 근데 만약에 B같은 커밋이 하나가 아니라 여러개라면? Chanwoo만 브랜치 따서 작업하는게 아니라 다른 사람도 브랜치 따서 작업하던거라면? C나 D 커밋에서 브랜치 따서 작업하던 사람들이 또 있다면? 그 사람들이 rebase할때마다 똑같은 일을 하는 커밋들이 (커밋로그도 똑같음) 늘어나는겁니다.
이것만 조심하시면 됩니다.
잘 상의해서 어떤 방식을 쓸지 합의하시길 바랍니다.
충돌 없이 잘 되는 경우만 살펴봤죠. 하지만 아까 머지 커밋을 만들때 말했던 것 처럼 리베이스 할때도 서로 다른 코드들이 합쳐지면서 마찬가지로 충돌이 일어날 수 있습니다. 이런경우 어떻게 해결할까요?
리베이스 세션이 열려있을때만 사용할 수 있는 명령어가 몇개 있습니다.
충돌이 일어난다고해서 리베이스가 바로 취소되지는 않고 사용자에게 무슨 짓을 할지를 물어봅니다.
해결하고 리베이스를 계속 진행할껀지
그냥 해당 커밋만 넘겨 버릴지
아니면 아예 포기할지
F를 쌓아 올리지 않고, 리베이스 세션을 잠깐 멈추고 유저에게 뭘 어떻게 할지 물어봅니다.
평소에는 resolve하고 그냥 머지커밋을 만들었다면, 여기선 그냥 rebase를 계속 하면 됩니다.
리베이스를 하고 나면 브랜치 레퍼런스가 가리키는 커밋 대상이 바뀌게 되죠.
커밋을 수정하는 방법에 대해서 알아보죠
어떤 한 기능을 구현하기 위해서 이래저래 삽질을 합니다.
이래저래 삽질을 하면 커밋로그가 정신없어 집니다.
여러개의 커밋을 하나로 묶으면 보기 편합니다. 오픈 소스들 같은데 PR 날리다 보면, 커밋들 정리해서 다시 보내라고 하는 경우가 많이 있는데, (커밋이 많다거나, 하나의 커밋이 너무 많은 일을 하거나, 너무 자잘한 커밋이거나) 이럴때 커밋들을 적당히 묶어서 보내야 합니다.
묶을때 또 너무 하나의 커밋이 너무 커지게 묶으면 안되겠죠
묶는 방법 두개를 소개해 드릴겁니다.
이번껀 간단하니까 예시로 보여드리죠,
제가 chanwoo/test라는 브랜치를 만들어서 몇가지 쓸모없는 헤더파일을 지우는 커밋을 만들었다고 쳐요,
여기 보면 diff에 빨간색으로 몇개 지워져 있는게 보이죠?
커밋하고 나서 보니, 수정해야할 내역이 남아 있는데 깜빡하고 이미 커밋을 한 상태입니다
이럴땐 마지막 커밋에 추가할 내용을 stage 시킵니다.
--amend 옵션을 붙여서 커밋을 합니다. 커밋 메세지를 수정하라고 vi가 한번 나옵니다. 수정할꺼 수정하시고 저장하고 vi를 종료 하시면 됩니다.
이렇게 커밋을 새로 만들지 않고도 마지막 커밋의 내용을 수정해서 하나로 몰아넣을 수 있습니다.
그냥 마스터를 리베이스 할 때도, -I 옵션을 붙여서 명령을 내릴 수 있지만, 보통 그렇게는 잘 안쓰고, 커밋 이력을 간소화 하는데 저는 자주 썼습니다.
VI창이 열리면서 이렇게 우리한테 물어봅니다. 어떻게 할지.
여기서 이제 이 커밋들을 어떻게 할지 여러분 마음대로 할 수 있습니다. 순서도 바꿀 수 있고요, 충돌이 나지 않는 선에서. 커밋을 하나하나 제대로 나눠서 했다면 충돌날 일이 적겠죠?
그 VI 창 아래를 보시면 아래처럼 설명이 다 나와 있어요
squash, fixup을 설명할때, 병합되길 원하는 커밋 아래로 로그를 옮길 수 있습니다.
이 상태에서 저장하고 종료를 하면, 이 명령대로 리베이스가 진행이 됩니다.
그냥 평소처럼 D (정확히는 D’) 를 쌓아 올립니다.
두가지 커밋을 squash로 하나로 합쳤으니 에디터가 열리며 커밋 메세지를 수정할 기회를 줍니다.
F는 커밋메세지를 수정 단계를 거치지 않고 바로 직전 커밋과 합쳐집니다.
H커밋에 대한 커밋 메세지 수정 기회가 생깁니다.
그리고 마지막으로 G 커밋을 쌓아 올립니다.
브랜치 레퍼런스를 옮기고, 리베이스 세션을 닫습니다.
제가 1차 프로젝트때 썼던 브랜치 관리 전략을 알려드리겠습니다.
이거 하나만 알아두세요 master브랜치는 성역입니다. 그렇기 때문에 절대로 master에 바로 커밋하면 안됩니다. merge나, rebase같이 간접적으로 커밋해야 합니다
모두가 같은 저장소에서 작업했기 때문에 이름을 앞에 붙이고 슬래쉬로 구분했습니다. 그리고 뒤에 기능 이름을 붙였죠.
이렇게 소스트리에서는 사람별로 모아서 볼 수 있었습니다.
그리고 저희는 풀리퀘를 날리는 방식으로 커밋들을 진행시켰습니다.
풀리퀘는 머지 커밋이 생깁니다. 이 머지 커밋은 풀리퀘를 받았다는 의미로 기록에서 없애지 않습니다.
somebody 입장에서는 master에서 잘 작업중이었는데 엄한 놈이 와서 내 작업물들을 충돌나게 만들었죠
이렇게 G기반으로 C,D를 쌓아올리고 나서, 리베이스 하고 나서, 작업을 계속 할 수 밖에 없습니다.
시퍼런 색이 마스터 브랜치 입니다.
마스터에서 기능별로 브랜치를 따서 작업을 시작합니다.
몇몇 브랜치는 도태되고, 하나의 브랜치만 살아 남아, master에 머지 됩니다.
그 사이클을 계속 반복해 나가면서 개발이 진행되게 됩니다.
때로는 많은 기능들을 물리치고 자잘한 커밋이 먼저 머지 될 수 있습니다.
f 커밋에만 문제가 있었다고 봅시다.
끝에 –v2를 붙여서 두번째 시도 브랜치임을 알립니다.
풀리퀘로그도 잘 써야합니다
CI통과
생각없이 이상한 브랜치에 커밋 하나 잘못했을때 쓸 수 있습니다.
아 물론 커밋을 옮기는 거니까 충돌이 일어날 수 있습니다. 알아서 해결 하시면 됩니다.