git pull | --force, --rebase 옵션, fetch 와의 차이, conflict 해결
이번 포스팅은 git pull
명령어와 관련해서 자주 궁금해하는 내용을 다룹니다.
기본적인 개념과 사용법, --force
옵션, --rebase
옵션, conflict 해결 방법 등을 정리하겠습니다.
1. git pull 개념 잡기
git pull
명령어는 원격 저장소의 변경사항을 내 로컬 저장소에 적용하는 명령어입니다.
이 과정은 2단계로 이루어집니다. git pull
명령어는 내부적으로 git fetch
명령어를 통해 원격 저장소의 변경사항을 로컬로 가져와 임시 저장하고,
git merge 명령어를 통해 이러한 변경사항을 로컬 저장소에 병합합니다.
원격 저장소의 커밋이 단지 앞서있을 뿐이라면 fast-forward 전략으로 쉽게 pull이 완료됩니다. 그러나 git merge 포스트에서 알 수 있듯이, merge 단계에서 conflict가 발생할 수 있으며, 이 때 충돌을 해결하는 선택권은 개발자에게 주어집니다. 옵션에 따라 merge 대신 rebase 전략을 사용할 수도 있습니다.
사용 방법은 간단합니다. 원격 저장소의 특정 브랜치의 변경사항을 가져올 때는 remote 이름과 branch 이름을 명시해줍니다.
$ git pull origin main
만약 아래와 같이 원격 저장소 이름과 브랜치 이름을 작성하지 않으면, 현재 작업 브랜치에 따라, 설정값에 따라 의도치 않은 결과를 가져올 수 있습니다. 되도록 위의 명령어처럼 원격 저장소, 브랜치 이름을 함께 사용하도록 합니다.
$ git pull
이제, 각각의 상황에 따른 git pull
실행 결과를 보겠습니다.
1.1. fast-forward 전략인 경우
git pull
에서 fast-forward 전략은 원격 브랜치가 로컬 브랜치의 마지막 커밋에서 몇 커밋 앞서 있는 상태를 의미합니다.
예를 들어, 로컬 브랜치가 커밋 A <- 커밋 B
, 원격 브랜치가 커밋 A <- 커밋 B <- 커밋 C
를 가진 경우, git pull
은 원격 브랜치의 커밋 C
만 받아와
로컬 브랜치에 붙이면 됩니다.
이러한 경우에, 따로 병합이 필요하지 않기 때문에, 병합 과정이 일시 중지되지 않으며, 별도의 merge commit도 생성할 필요가 없습니다. 실제 실행 결과를 보겠습니다.
출력 메시지를 보면, fast-forward 전략을 사용했으며, git pull
과정이 자동으로 마무리되었습니다.
1.2. merge conflict 해결이 필요한 경우
원격 브랜치와 로컬 브랜치 간에 충돌되는 라인이 있다면 git pull
특히 그 중 merge 과정이 일시 중지됩니다. 예를 들어, 원격 브랜치와 로컬 브랜치가 각각 다른 변경사항으로 새로운 커밋을 했을 때가 대표적입니다.
이렇게 되면, git fetch 만 실행된 상태에서 우리가 merge 단계를 마무리해야 합니다. merge conflict 를 해결하는 방법은 이미 git merge 포스트에서 알아보았기 때문에 바로 실행 결과를 보겠습니다.
pull 하는 과정에서 merge conflict 가 발생했고, 이를 해결한 후 커밋을 한 것을 로그에서 확인할 수 있습니다.
2. git fetch 와의 차이
위에서 잠깐 언급했지만, git pull
명령어는 git fetch
명령어를 포함합니다.
git fetch
명령어는 현재 브랜치가 트래킹하고 있는 원격 브랜치의 모든 정보를 가져와 저장합니다.
원격 브랜치는 대개 origin/main
의 이름 형식을 가지며, git branch 포스트에서 보았듯 git branch -r
명령어로 조회할 수 있습니다.
다음의 명령어를 사용합니다.
$ git fetch [remote-name]
실행 결과는 다음과 같습니다.
실행 결과를 보면, 위에서 git pull
실행 결과에 출력되는 메시지 중 일부가 fetch
명령어를 통해 출력된 것임을 알 수 있습니다.
fetch
명령어를 입력했기 때문에, 원격 브랜치의 히스토리도 다음과 같이 조회할 수 있습니다.
3. --force 옵션
git pull
명령어와 사용할 수 있는 --force
옵션은 사실 git fetch
명령어의 옵션입니다.
이 옵션은 커밋 삭제 등의 이유로 원격 브랜치와 로컬 브랜치의 히스토리가 맞지 않을 때 사용합니다.
이러한 경우에는 git fetch
가 거부되지만, --force
옵션을 사용하면 현재 원격 브랜치의 커밋 히스토리를 강제로 로컬 트래킹 브랜치에 덮어씁니다.
다만, 이 옵션은 merge 단계에 적용되는 옵션이 아니기 때문에 강제로 fetch를 했더라도, 현재 브랜치와 충돌된다면 수동으로 merge conflict 해결이 필요합니다.
다음의 명령어로 사용합니다.
$ git fetch --force [remote-name]
혹은
$ git pull --force [remote-name] [branch-name]
4. --rebase 옵션
git pull
명령어는 기본적으로 merge 전략을 기반으로 동작합니다.
--rebase
옵션은 git fetch
이후에 merge 대신에 git rebase 명령어를 수행하도록 하는 옵션입니다.
merge와 rebase의 차이는 여기에 정리해두었습니다.
rebase를 이용해 pull 하면 원격 브랜치와 로컬 브랜치의 공통 조상에 원격 브랜치의 커밋을 붙이고 이 마지막 커밋에 로컬 브랜치 커밋들을 붙입니다. 이 과정에서 커밋 히스토리가 변경되기 때문에 주의해서 사용해야 합니다.
사용을 위해선 아래 명령어를 입력합니다.
$ git pull --rebase <remote-name> <branch-name>
5. git pull 기타 옵션들
5.1. --no-commit 옵션
$ git pull --no-commit
이 옵션은 git pull
결과를 한 번 더 검토하거나 조작을 추가하고자 할 때 사용합니다. 이 옵션을 사용하면, git pull
과정이 commit 직전에 멈추게 됩니다. 여기서 원하는 새로운 조작을 추가한 후, 수동으로 커밋해서 pull 명령을 완료하게 됩니다.
5.2. --ff-only 옵션
$ git pull --ff-only
이 옵션은 오직 fast-forward 상황일 때만, pull 이 완료되도록 강제합니다. 그 외의 상황에서는 pull 이 거절됩니다. 이 옵션을 통해, 내 로컬 브랜치가 실수로라도 수정이 가해지지 않은 상태이며, 원격 브랜치만 온전히 복사한 상태임을 보장할 수 있습니다.
5.3. --depth 옵션
$ git pull --depth [depth-number]
이 옵션은 많은 다른 명령어에서도 자주 보이는 옵션으로, 원하는 수의 커밋만 pull 할 수 있도록 합니다. 원격 브랜치의 전체 커밋 히스토리가 필요하지 않은 경우에, 전송의 오버헤드나 용량 등을 아낄 수 있습니다.
5.4. --prune 옵션
$ git pull --prune
이 옵션은 현재 원격 저장소에 존재하지 않는 브랜치를 트래킹하는 로컬 브랜치들을 정리하는 역할을 합니다. 원격 저장소가 source of truth 임을 확신할 수 있다면, 이 옵션을 사용해서 로컬 Git 환경을 더 깔끔하게 정리할 수 있습니다.
5.5. --autostash 옵션
$ git pull --autostash
이 옵션은 현재 작성중이던 변경사항을 자동으로 stash, 즉 임시 저장하고 pull 을 진행하도록 합니다. stash한 변경사항은 pull 완료 후 다시 자동으로 돌아옵니다. 아직 커밋하기 전 pull을 해야 하는 경우에, 유용하게 사용할 수 있습니다.
6. 마치며
git pull
명령어는 원격 저장소와 로컬 저장소의 동기화를 위한 중요한 명령어 중 하나입니다.
git pull
명령어를 사용하면서 쉽게 트러블슈팅하는 방법 중 하나는 pull을 언제나 fetch 와 merge 혹은 rebase, 2가지 단계로 생각하는 것입니다.
git merge
명령어에 대한 더 자세한 설명은 이 포스트를 참고하세요.