콘텐츠로 건너뛰기

Git 사용 방법 (브랜치와 Pull Request) (3편)

  • 기준

이 글은 Git 사용 방법 (GitHub 원격 저장소) (2편)에서 이어진다.

1편에서는 내 PC에 Git 저장소를 만들고, git add, git commit, git log, git diff 등의 기본 명령어를 사용하는 방법을 살펴봤다.

2편에서는 로컬 Git 저장소를 GitHub 원격 저장소와 연결하고, git push, git pull, git fetch, git clone을 사용해 커밋을 주고받는 방법을 정리했다. 또한 마지막 부분에서는 새 브랜치를 만들고 GitHub에 올리는 기초 흐름도 살펴봤다.

이번 3편에서는 브랜치를 실제 협업 과정에서 어떻게 사용하는지 자세히 알아본다. 별도의 브랜치에서 작업하고, GitHub에서 Pull Request를 만든 뒤, 변경 내용을 검토하고 main 브랜치에 merge하는 전체 흐름을 정리한다.

브랜치끼리 같은 부분을 다르게 수정했을 때 발생할 수 있는 병합 충돌(merge conflict)의 의미와 기초 해결 방법도 함께 살펴본다.

이 글의 실습은 본인이 만든 GitHub 저장소이거나, 본인에게 브랜치를 push할 권한이 있는 저장소를 기준으로 한다. 다른 사람의 공개 저장소에 직접 push할 권한이 없는 경우에는 저장소를 fork한 뒤 Pull Request를 만드는 별도의 흐름이 필요하다.

1. 브랜치와 Pull Request의 전체 흐름

실제 협업 프로젝트에서는 새로운 기능이나 수정 사항을 main 브랜치에서 바로 작업하지 않는 경우가 많다.

  1. 현재 작업 폴더에 커밋하지 않은 변경 사항이 없는지 확인한다.
  2. main 브랜치로 이동한다.
  3. GitHub의 최신 main 브랜치를 가져온다.
  4. 새 작업 브랜치를 만든다.
  5. 새 브랜치에서 파일을 수정한다.
  6. 수정 내용을 커밋한다.
  7. 작업 브랜치를 GitHub에 push한다.
  8. GitHub에서 Pull Request를 만든다.
  9. 변경된 파일과 커밋을 검토한다.
  10. 문제가 없으면 Pull Request를 main에 merge한다.
  11. 로컬 main 브랜치를 최신 상태로 갱신한다.
  12. 작업이 끝난 브랜치를 삭제한다.

명령어와 GitHub 작업을 함께 표시하면 다음과 같다.

git status
git switch main
git pull
git switch -c profile-update

# 파일 수정
git status
git add 파일명
git commit -m "커밋 메시지"
git push -u origin profile-update

그다음 GitHub 웹사이트에서 Pull Request를 만들고, 변경 내용을 확인한 뒤 main 브랜치에 merge한다.

merge가 끝나면 다시 로컬 저장소로 돌아와 아래와 같이 정리한다.

git switch main
git pull
git branch -d profile-update

이번 글에서는 이 전체 흐름을 단계별로 살펴본다.

2. 브랜치란 무엇인가?

브랜치(Branch)는 Git의 작업 흐름을 나누는 독립적인 작업 줄기라고 생각하면 된다.

Git 저장소에는 보통 main이라는 기본 브랜치가 있다. 프로젝트에서 main은 다른 작업의 기준이 되는 안정적인 브랜치로 사용하는 경우가 많다.

새로운 기능을 만들거나, 버그를 수정하거나, 실험적인 작업을 할 때 별도의 브랜치를 만들면 main에 직접 영향을 주지 않고 작업할 수 있다.

예를 들어 프로필 관련 기능을 수정한다고 가정해 보자.

이때 profile-update라는 브랜치를 만들 수 있다.

main
└─ profile-update

처음 브랜치를 만들었을 때 profile-update는 현재 main과 같은 커밋을 가리킨다.

이후 profile-update 브랜치에서 파일을 수정하고 커밋하면, 두 브랜치의 기록이 서로 달라진다.

A---B---C main
         \ D---E  profile-update

위 그림에서 A, B, C, D, E는 각각 커밋을 의미한다.

profile-update 브랜치는 C 커밋에서 갈라진 뒤 D, E라는 새로운 커밋을 갖게 된다. 하지만 main은 여전히 C를 가리키고 있다.

따라서 profile-update에서 작업하더라도 그 변경 내용이 자동으로 main에 들어가는 것은 아니다.

작업이 끝난 뒤 merge해야 profile-update의 변경 내용이 main에 반영된다.

브랜치를 만들었다고 파일 전체가 별도로 복사되는 것은 아니다. Git은 커밋 기록을 기준으로 브랜치를 관리하므로 브랜치를 빠르게 만들고 전환할 수 있다.

3. main 브랜치에서 바로 작업하지 않는 이유

혼자 사용하는 연습용 저장소라면 main에서 바로 파일을 수정하고 커밋해도 큰 문제가 없을 수 있다.

하지만 여러 사람이 함께 사용하는 프로젝트에서는 main의 안정성이 중요하다.

예를 들어 아직 완성되지 않은 기능을 main에 바로 커밋하고 push했다고 가정해 보자.

이 경우 다른 팀원이 git pull을 실행했을 때 완성되지 않은 코드를 그대로 내려받을 수 있다. 코드에 오류가 있다면 프로그램이 실행되지 않거나 기존 기능까지 영향을 받을 수 있다.

별도의 작업 브랜치를 사용하면 이러한 문제를 줄일 수 있다.

profile-update 브랜치에서 작업하는 동안에는 main이 변경되지 않는다. 작업이 완료된 뒤 Pull Request를 통해 변경 내용을 먼저 검토하고, 문제가 없을 때만 main에 merge할 수 있다.

Pull Request에서는 다음 내용을 확인할 수 있다.

  • 어떤 브랜치에서 어떤 브랜치로 합치려는지
  • 어떤 커밋이 포함되어 있는지
  • 어떤 파일이 변경되었는지
  • 기존 코드와 비교해 어떤 줄이 달라졌는지
  • 다른 사람이 작성한 댓글이나 리뷰가 있는지
  • 자동 테스트가 성공했는지
  • 현재 merge 가능한 상태인지

따라서 브랜치와 Pull Request를 사용하면 작업과 검토 과정을 분리하고, main에 들어갈 변경 내용을 merge 전에 확인할 수 있다.

4. 작업 시작 전 상태 확인하기

새 브랜치를 만들기 전에 먼저 현재 작업 폴더 상태를 확인한다.

git status

아래와 같이 표시된다면 현재 커밋하지 않은 변경 사항이 없는 상태다.

On branch main
nothing to commit, working tree clean

# 또는

On branch practice-branch
Your branch is up to date with 'origin/practice-branch'.

nothing to commit, working tree clean

여기서 working tree clean은 현재 작업 폴더에 커밋하지 않은 수정 사항이 없다는 뜻이다.

하지만 작업 폴더가 깨끗하다는 것이 GitHub의 최신 상태와 같다는 뜻은 아니다.

로컬 main에 수정 사항이 없더라도, 다른 사람이 GitHub의 main에 새 커밋을 올렸을 수 있다.

따라서 새 작업을 시작할 때는 다음 순서가 안전하다.

git status
git switch main
git pull

각 명령어의 의미는 다음과 같다.

  • git status: 커밋하지 않은 변경 사항이 있는지 확인한다.
  • git switch main: 현재 브랜치를 main으로 전환한다.
  • git pull: GitHub의 최신 변경 내용을 로컬 main에 가져와 반영한다.
  • git status에서 수정 중인 파일이 표시된다면 무작정 브랜치를 전환하거나 git pull을 실행하지 않는 것이 좋다.

먼저 현재 변경 내용을 커밋할지, 원래 작업하던 브랜치에서 계속 작업할지 판단해야 한다.

이번 실습에서는 아래와 같이 작업 폴더가 깨끗한 상태라고 가정한다.

nothing to commit, working tree clean

5. 새 브랜치 만들고 이동하기

로컬 main을 최신 상태로 만들었다면 새 작업 브랜치를 만든다.

이번 실습에서는 profile-update라는 브랜치를 사용한다.

# 명령어 실행은 아래에서 다루니 참고만 할 것
git switch -c profile-update

git switch -c profile-update는 다음 두 작업을 동시에 수행한다.

  1. 현재 브랜치를 기준으로 profile-update라는 새 브랜치를 만든다.
  2. 새로 만든 profile-update 브랜치로 이동한다.

여기서 중요한 점은 새 브랜치가 현재 브랜치를 기준으로 만들어진다는 것이다.

따라서 오래된 브랜치에서 git switch -c profile-update를 실행하면 새 브랜치도 오래된 커밋을 기준으로 만들어질 수 있다.

이 때문에 먼저 main으로 이동하고 git pull을 실행한 뒤 새 브랜치를 만드는 것이 좋다.

git switch main
git pull
git switch -c profile-update

현재 브랜치를 확인하려면 아래 명령어를 입력한다.

git branch

출력 결과는 다음과 비슷하다.

  main
  practice-branch
* profile-update

별표(*)가 붙은 profile-update가 현재 작업 중인 브랜치다.

이미 존재하는 브랜치로 이동할 때는 -c 옵션을 사용하지 않는다.

git switch main

정리하면 다음과 같다.

새 브랜치를 만들고 이동:

git switch -c 브랜치명

-c는 새로운 브랜치를 생성하는 옵션이다.

이미 있는 브랜치로 이동:

git switch 브랜치명

6. 브랜치에서 파일 수정하고 커밋하기

이제 profile-update 브랜치에서 파일을 수정해 보자.

이번 실습에서는 저장소에 profile.txt 파일을 만들고 다음 문장을 입력한다고 가정한다.

프로필 페이지 수정 연습

파일을 저장한 뒤 현재 상태를 확인한다.

git status

새 파일이라면 다음과 비슷하게 표시될 수 있다.

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        profile.txt

Untracked files는 Git이 아직 추적하지 않는 새 파일이라는 뜻이다.

기존에 Git이 추적하던 파일을 수정했다면 git diff로 변경 내용을 확인할 수 있다.

단, 아직 git add하지 않은 새 파일(Untracked file)의 내용은 일반적인 git diff 결과에는 나타나지 않는다.

새 파일을 포함해 커밋할 내용을 확인하고 싶다면 먼저 스테이징한 뒤 git diff --staged를 사용할 수 있다.

git add profile.txt
git diff --staged

git diff --staged는 스테이징 영역에 올라간 변경 사항, 즉 다음 커밋에 포함될 내용을 보여준다.

내용을 확인했다면 커밋을 만든다.

git commit -m "프로필 설명 파일 추가"

커밋이 만들어졌는지 확인하려면 다음 명령어를 사용할 수 있다.

git log --oneline

출력 결과는 다음과 비슷하다.

81331c1 (HEAD -> profile-update) 프로필 설명 파일 추가
21ae00a 이전 커밋

이제 profile-update 브랜치에는 새로운 커밋이 생겼다.

하지만 아직 GitHub에는 올라가지 않았고, 로컬 main에도 반영되지 않은 상태다.

7. 작업 브랜치를 GitHub에 push하기

로컬 profile-update 브랜치의 커밋을 GitHub에 올리려면 아래 명령어를 입력한다.

git push -u origin profile-update

각 부분의 의미는 다음과 같다.

git push: 로컬 커밋을 원격 저장소로 올린다.

-u: 로컬 브랜치와 원격 브랜치의 추적 관계를 설정한다.

origin: 2편에서 등록한 GitHub 원격 저장소의 별명이다.

profile-update: GitHub에 올릴 브랜치 이름이다.

명령어를 실행하면 GitHub 원격 저장소에도 profile-update 브랜치가 생성된다.

처음 한 번 추적 관계를 설정한 뒤에는 같은 브랜치에서 추가 커밋을 올릴 때 아래처럼 입력해도 된다.

git push

예를 들어 Pull Request를 만든 뒤 리뷰 의견을 받아 파일을 다시 수정했다면 다음과 같이 작업할 수 있다.

git add profile.txt
git commit -m "프로필 설명 문장 수정"
git push

기존 Pull Request를 닫고 새로 만들 필요는 없다.

같은 profile-update 브랜치에 새 커밋을 push하면 기존 Pull Request에도 새로운 커밋과 변경 내용이 자동으로 추가된다.

8. GitHub에서 Pull Request 만들기

GitHub 저장소 페이지를 열면 방금 push한 브랜치와 관련된 안내가 표시될 수 있다.

GitHub - Compare & pull request
GitHub - Compare & pull request

보통 Compare & pull request 버튼을 눌러 Pull Request를 만들 수 있다.

GitHub Pull requests - New pull request
GitHub Pull requests - New pull request

버튼이 보이지 않는다면 GitHub 저장소의 Pull requests 탭으로 이동한 뒤 New pull request 버튼을 눌러도 된다.

이후 과정은 Pull requests > New pull request 경로를 기준으로 설명한다. 이 경로에서는 base와 compare 브랜치를 선택한 뒤 Create pull request 버튼을 눌러 Pull Request 작성 화면으로 이동한다. 반면 Compare & pull request 버튼으로 바로 들어간 경우에는 base와 compare를 확인한 뒤 제목과 설명을 작성하는 화면으로 바로 이어질 수 있다.

Pull Request를 만들 때는 base 브랜치와 compare 브랜치를 확인해야 한다.

이번 실습에서는 다음과 같이 설정한다.

base: main
compare: profile-update
GitHub - Pull Request의 base 및 compare 브랜치
GitHub - Pull Request의 base 및 compare 브랜치

base 브랜치는 변경 내용을 받을 대상 브랜치다.

compare 브랜치는 merge하려는 변경 내용이 들어 있는 작업 브랜치다.

즉, 위 설정은 다음과 같은 의미다.

profile-update 브랜치의 변경 내용을 main 브랜치에 반영하고 싶다.

base와 compare를 반대로 선택하면 의도와 다른 방향으로 Pull Request가 만들어질 수 있으므로 반드시 확인해야 한다.

Pull requests > New pull request 경로에서는 base와 compare 브랜치를 확인한 뒤 Create pull request 버튼을 누르면 Pull Request 작성 화면으로 이동한다.

GitHub Pull requests - Create pull request
GitHub Pull requests - Create pull request

Pull Request 제목을 작성한다. 예를 들면 다음과 같다.
예: 프로필 설명 파일 추가

GitHub Pull requests - Add a title
GitHub Pull requests - Add a title

설명에는 무엇을 변경했는지 간단히 작성한다.
예: profile.txt 파일을 추가하고 프로필 페이지 연습 문장을 작성했습니다.

GitHub Pull requests - Add a description
GitHub Pull requests - Add a description

조금 더 구조적으로 작성하려면 다음과 같이 쓸 수 있다.

변경 내용
- profile.txt 파일 추가
- 프로필 페이지 설명 문장 작성

확인 사항
- profile.txt 파일이 정상적으로 표시되는지 확인

제목과 설명을 작성한 뒤 다시 Create pull request 버튼을 누르면 Pull Request가 실제로 생성된다.

아직 작업이 끝나지 않았지만 진행 상황을 먼저 공유하고 싶다면 Draft Pull Request를 사용할 수도 있다. Draft 상태의 Pull Request는 작업 중이라는 의미이며, Draft 상태에서는 merge할 수 없다. 작업이 완료되면 Ready for review 상태로 전환한 뒤 merge할 수 있다.

9. Pull Request의 변경 내용 검토하기

Pull Request를 만든 뒤 바로 merge하기보다, 먼저 변경 내용을 확인하는 습관이 좋다.

Pull Request 화면에는 일반적으로 다음과 같은 탭이 있다.

Conversation: Pull Request 설명, 댓글, 리뷰, 진행 기록을 확인한다.

Commits: 해당 Pull Request에 포함된 커밋을 확인한다.

Checks: 자동 테스트, 빌드, 검사 결과를 확인한다.

Files changed: 변경된 파일과 줄을 기존 내용과 비교한다.

Files changed에서는 추가된 줄과 삭제된 줄을 확인할 수 있다.

일반적으로 추가된 줄은 +, 삭제된 줄은 -로 표시된다.

GitHub Pull requests - Files changed
GitHub Pull requests - Files changed

실수로 관계없는 파일을 수정하지 않았는지, 불필요한 내용이 포함되지 않았는지 확인한다.

또한 Commits 탭에서 커밋 메시지가 변경 내용을 이해하기 쉽게 작성되었는지 확인한다.

팀 프로젝트에서는 다른 사람에게 리뷰를 요청할 수도 있다.

리뷰어는 변경된 줄에 댓글을 남기거나 다음과 같은 의견을 보낼 수 있다.

  • 승인
  • 수정 요청
  • 일반적인 의견

리뷰 의견을 반영해야 한다면 로컬 profile-update 브랜치에서 파일을 다시 수정하고 커밋한 뒤 push한다.

git status
git add profile.txt
git commit -m "리뷰 의견에 따라 프로필 문장 수정"
git push

같은 브랜치에 push했기 때문에 기존 Pull Request가 자동으로 갱신된다.

GitHub Pull requests - Commits
GitHub Pull requests - Commits

10. Pull Request를 main에 merge하기

변경 내용을 확인했고 문제가 없다면 Pull Request를 main에 merge할 수 있다.

저장소 설정과 사용 권한에 따라 merge 버튼의 종류가 다르게 표시될 수 있다.

대표적인 방식은 다음과 같다.

Merge pull request: 작업 브랜치의 각 커밋을 유지하면서 별도의 merge commit을 만들어 base 브랜치에 병합한다.

Squash and merge: 작업 브랜치의 여러 커밋을 하나의 커밋으로 합쳐 병합한다.

Rebase and merge: 작업 브랜치의 각 커밋을 base 브랜치 위에 차례대로 다시 적용하여 merge commit 없이 선형으로 병합한다.

이번 글에서는 가장 기본적인 Merge pull request를 기준으로 설명한다.

GitHub 저장소의 Pull requests 탭에서 병합할 Pull Request를 선택해 상세 페이지로 들어간다.

특정 Pull Request의 상세 페이지에서 상단의 Ready to merge 상태 버튼을 눌러 Merge status 패널을 연 뒤, Merge pull request 버튼을 누른다.

Ready to merge는 현재 Pull Request가 병합 가능한 상태임을 나타내며, 리뷰, 자동 검사 등의 조건을 충족하지 못한 경우에는 다른 merge status가 표시될 수 있다.

GitHub - Ready to merge
GitHub - Ready to merge
GitHub - Merge pull request
GitHub - Merge pull request

그다음 Confirm merge를 눌러 병합을 완료한다.

GitHub - Confirm merge
GitHub - Confirm merge

저장소에 브랜치 보호 규칙이나 필수 리뷰, 자동 테스트 조건이 설정되어 있다면 조건이 충족되기 전에는 merge 버튼을 사용할 수 없을 수 있다.

merge가 완료되면 profile-update 브랜치의 변경 내용이 GitHub의 main 브랜치에 반영된다.

하지만 이 시점에도 내 PC의 로컬 main은 자동으로 갱신되지 않는다.

GitHub의 main만 최신 상태가 된 것이므로 로컬에서도 별도로 git pull을 실행해야 한다.

11. merge 후 로컬 저장소 정리하기

GitHub에서 Pull Request를 merge한 뒤 로컬 저장소로 돌아온다.

먼저 main 브랜치로 이동한다.

git switch main

그다음 GitHub의 최신 main을 가져온다.

git pull

이제 로컬 main에도 Pull Request로 merge한 변경 내용이 반영된다.

작업이 끝난 로컬 브랜치는 삭제할 수 있다.

git branch -d profile-update

-d는 강제로 삭제하지 않는 일반적인 브랜치 삭제 옵션이다.

Git은 해당 브랜치가 설정된 upstream 브랜치에 완전히 merge되어 있는지 확인하며, upstream이 없다면 현재 HEAD에 merge되어 있는지 확인한 뒤 삭제한다.

따라서 Pull Request가 실제로 main에 merge되었는지는 GitHub에서 별도로 확인한 뒤 작업 브랜치를 삭제하는 것이 좋다.

-D 옵션을 사용하면 merge 여부와 관계없이 강제로 삭제할 수 있지만, 아직 다른 곳에 반영되지 않은 커밋을 가리키는 브랜치 참조를 잃을 수 있으므로 초보 단계에서는 함부로 사용하지 않는 것이 좋다.

GitHub 원격 저장소의 작업 브랜치는 Pull Request merge 후 Delete branch 버튼을 눌러 삭제할 수 있다.

GitHub - Delete branch
GitHub - Delete branch

명령어로 삭제하려면 다음과 같이 입력한다.

git push origin --delete profile-update

정리하면 Pull Request merge 이후의 기본 흐름은 다음과 같다.

git switch main
git pull
git branch -d profile-update

원격 브랜치까지 명령어로 삭제할 경우에는 다음 명령어를 추가한다.

git push origin --delete profile-update

12. 병합 충돌은 왜 발생할까?

이번 충돌 실습에서는 앞의 profile.txt와 구분하기 위해 새로운 profile2.txt 파일을 사용한다고 가정한다.

대부분의 경우 Git은 서로 다른 브랜치의 변경 내용을 자동으로 합칠 수 있다.

예를 들어 한 브랜치에서는 profile2.txt를 수정하고, 다른 브랜치에서는 README를 수정했다면 변경된 위치가 다르므로 Git이 자동으로 합칠 가능성이 높다.

하지만 서로 다른 브랜치에서 같은 파일(profile2.txt)의 같은 부분을 다르게 수정하면 Git이 어느 내용을 선택해야 할지 결정하지 못할 수 있다.

이를 병합 충돌(merge conflict)이라고 한다.

예를 들어 main 브랜치의 profile2.txt에 다음 문장이 있다고 하자.

프로필 설명: 기본 버전

profile-update 브랜치에서는 같은 줄을 아래처럼 수정했다.

프로필 설명: 사용자 정보 표시

그런데 다른 사람이 main 브랜치에서 같은 줄을 아래처럼 수정하고 먼저 push했다.

프로필 설명: 사용자 이름 표시

이제 profile-updatemain에 merge하려고 하면 Git은 어느 문장을 최종 결과로 사용할지 자동으로 판단하기 어렵다.

  • 사용자 정보 표시를 남길 것인가?
  • 사용자 이름 표시를 남길 것인가?
  • 두 내용을 조합할 것인가?

Git은 임의로 한쪽을 선택하지 않고 사용자에게 직접 결정하도록 요청한다.

병합 충돌은 주로 다음과 같은 상황에서 발생한다.

  • 두 브랜치가 같은 파일의 같은 줄을 다르게 수정한 경우
  • 한 브랜치에서는 파일을 수정하고 다른 브랜치에서는 같은 파일을 삭제한 경우
  • 작업 브랜치가 오래되어 최신 main과 같은 부분에 서로 다른 변경 사항이 누적된 경우
  • 여러 사람이 같은 파일의 같은 부분을 서로 다르게 수정한 경우

병합 충돌은 Git 저장소가 망가졌다는 뜻이 아니다.

Git이 자동으로 결정할 수 없는 부분에 대해 사용자가 최종 내용을 선택해야 한다는 뜻이다.

13. Pull Request의 충돌을 로컬에서 해결하기

profile-update에서 Pull Request를 만들기 전 main과 충돌한다면 GitHub에 다음과 비슷한 메시지가 나타날 수 있다.

Can’t automatically merge. Don’t worry, you can still create the pull request.
GitHub Compare changes - Can't automatically merge.
GitHub Compare changes - Can't automatically merge.

profile-update에서 Pull Request를 만들었는데 main과 충돌한다면 GitHub에 다음과 비슷한 메시지가 나타날 수 있다.

This branch has conflicts that must be resolved
GitHub - Pull requests - Resolve conflicts
GitHub - Pull requests - Resolve conflicts

이 경우 profile-update의 변경 내용을 곧바로 main에 merge할 수 없다.

충돌을 로컬에서 해결할 때는 작업 브랜치에 최신 main의 내용을 가져와 병합한 뒤, 충돌을 해결하고 다시 작업 브랜치를 push한다.

먼저 현재 작업 상태를 확인한다.

git status

커밋하지 않은 변경 사항이 없는지 확인한 뒤 충돌이 발생한 작업 브랜치로 이동한다.

git switch profile-update

그다음 GitHub의 최신 브랜치 정보를 가져온다.

git fetch origin

git fetch origin을 실행하면 원격 저장소의 최신 정보가 내려받아지고, GitHub의 main을 나타내는 원격 추적 브랜치(remote-tracking branch)origin/main도 갱신된다.

이제 최신 origin/main을 현재 작업 브랜치인 profile-update에 merge한다.

git merge origin/main

이 명령어의 방향을 정확하게 이해해야 한다.

현재 브랜치: profile-update

가져와 합칠 브랜치: origin/main

즉, 최신 GitHub main의 변경 내용을 profile-update 브랜치에 반영하는 것이다.

충돌 없이 자동으로 merge되었다면 다음과 같이 push하면 된다.

git push

기존 Pull Request는 같은 profile-update 브랜치를 사용하고 있으므로 자동으로 갱신된다.

충돌이 발생했다면 Git이 merge를 중단하고 직접 해결해야 하는 파일을 표시한다.

Auto-merging profile2.txt
CONFLICT (content): Merge conflict in profile2.txt
Automatic merge failed; fix conflicts and then commit the result.

이때 먼저 아래 명령어로 충돌 파일을 확인한다.

git status

출력 결과에는 다음과 비슷한 항목이 표시될 수 있다.

Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   profile2.txt

both modified는 현재 브랜치와 병합하려는 브랜치에서 같은 파일을 모두 수정했다는 뜻이다.

14. 충돌 표시를 직접 수정하는 방법

충돌이 발생한 profile2.txt 파일을 텍스트 편집기나 Visual Studio Code로 연다.

파일에는 다음과 같은 충돌 표시가 들어 있을 수 있다.

<<<<<<< HEAD
프로필 설명: 사용자 정보 표시
=======
프로필 설명: 사용자 이름 표시
>>>>>>> origin/main

<<<<<<< HEAD

현재 체크아웃한 브랜치의 내용이 시작되는 위치다. 지금은 profile-update 브랜치에서 merge했으므로 profile-update 쪽 변경 내용이다.

=======

두 브랜치의 변경 내용을 구분하는 선이다.

>>>>>>> origin/main

병합하려는 origin/main 쪽 내용이 끝나는 위치다.

사용자는 두 변경 내용을 확인한 뒤 최종적으로 남길 내용을 직접 정해야 한다.

현재 브랜치 내용만 남길 수도 있다.

프로필 설명: 사용자 정보 표시

origin/main의 내용만 남길 수도 있다.

프로필 설명: 사용자 이름 표시

두 내용을 조합해 새로운 문장으로 작성할 수도 있다.

프로필 설명: 사용자 이름과 정보 표시

최종 내용을 결정한 뒤 아래 충돌 표시를 모두 삭제해야 한다.

<<<<<<< HEAD
=======
>>>>>>> origin/main

파일에 최종 문장만 남아 있어야 한다.

프로필 설명: 사용자 이름과 정보 표시

파일을 저장한 뒤 해결한 파일을 스테이징한다.

git add profile2.txt

상태를 다시 확인한다.

git status

아래와 같이 표시되면 모든 충돌이 고쳐졌다는 것이다.

On branch profile-update
Your branch is up to date with 'origin/profile-update'.

All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:
        modified:   profile2.txt
Git - All conflicts fixed
Git - All conflicts fixed

충돌이 모두 해결되었다면 merge 커밋을 만든다.

git commit -m "main 브랜치와의 병합 충돌 해결"

또는 다음 명령어로 중단되었던 merge 작업을 계속할 수도 있다.

git merge --continue

git merge --continue를 사용하면 커밋 메시지를 작성하기 위한 편집기가 열릴 수 있다.

초보자에게는 메시지를 직접 지정하는 아래 방식이 더 단순할 수 있다.

git commit -m "main 브랜치와의 병합 충돌 해결"

커밋이 완료되면 작업 브랜치를 GitHub에 다시 올린다.

git push

기존 Pull Request는 자동으로 갱신된다.

GitHub Pull requests - Commits
GitHub Pull requests - Commits

GitHub에서 Pull Request 페이지를 새로고침하면 충돌 표시가 사라지고 merge 가능한 상태로 바뀔 수 있다.

GitHub Pull requests - Merge pull request
GitHub Pull requests - Merge pull request

전체 과정을 정리하면 다음과 같다.

git status
git switch profile-update
git fetch origin
git merge origin/main
# 충돌 파일을 열어 최종 내용으로 수정
git add profile2.txt
git commit -m "main 브랜치와의 병합 충돌 해결"
git push

GitHub 웹사이트의 Resolve conflicts 버튼으로 간단한 줄 단위 충돌을 해결할 수도 있다.

GitHub - Pull requests - Resolve conflicts
GitHub - Pull requests - Resolve conflicts

하지만 GitHub 웹 편집기는 같은 줄을 다르게 수정한 단순한 충돌만 해결할 수 있다. 수정·삭제 충돌처럼 복잡한 경우에는 로컬 Git이나 다른 Git 클라이언트에서 해결해야 한다.

GitHub - Pull requests - Resolve conflicts
GitHub - Pull requests - Resolve conflicts

15. 충돌 해결을 취소하고 처음으로 돌아가기

충돌이 너무 복잡하거나 잘못된 브랜치를 merge했다면 작업을 억지로 계속하지 않고 merge 이전 상태로 돌아갈 수 있다.

merge가 아직 완료되지 않은 상태에서 다음 명령어를 사용한다.

git merge --abort

git merge --abort는 진행 중인 merge를 취소하고 가능한 한 merge 직전 상태로 복원한다.

다만 merge를 시작하기 전에 커밋하지 않은 변경 사항이 많았다면 원래 상태를 완전히 복원하지 못할 수도 있다.

따라서 git merge를 실행하기 전에는 다음 사항을 확인하는 것이 중요하다.

  • 올바른 브랜치에 있는가?
  • 현재 작업 폴더에 커밋하지 않은 수정 사항이 없는가?
  • 필요한 작업은 미리 커밋했는가?
  • 어떤 브랜치를 현재 브랜치에 합치려는지 알고 있는가?

이번 실습의 경우 다음 상태인지 확인한다.

현재 브랜치: profile-update
merge할 대상: origin/main
실행 명령어: git merge origin/main

충돌 해결을 포기하고 merge 전으로 돌아가려면 다음 명령어를 사용한다.

git merge --abort

merge를 취소한 뒤 git status로 상태를 확인한다.

git status

16. 전체 명령어 요약

이번 글에서는 새 브랜치를 만들고, 해당 브랜치에서 작업한 뒤, GitHub에 push하고 Pull Request로 main에 merge하는 과정을 살펴봤다.

가장 기본적인 브랜치 작업 흐름은 다음과 같다.

# 1. 작업 폴더 상태 확인
git status
# 2. main 브랜치로 이동
git switch main
# 3. 최신 main 가져오기
git pull
# 4. 새 작업 브랜치 생성 및 이동
git switch -c profile-update
# 5. 파일 수정
# 6. 변경 상태 확인
git status
git diff
# 7. 스테이징
git add 파일명
# 8. 스테이징된 변경 내용 확인
git diff --staged
# 9. 커밋
git commit -m "커밋 메시지"
# 10. GitHub에 작업 브랜치 올리기
git push -u origin profile-update

그다음 GitHub에서 다음 작업을 수행한다.

경로 A: Compare & pull request 사용

  1. Compare & pull request 선택
  2. base: maincompare: profile-update 확인
  3. Pull Request 제목과 설명 작성
  4. Create pull request 선택 > Pull Request 생성

경로 B: Pull requests 탭에서 직접 생성

  1. Pull requests > New pull request 선택
  2. base: main 확인
  3. compare: profile-update 확인
  4. Create pull request 선택 > Pull Request 작성 화면으로 이동
  5. Pull Request 제목과 설명 작성
  6. Create pull request 선택 > Pull Request 생성

그 이후는 공통:

  1. Files changedCommits 확인
  2. Ready to merge 상태 확인
  3. Merge pull request 선택
  4. Confirm merge 선택

Pull Request merge 후 로컬 저장소 정리:

git switch main
git pull
git branch -d profile-update

원격 작업 브랜치 삭제:

git push origin --delete profile-update

Pull Request에서 충돌이 발생했을 때의 기본 흐름:

# 1. 작업 상태 확인
git status
# 2. 작업 브랜치로 이동
git switch profile-update
# 3. GitHub의 최신 브랜치 정보 가져오기
git fetch origin
# 4. 최신 main을 작업 브랜치에 merge
git merge origin/main
# 5. 충돌 파일을 열어 최종 내용으로 수정
# 6. 해결한 파일 스테이징
git add 파일명
# 7. 충돌 해결 커밋
git commit -m "main 브랜치와의 병합 충돌 해결"
# 8. 작업 브랜치를 다시 push
git push

진행 중인 merge를 취소할 때는 다음 명령어를 사용한다.

git merge --abort

각 명령어의 역할을 정리하면 다음과 같다.

상황: 현재 브랜치와 작업 상태 확인
명령어: git status
의미: 현재 브랜치, 수정 파일, 스테이징 상태, 충돌 상태를 확인한다.

상황: 브랜치 목록 확인
명령어: git branch
의미: 로컬 브랜치 목록과 현재 브랜치를 확인한다.

상황: 기존 브랜치로 이동
명령어: git switch 브랜치명
의미: 이미 존재하는 브랜치로 이동한다.

상황: 새 브랜치 생성 및 이동
명령어: git switch -c 브랜치명
의미: 현재 브랜치를 기준으로 새 브랜치를 만들고 이동한다.

상황: 브랜치를 GitHub에 처음 올리기
명령어: git push -u origin 브랜치명
의미: 로컬 브랜치를 GitHub에 올리고 원격 브랜치와 추적 관계를 설정한다.

상황: Pull Request 만들기
작업: basecompare 브랜치를 선택한다.
의미: 작업 브랜치의 변경 내용을 대상 브랜치에 merge하자고 제안한다.

상황: GitHub의 최신 정보 가져오기
명령어: git fetch origin
의미: 원격 저장소의 최신 브랜치와 커밋 정보를 가져오지만 현재 작업 파일에는 바로 반영하지 않는다.

상황: 최신 main을 작업 브랜치에 반영
명령어: git merge origin/main
의미: 현재 작업 브랜치에 최신 GitHub main의 변경 내용을 합친다.

상황: 충돌 해결 완료 표시
명령어: git add 파일명
의미: 충돌을 해결한 파일을 스테이징해 Git에 해결되었다고 알린다.

상황: 충돌 해결 커밋
명령어: git commit -m "커밋 메시지"
의미: 충돌을 해결한 결과를 새로운 커밋으로 기록한다.

상황: merge 취소
명령어: git merge --abort
의미: 완료되지 않은 merge를 중단하고 merge 이전 상태로 돌아가도록 시도한다.

상황: 로컬 브랜치 안전 삭제
명령어: git branch -d 브랜치명
의미: 설정된 upstream 브랜치에 완전히 merge된 브랜치를 삭제하며, upstream이 없으면 현재 HEAD를 기준으로 확인한다.

상황: GitHub 원격 브랜치 삭제
명령어: git push origin --delete 브랜치명
의미: GitHub 원격 저장소의 작업 브랜치를 삭제한다.

정리하면, 이번 3편의 핵심 흐름은 다음과 같다.

git status
git switch main
git pull
git switch -c 브랜치명
# 작업하기
git add 파일명
git commit -m "커밋 메시지"
git push -u origin 브랜치명

그다음 GitHub에서 Pull Request를 만들고, 변경 내용을 검토한 뒤 main에 merge한다.

merge가 끝나면 로컬에서도 main을 최신 상태로 갱신하고 작업 브랜치를 정리한다.

git switch main
git pull
git branch -d 브랜치명

병합 충돌이 발생했다면 Git이 망가진 것이 아니다. 충돌 파일을 열어 최종 내용을 직접 결정하고, 충돌 표시를 제거한 뒤 다시 커밋하고 push하면 된다.

다음 글에서는 Git 작업 중 실수했을 때 변경 내용을 되돌리는 방법을 정리할 예정이다. git restore, git reset, git revert가 각각 어떤 상황에서 사용되는지와 안전하게 되돌리는 방법을 살펴볼 예정이다.

Join the conversation

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다