17. 포크(Fork)와 오픈소스 기여

🎯 이 장의 목표
  • 포크가 무엇이고 협업자 방식과 어떻게 다른지 이해한다
  • 포크한 저장소를 원본(upstream)과 동기화한다
  • 포크에서 변경을 만들어 원본에 PR을 보낸다
  • 오픈소스 기여의 전체 흐름을 익힌다

17.1 포크란 무엇인가?

16장에서는 협업자(collaborator)로 초대받아야 저장소에 push할 수 있었습니다. 하지만 세상 모든 오픈소스 프로젝트가 나를 협업자로 초대해줄 리는 없습니다. 권한이 없는 남의 저장소에 어떻게 기여할까요?

포크(Fork)는 남의 저장소를 통째로 내 GitHub 계정으로 복사해오는 것입니다. 복사본은 완전히 내 것이라 마음대로 수정·push할 수 있고, 다 되면 원본에 "이 변경 받아주세요"라고 PR을 보냅니다.

flowchart LR
    O["📦 원본 저장소<br/>octocat/repo<br/><small>권한 없음</small>"]
    F["🍴 내 포크<br/>나/repo<br/><small>내 소유·자유 수정</small>"]
    L["💻 내 로컬<br/><small>자유 작업</small>"]
    O -- "Fork" --> F
    F -- "clone" --> L
    L -. "Pull Request<br/>(변경 받아달라 요청)" .-> O
    classDef origin fill:#fee2e2,stroke:#dc2626,color:#991b1b
    classDef fork fill:#fef3c7,stroke:#d97706,color:#92400e
    classDef local fill:#dcfce7,stroke:#16a34a,color:#166534
    class O origin
    class F fork
    class L local

포크 vs 협업자 — 무엇이 다른가?

협업자(Collaborator)포크(Fork)
권한원본에 직접 push 가능원본 권한 없음
작업 위치원본 저장소의 브랜치내 계정의 복사본
적합한 상황같은 팀·회사 내부오픈소스, 외부 기여
💡 팁
요약: 내부 팀원이면 협업자 방식, 외부에서 오픈소스에 기여하면 포크 방식.

17.2 포크 만들기

  1. 기여하려는 GitHub 저장소 페이지에서 우측 상단 Fork 버튼 클릭
  2. 내 계정으로 복사본이 생성됨 (내아이디/저장소명)
  3. 그 포크를 로컬로 clone
BASH
git clone https://github.com/내아이디/저장소명.git
cd 저장소명

또는 12장의 gh로 포크와 clone을 한 번에:

BASH
gh repo fork octocat/repo --clone

17.3 origin과 upstream — 두 개의 원격

포크를 clone하면 origin내 포크를 가리킵니다. 하지만 시간이 지나면 원본 저장소에 새 변경이 쌓입니다. 내 포크가 뒤처지지 않으려면 원본도 원격으로 등록해야 합니다. 관례적으로 이 원본을 upstream이라 부릅니다.

BASH
# 현재 원격 확인 (origin = 내 포크)
git remote -v
CODE
origin  https://github.com/내아이디/저장소명.git (fetch)
origin  https://github.com/내아이디/저장소명.git (push)
BASH
# 원본 저장소를 upstream으로 추가
git remote add upstream https://github.com/원본소유자/저장소명.git

git remote -v
CODE
origin    https://github.com/내아이디/저장소명.git      (내 포크 - push 가능)
upstream  https://github.com/원본소유자/저장소명.git    (원본 - 보통 읽기만)
flowchart LR
    U["📦 upstream<br/>(원본)"]
    L["💻 내 로컬"]
    O["🍴 origin<br/>(내 포크)"]
    U -- "fetch / pull" --> L
    L -- "push" --> O
    classDef up fill:#fee2e2,stroke:#dc2626,color:#991b1b
    classDef local fill:#dcfce7,stroke:#16a34a,color:#166534
    classDef ori fill:#fef3c7,stroke:#d97706,color:#92400e
    class U up
    class L local
    class O ori

17.4 원본 변경 동기화하기

원본(upstream)에 새 커밋이 생겼을 때, 내 포크를 따라잡는 방법입니다.

BASH
# 1. 원본의 최신 변경 가져오기
git fetch upstream

# 2. 내 main으로 이동
git switch main

# 3. 원본 main의 변경을 내 main에 병합
git merge upstream/main

# 4. 동기화된 내 main을 내 포크(origin)에도 반영
git push origin main
💡 팁
GitHub 포크 페이지에는 "Sync fork" 버튼이 있어, 웹에서 클릭 한 번으로 원본과 동기화할 수도 있습니다. 하지만 로컬에서 fetch upstreammerge를 직접 해보면 무슨 일이 일어나는지 명확히 이해됩니다.
📌 핵심
기여 전 항상 동기화부터. 원본이 한참 앞서 있는 상태에서 작업하면 나중에 충돌이 잔뜩 납니다. 새 작업을 시작하기 전에 먼저 upstream과 동기화하는 습관을 들이세요.

17.5 포크에서 기여하기 (전체 흐름)

오픈소스에 기여하는 표준 절차입니다.

BASH
# 0. (사전) 원본과 동기화
git fetch upstream
git switch main
git merge upstream/main

# 1. 작업용 브랜치 생성 (main에서 직접 작업하지 않기!)
git switch -c fix/typo-in-readme

# 2. 변경 작업 후 커밋
# ... 파일 수정 ...
git add .
git commit -m "Fix typo in README"

# 3. 내 포크(origin)에 브랜치 push
git push -u origin fix/typo-in-readme
  1. 원본에 PR 보내기: GitHub의 내 포크 페이지에서 "Contribute → Open pull request"를 누르면, base는 원본 저장소, compare는 내 포크의 브랜치로 자동 설정됩니다. 설명을 적고 PR을 생성합니다.
BASH
# gh로도 가능 (12장)
gh pr create --repo 원본소유자/저장소명 --fill
  1. 원본 관리자가 리뷰 → 승인 → 병합. 내 변경이 원본에 반영되면 기여 완료! 🎉
⚠️ 흔한 실수
흔한 실수: main에서 직접 작업하지 마세요. 포크의 main은 원본과 동기화하는 용도로 깨끗이 두고, 모든 기여는 별도 브랜치에서 하세요. 그래야 동기화가 꼬이지 않고, 여러 기여를 동시에 진행할 수 있습니다.

17.6 좋은 오픈소스 기여 매너

  • CONTRIBUTING.md를 먼저 읽기: 많은 프로젝트가 기여 규칙을 문서로 둡니다.
  • 작은 단위로 기여: 거대한 PR보다 명확한 작은 PR이 환영받습니다.
  • 이슈 먼저 확인: 큰 변경은 PR 전에 이슈로 제안해 합의하는 게 예의입니다.
  • 커밋 메시지·PR 설명을 정성껏: 관리자가 맥락을 이해해야 병합됩니다.
  • 리뷰 피드백에 열린 태도: 수정 요청은 거절이 아니라 협업입니다.

17.7 이 장에서 배운 것 (요약)

  • 포크 = 남의 저장소를 내 계정으로 복사 (권한 없이 기여하는 길)
  • 협업자(내부) vs 포크(외부 오픈소스)
  • origin = 내 포크, upstream = 원본 (직접 추가해야 함)
  • 동기화: git fetch upstreamgit merge upstream/maingit push origin main
  • 기여: 동기화 → 브랜치 생성 → 커밋 → 포크에 push → 원본에 PR
  • 포크의 main은 동기화용으로 깨끗이, 작업은 항상 별도 브랜치

✍️ 확인 문제

  1. 오픈소스 프로젝트에 권한 없이 기여하려면 협업자와 포크 중 무엇을 쓰나요?
  2. originupstream은 각각 무엇을 가리키나요?
  3. 포크에서 기여할 때 main에서 직접 작업하면 안 되는 이유는?
다음 장에서는 cherry-pick, squash, amend 등 한 단계 위의 고급 명령들을 모아 다룹니다. → 18_고급명령모음.md