사전 풀 리퀘스트
개념

풀 리퀘스트

gabury1고친 사람 github-actions[bot]

풀 리퀘스트는 내가 따로 고친 변경을 본래 코드에 합쳐 달라고 올리는 요청입니다. 요청 하나에 무엇이 바뀌었는지와 그 변경을 두고 오간 말이 함께 모입니다. 합치는 일은 다른 사람이 읽고 받아들인 다음에 일어납니다.

쉽고 빠른 이해

"이렇게 고쳤으니 봐 주고 합쳐 달라"고 올리는 쪽지입니다. 쪽지 한 장에 바뀐 파일과 그걸 두고 오간 말이 같이 붙어 있습니다.

이게 없으면 고친 것이 아무도 안 본 채로 본래 코드에 들어갑니다. 왜 그렇게 고쳤는지 물을 곳도 코드 밖 어딘가로 흩어집니다.

돌아가는 방식은 이렇습니다.

  1. 코드 이력을 한 줄기 갈라서 거기서만 고칩니다
  2. 갈라 둔 줄기를 가리키며 합쳐 달라는 요청을 엽니다
  3. 읽은 사람이 짚어 준 곳을 고치고, 받아들여지면 그때 합쳐집니다

대가가 있습니다. 읽어 줄 사람을 기다리는 동안 그 변경은 멈춰 있습니다. 기다리는 사이 본래 코드가 앞서 나가면 어긋난 곳을 다시 맞춰야 합니다.

상세

이 절은 풀 리퀘스트 하나가 무엇을 담고 어떤 순서로 열리고 닫히는지를 봅니다. 명령 한 줄이면 끝날 합치기를 굳이 요청으로 한 번 거치는 까닭도 같이 봅니다.

남의 문서를 고쳐 본 적이 있다면 그림이 쉽습니다. 원본을 복사해 내 사본에서 고친 다음, 원본을 맡은 사람에게 "이렇게 바꿨으니 원본에 반영해 주세요"라고 건네는 일입니다. 건네는 쪽은 원본을 직접 못 고치고, 반영할지 말지는 받는 쪽이 정합니다.

풀 리퀘스트는 한쪽 브랜치에 쌓인 변경을 다른 쪽 브랜치로 가져가 달라고 올리는 요청입니다. 브랜치는 파일과 그 이력을 담은 폴더, 곧 저장소 안에서 이력을 갈라 따로 쌓는 줄기입니다. 로그인 화면을 손보는 동안 본래 줄기를 건드리지 않으려고 하나 만들어 쓰는 것이 흔한 쓰임입니다.

브랜치에 쌓이는 낱개 단위는 커밋입니다. 커밋은 고친 것을 한 덩이로 묶어 이력에 남긴 기록이고, 요청이 가리키는 변경은 이 덩이들이 모인 것입니다.

이름에 붙은 "풀"은 당겨 간다는 뜻입니다. 미는 쪽이 아니라 받는 쪽이 당겨 가는 그림이라 이렇게 부릅니다. 저장소를 맡아 주는 서비스에 따라 머지 리퀘스트라고 부르기도 하는데, 가리키는 것은 같습니다.

왜 곧장 합치지 않나

두 브랜치를 합치는 일 자체는 명령 한 줄이면 끝납니다. 그런데도 요청을 한 번 거치는 까닭이 셋 있습니다.

첫째는 읽는 눈입니다. 합치기 전에 다른 사람이 변경을 한 번 읽고 고칠 곳을 짚어 줄 수 있습니다. 이 활동을 코드 리뷰라고 부릅니다.

둘째는 기록입니다. 왜 이렇게 고쳤는지가 변경 바로 옆에 남습니다. 몇 달 뒤 "이 줄이 왜 이렇게 돼 있지"를 물을 때 그 요청을 펴면 그때 오간 말이 그대로 있습니다.

셋째는 문지기입니다. 저장소에 쓸 권한이 없는 사람도 변경을 낼 수 있고, 받는 쪽은 조건을 걸어 둘 수 있습니다. 승인이 몇 개 모여야 합쳐지게 하거나, 자동 검사가 통과해야 합쳐지게 막는 식입니다.

요청 하나에 담기는 것

요청은 화면 하나에 여섯 가지를 모아 놓은 묶음입니다.

담는 것 무엇인가
출발 브랜치 내가 고친 변경이 쌓여 있는 쪽
도착 브랜치 그 변경을 받아 갈 쪽
변경 묶음 두 브랜치의 차이. 커밋 목록과 줄 단위 차이로 보인다
설명 무엇을 왜 바꿨는지 제안자가 쓴 글
검토 기록 읽은 사람이 남긴 말과 거기 달린 답
상태 열림 · 병합됨 · 닫힘 중 하나

표에서 눈여겨볼 것은 세 번째 줄입니다. 변경 묶음은 파일 사본이 아니라 두 브랜치의 차이입니다. 그래서 출발 브랜치에 커밋을 더 쌓으면 같은 요청 안에서 변경 묶음이 따라 바뀝니다. 요청을 새로 열 필요가 없습니다.

열리고 닫히기까지

한 요청이 도는 순서는 이렇습니다.

sequenceDiagram
    participant 제안자
    participant 저장소
    participant 검토자
    제안자->>저장소: 고친 브랜치를 올린다
    제안자->>저장소: 풀 리퀘스트를 연다
    저장소->>검토자: 읽어 달라고 알린다
    검토자-->>제안자: 고칠 곳을 짚는다
    제안자->>저장소: 커밋을 더 쌓는다
    Note over 제안자,검토자: 짚을 곳이 없으면 바로 승인으로 간다
    검토자-->>저장소: 승인한다
    저장소->>저장소: 두 브랜치를 합친다

가운데 두 화살표는 한 번으로 끝나지 않습니다. 짚고 고치는 왕복이 몇 번이고 되풀이될 수 있고, 그동안 요청은 계속 열려 있습니다.

마지막 줄의 합치기는 대개 사람이 아니라 저장소를 맡은 서비스가 합니다. 제안자가 자기 컴퓨터에서 합쳐 올리는 것이 아니라, 승인이 끝난 요청을 그 서비스가 대신 합칩니다. 두 브랜치의 이력을 하나로 잇는 이 일을 병합이라고 부릅니다.

상태는 셋 중 하나다

상태는 셋입니다. 요청은 열림에서 시작해 병합됨이나 닫힘으로 끝납니다.

stateDiagram-v2
    [*] --> 열림
    열림 --> 병합됨: 승인 뒤 합친다
    열림 --> 닫힘: 합치지 않고 접는다
    닫힘 --> 열림: 다시 연다
    병합됨 --> [*]

닫힘이 거절만 뜻하지는 않습니다. 다른 요청이 같은 일을 먼저 했거나, 하려던 방향이 바뀌어 접는 경우도 닫힘입니다. 닫은 요청은 대개 다시 열 수 있습니다.

병합됨은 되돌리는 문이 없습니다. 이미 합친 변경을 무르려면 그 변경을 되돌리는 새 요청을 하나 더 내야 합니다.

쓰기 권한이 없는 사람이 낼 때

여기까지는 같은 저장소 안에서 브랜치를 갈라 요청을 여는 그림이었습니다. 그런데 저장소에 쓸 권한이 없으면 브랜치조차 못 만듭니다.

그때는 저장소를 통째로 복사해 내 몫으로 하나 만듭니다. 이 복사본을 포크라고 합니다. 포크 안에서는 내가 주인이니 브랜치를 만들고 커밋을 쌓을 수 있습니다.

flowchart TD
    subgraph 원본["원본 저장소 · 나는 못 쓴다"]
        M["기준 브랜치"]
    end
    subgraph 내포크["내 포크 · 내가 쓴다"]
        F["고친 브랜치"]
    end
    M -->|복사해 온다| F
    F -->|풀 리퀘스트를 건다| M

요청은 내 포크의 브랜치에서 원본 저장소의 브랜치로 겁니다. 받는 쪽은 원본에 손댈 권한을 나눠 주지 않고도 변경만 받아 갈 수 있습니다. 밖에서 온 사람이 오픈 소스 프로젝트에 변경을 내는 길이 이것입니다.

언제 거치고 언제 건너뛰나

혼자 쓰는 저장소라면 요청을 열 이유가 적습니다. 읽어 줄 사람이 없으니 요청은 나 자신에게 보내는 쪽지가 됩니다. 이럴 때는 본래 브랜치에 곧장 커밋하는 편이 흔합니다.

여럿이 같은 저장소를 쓰면 이야기가 달라집니다. 남이 짠 코드가 예고 없이 들어오는 것을 막으려고 본래 브랜치에 직접 푸시하는 길을 아예 잠가 두는 팀이 많습니다. 그러면 요청이 유일한 통로가 됩니다.

요청을 얼마나 크게 낼지도 선택입니다. 한 요청에 담긴 변경이 크면 읽는 사람이 한 번에 봐야 할 줄이 늘고, 그만큼 요청이 열려 있는 기간도 길어집니다. 그 사이 도착 브랜치가 앞서 나가면 어긋난 곳을 다시 풀어야 합니다. 이 되풀이를 줄이려고 변경을 잘게 쪼개 자주 내는 방식이 트렁크 기반 개발 같은 협업 규칙에 담겨 있습니다.

관련 항목

풀 리퀘스트를 올리기까지 거치는 단계

클론 · 브랜치 · 커밋 · 푸시 · 포크

풀 리퀘스트를 받아들일 때 하는 병합

병합 · 빨리 감기 병합 · 스쿼시 병합 · 리베이스 · 충돌 해소

풀 리퀘스트 안에서 오가는 검토 활동

코드 리뷰 · 코드 소유자 · 승인 규칙 · 커밋 메시지

풀 리퀘스트에 걸어 두고 돌리는 자동 검사

지속적 통합 · 빌드 파이프라인 · 자동 테스트 · 정적 분석 · 린터

풀 리퀘스트를 여는 방식을 정하는 협업 규칙

브랜치 전략 · 트렁크 기반 개발 · 통합 관리자 워크플로 · 기여 가이드 · 코드 리뷰 규칙

풀 리퀘스트가 올라가는 저장소와 서비스

원격 저장소 · Git · GitHub · GitLab · Bitbucket

다른 이름: PR · Pull Request · pull request · 풀리퀘스트 · 머지 리퀘스트