치춘짱베리굿나이스

Github, Git 기본 <Pull Request, pull, 충돌 해결> 본문

이론적인 부분들/Git Github

Github, Git 기본 <Pull Request, pull, 충돌 해결>

치춘 2022. 7. 23. 14:39

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는 gitmaintest/testbranch분기 지점 (공통 조상) 을 찾아내고, 3-way merge 방식을 사용하여 두 브랜치를 합친다

3-way merge는

  • merge 받으려는 브랜치 (main) 의 가장 최신 커밋
  • merge하려는 브랜치 (test/testbranch)의 가장 최신 커밋
  • 분기 지점 커밋

의 3개 커밋을 이용한다

분기 지점 커밋을 이용하여 maintest/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 pullgit fetchgit 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인 것도 비교적 최근에 알았다

많이 쓰는 기술인 만큼 조금 더 공부가 필요할 듯 하다


참고 자료

git pull 할 때 인자 안줘도 알아서 되게 하기

[Git] Merge(3-way merge) 이해하기

병합할 때 발생하는 충돌 해결하기

브랜치 통합하기

Comments