Git 원격 저장소와 협업: push·pull·fetch 차이, Fork와 Pull Request 흐름
이 글의 핵심
원격 저장소가 무엇인지, push·pull·fetch가 각각 무엇을 하는지, GitHub에서 Fork와 Pull Request로 협업하는 흐름과 충돌 후 안전하게 push하는 방법을 정리합니다.
[Git 실전 가이드 #3] 원격 저장소와 협업
이전 글 Git 브랜치와 병합(#2)에서 브랜치 생성, 병합, 충돌 해결을 다뤘습니다.
원격 저장소(remote)는 GitHub, GitLab 같은 서버에 있는 Git 저장소입니다. 로컬에서 만든 커밋을 원격에 올리는 것이 push, 원격의 새 커밋을 가져와 현재 브랜치에 합치는 것이 pull입니다. 팀원과 같은 이력을 맞추려면 이 두 동작을 번갈아 쓰게 됩니다. 이 글에서는 remote 추가와 clone, push·pull·fetch의 차이, GitHub에서 Pull Request(PR)로 협업하는 흐름, 충돌을 해결한 뒤 안전하게 push하는 방법까지 정리합니다.
원격 저장소란
로컬 vs 원격
- 로컬 저장소: 내 PC의
.git폴더입니다.git commit은 여기에만 반영됩니다. - 원격 저장소: GitHub, GitLab, Bitbucket 같은 서버에 있는 저장소입니다. 로컬 커밋을 push로 올리면 팀원이 pull이나 clone으로 받을 수 있습니다.
push는 로컬 브랜치의 커밋을 원격 브랜치에 올리는 것이고, pull은 원격 브랜치의 최신 커밋을 가져와 현재 로컬 브랜치에 합치는 것입니다. 협업할 때는 작업 전에 pull로 최신 상태를 맞추고, 작업 후에 push로 올리는 습관이 중요합니다.
원격 추가·확인·clone
원격 저장소 추가
로컬 저장소에 원격 저장소를 연결합니다.
git remote add origin https://github.com/username/repo.git
# git remote add: 원격 저장소 추가 명령
# origin: 원격 저장소의 별칭 (관례적으로 origin 사용)
# https://github.com/username/repo.git: 원격 저장소 URL
#
# 동작:
# .git/config 파일에 아래 내용이 추가됨:
# [remote "origin"]
# url = https://github.com/username/repo.git
# fetch = +refs/heads/*:refs/remotes/origin/*
origin은 원격 저장소에 붙이는 관례적인 기본 이름일 뿐이고, git remote add upstream https://...처럼 다른 이름으로 여러 원격을 추가할 수도 있습니다.
새 프로젝트를 처음 올리는 흐름은 다음과 같습니다.
# 1. 로컬 저장소 초기화
git init
# 2. 원격 저장소 추가
git remote add origin https://github.com/myuser/myproject.git
# 3. 첫 커밋 후 push
git add .
git commit -m "Initial commit"
git branch -M main # 기본 브랜치 이름이 master인 환경이면 main으로 변경
git push -u origin main
# -u: 추적 관계 설정 (다음부터 git push만 입력 가능)
git init이 만드는 첫 브랜치 이름은 init.defaultBranch 설정에 따라 master일 수도 있습니다. 그 상태에서 git push -u origin main을 하면 “src refspec main does not match any” 에러가 나므로, 위처럼 브랜치 이름을 먼저 맞춥니다.
원격 목록·URL 확인
git remote -v
# origin https://github.com/username/repo.git (fetch)
# origin https://github.com/username/repo.git (push)
-v로 각 remote의 이름과 URL을 확인할 수 있습니다. URL을 바꾸려면 git remote set-url origin <새URL>을 씁니다.
저장소 복제(clone)
원격 저장소를 로컬로 복사합니다.
git clone https://github.com/username/repo.git
# git clone: 원격 저장소를 로컬로 복제
# https://github.com/username/repo.git: 복제할 원격 저장소 URL
#
# 동작:
# 1. "repo" 폴더 생성
# 2. 원격 저장소의 모든 커밋 히스토리 다운로드
# 3. 기본 브랜치(main/master)를 체크아웃
# 4. origin 원격 저장소 자동 설정
# 5. 추적 관계 자동 설정 (origin/main → main)
cd repo
# 복제된 폴더로 이동
clone한 직후 상태를 확인해 보면 다음과 같습니다.
# 원격 저장소 확인
git remote -v
# origin https://github.com/username/repo.git (fetch)
# origin https://github.com/username/repo.git (push)
# → origin이 자동으로 설정되어 있음
# 브랜치 확인
git branch -vv
# * main 1a2b3c4 [origin/main] Initial commit
# → main 브랜치가 origin/main을 추적하도록 설정됨
clone은 아래의 수동 과정을 한 번에 해 주는 명령입니다.
# 방법 1: clone (간편, 권장)
git clone https://github.com/username/repo.git
# 한 번에 모든 설정 완료
# 방법 2: init + remote add (수동)
mkdir repo
cd repo
git init
git remote add origin https://github.com/username/repo.git
git fetch origin
git checkout -b main origin/main
# 여러 단계 필요, 실수 가능성 있음
자주 쓰는 clone 옵션입니다.
# 특정 브랜치를 체크아웃한 상태로 clone (다른 브랜치 이력도 함께 받음)
git clone -b develop https://github.com/username/repo.git
# 그 브랜치만 받으려면 --single-branch 추가
git clone -b develop --single-branch https://github.com/username/repo.git
# 얕은 clone (최근 커밋만, 빠름)
git clone --depth 1 https://github.com/username/repo.git
# 특정 폴더 이름으로 clone
git clone https://github.com/username/repo.git my-project
push: 로컬 → 원격 올리기
기본 push
git push -u origin main
origin은 원격 저장소 이름이고, main은 올릴 로컬 브랜치 이름입니다. “origin의 main 브랜치에 로컬 main을 올린다”는 뜻입니다. -u(--set-upstream)는 로컬 main이 origin/main을 추적하도록 기록해서, 다음부터는 git push만 입력해도 같은 곳으로 올라가게 합니다. 브랜치마다 처음 한 번만 붙이면 됩니다. 다른 브랜치를 처음 올릴 때는 git push -u origin feature/login처럼 브랜치 이름을 지정합니다.
push가 거부될 때
원격 브랜치에 내 로컬에는 없는 커밋이 있으면 push가 거부됩니다. 그대로 올리면 원격의 커밋이 사라지기 때문입니다. 이때는 git pull(또는 git pull --rebase)로 원격 변경을 먼저 받아 합친 뒤 다시 push합니다.
git pull origin main
git push origin main
pull과 fetch: 원격 → 로컬 가져오기
git pull
pull은 원격 저장소의 변경을 가져와 현재 브랜치에 바로 합칩니다. fetch와 merge(설정에 따라 rebase)를 한 번에 하는 명령입니다.
git pull origin main
현재 브랜치가 main이라면 origin의 main 최신 커밋을 가져와 로컬 main에 병합합니다. 작업을 시작하기 전에 한 번 pull해 두면 나중에 충돌이 줄어듭니다.
git fetch
fetch는 원격의 최신 이력을 가져오기만 하고 현재 브랜치는 바꾸지 않습니다. 가져온 내용은 origin/main 같은 원격 추적 브랜치에만 반영됩니다.
git fetch origin
git log origin/main # 원격 main 상태 확인
git merge origin/main # 필요할 때 병합
원격 상태를 먼저 확인하고 병합할지 나중에 정하고 싶을 때는 fetch를, 지금 바로 현재 브랜치를 최신으로 맞추려면 pull을 쓰면 됩니다.
push·pull·fetch 비교
| 동작 | 명령 | 설명 |
|---|---|---|
| 로컬 → 원격 | git push origin main | 로컬 main 커밋을 원격 main에 올림 |
| 원격 → 로컬(병합까지) | git pull origin main | 원격 main을 가져와 현재 브랜치에 병합 |
| 원격 → 로컬(가져오기만) | git fetch origin | 원격 이력을 origin/main 등에만 반영 |
협업에서 자주 쓰는 변형인 git pull --rebase와 git push --force-with-lease는 아래 실전 명령어에서 다룹니다.
GitHub 협업과 Pull Request
PR이란?
Pull Request(PR)는 내 브랜치의 변경을 main(또는 develop)에 반영해 달라고 요청하는 것입니다. GitHub와 GitLab(GitLab에서는 Merge Request라고 부름)에서는 코드 리뷰, 토론, CI 결과 확인을 PR 안에서 처리합니다.
기본 흐름
- 로컬에서 feature 브랜치를 만들고 작업한 뒤 커밋합니다.
git push -u origin feature/login으로 브랜치를 원격에 올립니다.- GitHub에서 “Compare & pull request” 버튼을 눌러
base: main ← compare: feature/login으로 PR을 만듭니다. - 리뷰를 받고 수정한 뒤 Merge하면 main에 반영됩니다.
- 병합된 feature 브랜치는 원격과 로컬에서 삭제하고, main을 pull해 최신으로 맞춥니다.
이렇게 하면 main에는 리뷰를 거친 변경만 들어가고, 이력이 PR 단위로 남아 나중에 추적하기 쉽습니다.
원격 저장소 심화: origin·여러 remote·SSH와 HTTPS
origin이란 무엇인가
origin은 Git이 특별하게 취급하는 이름이 아니라, 기본으로 쓰는 원격을 가리키기 위해 관례적으로 붙이는 별칭입니다. git clone을 하면 복제해 온 원격이 origin이라는 이름으로 .git/config에 기록됩니다.
- 실제 URL은
git remote -v로 보거나,git remote get-url origin으로 URL만 볼 수 있습니다. - 이름은
git remote rename origin company처럼 바꿀 수 있지만, 대부분의 도구와 문서가origin을 가정하므로 특별한 이유가 없으면 그대로 두는 편이 편합니다.
여러 원격 저장소 관리: upstream과 fork
한 프로젝트에 여러 원격을 둘 수 있으며, 대표적인 경우가 fork입니다. GitHub에서 원본 저장소를 내 계정으로 fork한 뒤 로컬에서는 origin을 내 fork로 두고, 원본 저장소는 관례적으로 upstream이라는 이름으로 추가합니다.
# 실행 예제
git remote add upstream https://github.com/original-owner/awesome-project.git
git fetch upstream
git checkout main
git merge upstream/main
# 또는 팀 정책에 따라: git rebase upstream/main
사내에서는 메인 저장소와 미러, 레거시 저장소를 함께 쓰면서 backup, vendor처럼 이름을 나누기도 합니다. 중요한 것은 어느 remote에 push하면 어떤 결과가 나는지를 팀이 같은 뜻으로 이해하는 것입니다.
SSH vs HTTPS 인증
| 구분 | HTTPS | SSH |
|---|---|---|
| URL 예 | https://github.com/user/repo.git | [email protected]:user/repo.git |
| 인증 | PAT(Personal Access Token)나 자격 증명 헬퍼 | GitHub에 등록한 SSH 공개키 |
| 장점 | 443 포트라 회사 방화벽을 통과하기 쉬움 | 키를 한 번 등록하면 입력이 필요 없고 스크립트에서 쓰기 편함 |
GitHub는 HTTPS로 push할 때 계정 비밀번호를 받지 않으므로, PAT를 만들거나 Git Credential Manager 같은 헬퍼를 써야 합니다. 기존 저장소의 방식을 바꾸려면 git remote set-url origin <새 URL>로 URL만 바꾸면 됩니다.
협업 워크플로우: Fork·PR·리뷰·충돌 후 push
Fork와 Pull Request 전체 과정
- GitHub에서 원본 저장소를 fork해 내 계정에 복사본을 만듭니다.
- fork 저장소 URL로
git clone하고,git remote add upstream <원본 URL>로 원본을 추가합니다. - feature 브랜치를 만들어 작업한 뒤
git push -u origin feature/설명으로 내 fork에 올립니다. 같은 저장소 안에서 협업한다면 fork 없이origin에 feature 브랜치만 올리면 됩니다. - PR을 열 때 base(합쳐질 쪽, 보통 원본의 main)와 compare(내 변경 브랜치)를 지정합니다. fork에서 기여할 때는 base가 원본 저장소, compare가 fork의 브랜치입니다.
- 리뷰 피드백을 반영해 같은 브랜치에 push하면 PR이 자동으로 갱신됩니다.
- 승인 후 Merge되면, 로컬에서는
git checkout main후 upstream에서 가져와 정리합니다.
리뷰를 수월하게 만드는 습관
- 변경 범위를 작게 나눌수록 리뷰가 빠르고 정확해집니다.
- PR 본문에는 무엇을 왜 바꿨는지, 재현 방법과 위험 요소를 적어 리뷰어가 맥락을 추측하지 않게 합니다.
- 코멘트는 지적보다 질문과 제안의 형태로 쓰고, 해결된 스레드는 resolve합니다.
- 팀이 정한 최소 승인 수, 필수 리뷰어, CODEOWNERS 규칙이 있으면 따릅니다.
충돌 해결 후 force push 주의사항
PR 브랜치를 최신 main 위로 rebase하면 커밋이 새로 만들어지므로, 이미 원격에 올린 브랜치를 갱신하려면 git push --force-with-lease가 필요합니다.
--force-with-lease는 “원격 브랜치가 내가 마지막으로 본 상태(로컬의 origin/브랜치)와 같을 때만 덮어쓴다”는 조건이 붙어 있어 --force보다 안전합니다. 다만 IDE 등이 백그라운드에서 git fetch를 해 원격 추적 브랜치가 최신으로 갱신되어 있으면, 동료가 올린 커밋을 본 적이 없는데도 조건을 통과해 덮어쓸 수 있습니다. Git 2.30 이상이라면 --force-if-includes를 함께 주면, 원격의 커밋이 내 로컬 이력에 실제로 포함되어 있을 때만 push가 허용되어 이 위험이 줄어듭니다.
main, develop 같은 공유 브랜치에는 force push를 하지 않는 것이 원칙이며, 보통 브랜치 보호 규칙으로 아예 막습니다. 공유 브랜치의 충돌은 merge 커밋으로 해결하면 일반 git push로 충분합니다.
실전 명령어: rebase pull·force-with-lease
git fetch vs git pull
git fetch는 원격의 최신 커밋과 브랜치 정보를 가져와origin/main같은 원격 추적 브랜치에만 반영합니다. 현재 체크아웃한 브랜치는 바꾸지 않습니다.git pull은 fetch 후 현재 브랜치에 병합(또는 rebase)까지 합니다. 지금 작업 중인 브랜치를 원격과 맞추겠다는 의도가 분명할 때 씁니다.
병합 시점을 직접 정하고 싶다면 fetch 후 git log HEAD..origin/main으로 새로 들어온 커밋을 확인하고 merge나 rebase를 고르면 됩니다.
git pull —rebase
git pull --rebase(또는git pull --rebase origin main)는 가져온 원격 커밋 위에 내 로컬 커밋을 다시 쌓습니다. 머지 커밋 없이 이력을 한 줄로 유지하고 싶을 때 씁니다.- rebase는 커밋을 새로 만들기 때문에, 이미 push해서 다른 사람이 받아 간 커밋을 rebase하면 이력이 갈라집니다. 아직 push하지 않은 로컬 커밋을 정리할 때 가장 잘 맞습니다.
- 매번 옵션을 붙이기 번거로우면
git config pull.rebase true로 기본 동작을 바꿀 수 있습니다. 이 설정은 개인 설정이라 팀원마다 pull 결과가 달라질 수 있으므로, 팀에서 방식을 통일해 두는 편이 좋습니다.
git push —force-with-lease
- 예:
git push --force-with-lease origin feature/my-work - 원격 브랜치가 내가 기대하는 커밋과 다르면 push가 거부되어, 그 사이에 다른 사람이 올린 커밋을 덮어쓰는 사고를 줄여 줍니다.
- 그래도 강제 push는 본인만 쓰는 feature 브랜치처럼 합의된 경우에만 쓰고, main에는 쓰지 않는 것이 기본입니다.
GitHub 기능: PR 템플릿·이슈 연동·Actions
Pull Request 템플릿
저장소 루트, .github/, docs/ 중 한 곳에 pull_request_template.md를 두면 PR을 열 때 설명란이 자동으로 채워집니다(자세한 경로 규칙은 GitHub 문서 참고). “테스트 실행함”, “문서 수정함”, “호환성 깨지는 변경 여부” 같은 체크 항목을 넣어 두면 빠뜨리는 일이 줄어듭니다.
Issue 연동 (Closes #123)
PR 본문이나 커밋 메시지에 Closes #123, Fixes #45, Resolves #10 같은 키워드를 쓰면, PR이 저장소의 기본 브랜치에 병합될 때 해당 이슈가 자동으로 닫힙니다(키워드 목록). 기본 브랜치가 아닌 브랜치로 병합하면 닫히지 않는다는 점에 주의하세요. #이슈번호만 적어도 서로 링크가 걸려 추적하기 쉬워집니다.
GitHub Actions CI 기초
.github/workflows/ 아래에 YAML 워크플로를 두면 push나 pull_request 같은 이벤트마다 테스트, 빌드, 린트를 자동으로 실행할 수 있습니다.
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
run: echo "여기에 npm test, cargo test 등 실제 명령"
브랜치 보호 규칙에서 이 워크플로를 필수 상태 검사로 지정하면, CI가 통과하기 전에는 merge할 수 없게 강제할 수 있습니다.
팀 협업 규칙: 커밋 메시지와 브랜치 보호
커밋 메시지 규칙
많은 팀이 Conventional Commits 형식을 씁니다. feat(scope): 요약, fix(api): 타임아웃 처리, docs: README 갱신처럼 변경 종류를 앞에 붙이는 방식입니다. 한 커밋에 한 가지 의도만 담으면 리뷰와 되돌리기, cherry-pick이 쉬워집니다.
브랜치 보호 규칙
GitHub의 Settings → Branches(또는 Rulesets)에서 다음과 같은 규칙을 걸 수 있습니다.
- main에 직접 push 금지, Pull Request를 통해서만 병합
- 최소 승인 인원, CODEOWNERS 리뷰 필수
- 필수 상태 검사(CI 통과), 리뷰 대화가 모두 해결된 뒤에만 merge
- force push와 브랜치 삭제 금지
팀 규모와 배포 주기에 맞춰 하나씩 도입하는 경우가 많습니다.
자주 묻는 질문 (FAQ)
Q. git push가 “rejected” 될 때 어떻게 하나요?
A. 원격에 내 로컬에 없는 커밋이 있을 때 발생합니다. git pull origin main(또는 git pull --rebase origin main)으로 원격 변경을 받아 합친 뒤 다시 push하면 됩니다. pull 중에 충돌이 나면 브랜치와 병합(#2)의 충돌 해결 절차대로 처리합니다.
Q. push와 pull의 차이가 뭔가요?
A. push는 로컬 커밋을 원격에 올리는 것, pull은 원격 변경을 가져와 현재 브랜치에 병합하는 것입니다. 협업할 때는 먼저 pull로 최신화한 뒤 작업하고, 작업이 끝나면 push로 올리는 순서가 좋습니다.
Q. PR과 merge의 차이는?
A. merge는 브랜치를 합치는 Git 명령이고, PR은 GitHub나 GitLab 같은 서비스에서 “이 브랜치를 저 브랜치에 합쳐 달라”고 요청하고 리뷰와 승인을 거쳐 병합하기까지의 과정 전체를 말합니다.
협업 흐름 다이어그램
flowchart LR
subgraph local [로컬]
L[작업 브랜치]
end
subgraph remote [원격 origin]
R[원격 브랜치]
end
L -->|git push| R
R -->|git fetch / git pull| L
R -->|PR 생성·리뷰·Merge| M[기본 브랜치 반영]
로컬에서 push로 원격에 올리고, fetch나 pull로 최신 상태를 받으며, GitHub에서 PR과 리뷰를 거쳐 기본 브랜치에 합쳐지는 흐름입니다.
다음 글: Git 실전 가이드 #4: 되돌리기·rebase·정리 — reset, revert, rebase, 정리 이전 글: Git 실전 가이드 #2: 브랜치와 병합