Github, Git 기본 <Pull Request, pull, 충돌 해결>
Github, Git 기본 <Pull Request, pull, 충돌 해결>
7. 내 코드 팀 레포지토리에 합치기, Pull Request

커밋이 아주 잘 올라왔다
이것을 이제 팀 레포지토리에 합쳐보자

우상단의 New pull request를 누른다
브랜치를 원본 레포지토리의 main에 PR

만약 협업을 할 때 한 레포지토리에서 분기 (branch) 를 나누어 작업했다면,
base에는 내 브랜치를 합치려는 팀 레포지토리 브랜치를 선택한다 (예시:main)compare에는 내 브랜치를 선택한다 (예시:feat/test)
포크 레포지토리를 원본 레포지토리에 PR

만약 브랜치로 나눈 것이 아니라, Fork를 했다면 상단의 compare across forks를 누른다

그러면 출력되는 브랜치의 종류가 살짝 달라지게 된다
base repository에는 내 커밋을 합치려는 목적지 레포지토리를 선택한다 (예시: 내가 Fork한 원본 레포)head repository에는 내 Fork 레포지토리를 선택한다 (깃허브 계정명으로 찾으면 된다)base에는 목적지 레포지토리의 브랜치를 선택한다compare에는 내 레포지토리의 브랜치를 선택한다
풀 리퀘스트 생성

브랜치를 선택하면 내 브랜치의 커밋들과, 최하단에는 변경점이 표시된다
Create pull request를 누르자

상단에 앞서 지정한 브랜치들과, 자동 Merge 여부가 출력된다
자동으로 Merge할 수 있다면 초록색으로, 자동으로 Merge할 수 없으면 (충돌 발생) 빨간색으로 출력된다
위 이미지는 지금 충돌이 발생한 상태라 해결해주어야 한다
하단은 두 섹션으로 분리되어 있다
- 왼쪽 섹션에서는 풀 리퀘스트의 제목과 간략한 설명을 적을 수 있다
- 보통은 어떤 작업을 했고, 어느 부분이 개선되었는지 이미지와 함께 작성하는 편이다
- 꼼꼼하게 적으면 다른 사람들이 리뷰하기 쉽다
- 오른쪽 섹션은 이 풀 리퀘스트의 속성을 지정할 수 있다
Reviewers에서는 이 풀 리퀘스트를 리뷰해줬으면 하는 사람을 지정할 수 있다 (레포지토리에 소속된 사람만 가능)Assignees는 이 작업을 한 사람을 지정할 수 있다Labels는 태그같은 개념으로, PR의 특성을 지정할 수 있다Projects는 사전에 설정해놓은 칸반보드 등에 연동할 수 있는 기능이다Milestone은 사전에 해당 레포지토리에 설정한 투두리스트 같은 것을 연동할 수 있다Development는 이슈와 풀리퀘스트를 연결하여 풀리퀘스트가 해결되면 자동으로 닫히게 등 설정할 수 있다
다 작성했다면 Create pull request를 누른다

풀 리퀘스트가 잘 생성이 되었다
이제 다른 사람들이 내 PR을 보고 코드의 개선안을 제시하거나 오류를 지적하는 리뷰를 올릴 수 있다
draft pull request

아직 코드가 다 완성되진 않았지만 풀 리퀘스트를 미리 올리고 싶을 때 사용한다
draft PR은 merge할 수 없다
8. 원격 레포지토리 내용 내려받기, git pull (fetch & merge)

$> git pull origin main
원격 레포지토리 (origin) 의 main으로부터 내용을 내려받아 현재 로컬 브랜치에 합친다
만약 git clone을 통해 깃허브에서 복제한 레포지토리라면 인자를 넣지 않고 git pull만으로도 잘 동작할 것이다 (원래는 인자를 넣어주는 것이 맞다)
하지만 git init을 통해 로컬에서 레포지토리를 만들고 원격으로 올렸다면 이러한 설정이 잘 안 되어 있을 것이다
$> git push -u origin main
git push할 때 -u 옵션을 통해 기본 경로를 지정해주면 앞으로 git pull만으로도 잘 받아질 것이다
git fetch
$> git fetch origin main
원격 저장소에서 변경점을 가져오기만 하고, 합치지는 않는 명령어
원격 저장소의 변경점을 새로운 브랜치로 가져오기만 하며, 이 브랜치는 FETCH_HEAD 라는 이름으로 접근할 수 있다
원격 저장소의 변경점을 내 브랜치에 합치고 싶다면 FETCH_HEAD를 내 브랜치에 merge하면 된다
git merge
$> git merge [브랜치명]
현재 checkout된 브랜치 (현재 브랜치) 에 인자로 받은 브랜치를 merge한다
두 브랜치의 커밋 이력을 한 브랜치로 통합한다고 생각하면 된다

git merge를 진행하다 보면 Fast-forward라는 문구를 볼 수 있는데, 이는 현재 브랜치 (인자로 받은 브랜치를 merge하려는 브랜치) 가 분기 이후 변경점이 없어 단순히 맨 뒤에 다른 브랜치의 커밋을 붙이기만 해도 merge가 되는 상황을 의미한다

Fast-forward가 아닌 다른 merge를 관찰하기 위해 main, test/testbranch 두 브랜치가 서로 다른 커밋 이력을 갖고 있도록 서로 다른 파일을 추가해 보았다
(충돌은 나지 않도록 했다)

Fast-forward 대신 ‘recursive’ 전략을 사용해서 머지했다고 나온다
두 브랜치가 분기 이후로 독자 노선을 걷고 있기 때문에 브랜치의 맨 끝에 최신 커밋을 붙이기만 한다고 해결되지 않기 때문이다
recursive strategy는 git은 main과 test/testbranch의 분기 지점 (공통 조상) 을 찾아내고, 3-way merge 방식을 사용하여 두 브랜치를 합친다
3-way merge는
- merge 받으려는 브랜치 (
main) 의 가장 최신 커밋 - merge하려는 브랜치 (
test/testbranch)의 가장 최신 커밋 - 분기 지점 커밋
의 3개 커밋을 이용한다
분기 지점 커밋을 이용하여 main과 test/testbranch의 커밋 중 어느 쪽에서 파일에 변경점이 발생했는지 알아내고, 이를 이용하여 새로운 커밋 (Merge commit) 을 만들어 merge를 진행한다
8. 충돌 해결하기

충돌이 발생하면 Merge pull request 버튼이 비활성화된다
충돌을 해결하는 방법은 크게 두 가지가 있다
Github 페이지에서 바로 해결하기

conflict가 났다고 알려주는 박스의 우상단 Resolve conflicts 버튼이 활성화되어야지만 사용할 수 있다
파일의 수가 너무 많거나 변경점이 복잡하면 비활성화되기 때문에 이 방법으로 고칠 수 없다

충돌 표식을 보여준다
<<<<< 로 되어있는 칸 안의 내용이 머지하려는 브랜치의 내용이고,, >>>>> 로 표시된 칸 안에 있는 내용이 머지하고자 하는 기준 브랜치의 내용이다
유지하고자 하는 코드를 남겨두고, 지우려는 코드를 지워주자

Mark as resolved를 누르면 해당 충돌이 해결되었음을 표시할 수 있다

모든 파일이 Mark as resolved 되면 Commit merge 버튼이 활성화된다
로컬에서 해결하고 올리기 - Fork한 레포지토리

$> git remote add upstream [원본 레포지토리 링크]
만약에 레포지토리가 fork한 레포라면 upstream 리모트를 설정해주어야 한다

$> git pull upstream [upstream으로부터 가져올 브랜치]
upstream의 내용을 현재 브랜치에 pull 받아보자
git pull은 git fetch와 git merge를 한꺼번에 사용하는 명령어이다
git fetch는 변경사항을 가져오기만 하지만, git merge는 가져온 변경사항을 현재 브랜치에 Merge하려고 시도하기 때문에 충돌이 발생할 수 있다
위의 사진을 보면 벌써 충돌이 났다

git status를 실행해 보면 어느 커밋, 어느 파일에서 충돌이 발생했는지 보여준다
both modified인 것으로 보아 양 브랜치에서 동시에 같은 파일을 작업해서 발생한 충돌이다
충돌이 발생한 파일을 열어보자

내가 작성한 적 없는 <<<, >>> 표식이 생겨있다
이는 충돌 병합 표식으로, 어느 부분을 너의 브랜치는 이렇게 수정했고 머지하려는 브랜치는 이렇게 수정했으니 너가 합쳐봐라 라는 뜻이다
<<<<표식은 가져와서 머지하려는 브랜치====표식은 수정점을 구분하는 구분자>>>>표식은 머지받으려는 브랜치의 내용이다
양 브랜치에서 가져올 내용은 가져오고, 삭제할 내용은 삭제한 뒤 표식을 전부 지워주면 된다

수정사항을 add 후 commit하자

$> git rebase --continue
충돌을 해결할 때는 자동으로 rebase 상태가 되므로, git push를 할 수 없다!
git push 대신 git rebase -—continue 를 통해 다음 merge 단계로 넘어가자
간단하게 설명하자면 git rebase 도중 다음 단계 (다음 커밋) 으로 넘어가기 위한 명령어라고 생각하면 된다
rebase에 대해서는 다른 포스팅에서 더 자세히 정리하려고 한다
충돌이 완전히 해결될 때까지 위의 과정을 반복하고, git rebase —-continue를 통해 다음 커밋으로 넘어가자

모든 충돌 merge 작업이 끝나면 위와 같은 문구가 표시된다

rebase 과정은 기존의 커밋을 이용해서 아예 새로운 커밋을 만드는 것이기도 하고, 충돌을 합치는 과정에서 각 커밋에 다른 변화가 생겼기 때문에 원격 브랜치와 로컬 브랜치의 커밋 기록이 달라진다
당연하게 git push를 시도하면 빨간 글씨로 경고가 뜨기 때문에 당황스러울 수 있는데, 침착하게 -f 옵션을 붙여서 다시 시도한다
원격 브랜치에 어떤 내용이 있던 상관하지 않고 강제로 push해서 덮어씌워버리라는 의미이다

충돌이 잘 해결되면 위처럼 Merge pull request 버튼이 활성화되고 초록색으로 변한다
회색 침침한 배경에서 초록색으로 바뀌니 안도감이 느껴진다
Merge를 진행하자

옆의 화살표를 누르면 merge 옵션이 나온다
Create a merge commit은 merge 커밋 (merged ~ into ~) 을 하나 생성해 merge되었음을 표시한다squash and merge는 여러 커밋을 하나의 커밋으로 뭉쳐 merge한다rebase and merge는 여러 커밋을 전부 rebase하고 merge하는데, rebase 특성상 커밋들이 조금씩 수정되므로 해당 레포지토리에서 작업하는 다른 사람들한테서 충돌이 발생할 수 있다
보통은 기본적인 Create a merge commit으로 진행한다

새로운 merge commit을 생성하므로 커밋 메시지와 코멘트를 남긴다
아래의 코멘트는 커밋 시 커밋 제목이 아니라 개행 후 적히는 내용으로 들어간다
설정이 완료되면 Confirm merge를 누르자

잘 merge되면 풀 리퀘스트가 보라색으로 변하고, 아래에 Merge되었음을 알리는 문구가 추가된다
merge된 브랜치나 포크는 삭제해도 상관없다
만약 해당 PR에 연결되어 있는 이슈가 있다면, 그 이슈도 자동으로 닫히게 할 수있다

merge commit을 보자
아까 적은 커밋 메시지와 코멘트가 보인다
내 수정사항이 잘 반영되었음을 볼 수 있다
여담
깃은 항상 쓰는 기능만 썼었는데 파면 팔 수록 기능이 정말 많다는 것을.. 느낀다
사실 rebase도 최근에야 알았고 pull이 fetch & merge인 것도 비교적 최근에 알았다
많이 쓰는 기술인 만큼 조금 더 공부가 필요할 듯 하다