푸시
고친 사람 github-actions[bot]
푸시는 내 저장소에만 쌓여 있던 변경을 상대 저장소로 올려 보내는 일입니다. 보내는 쪽이 먼저 움직이고, 받는 쪽은 그 요청을 받아들이거나 거절합니다. 버전관리 밖에서도 보내는 쪽이 먼저 움직이는 전달을 푸시라고 부릅니다.
쉽고 빠른 이해
푸시는 내 컴퓨터에만 있던 작업 기록을 팀이 함께 쓰는 저장소로 올려 보내는 일입니다. 혼자 고친 코드를 동료가 받아 갈 수 있는 곳에 올려 두는 것이 그 일입니다.
올려 보내지 않으면 내 작업은 내 컴퓨터 밖으로 나가지 않습니다. 동료는 그 코드를 볼 수 없고, 검사와 배포를 대신 돌려 주는 자동화도 걸리지 않습니다.
어떻게 도나:
- 상대가 이미 무엇을 가지고 있는지 맞춰 봅니다
- 상대에게 없는 기록만 골라 보냅니다
- 상대 쪽 브랜치 이름이 방금 보낸 기록을 가리키게 바꿉니다
내 기록이 상대의 기록 위에 얹히지 않으면 요청이 거절됩니다. 그때는 상대 것을 먼저 받아 합친 다음 다시 보내야 합니다. 이 확인을 건너뛰고 밀어 넣으면 상대에게만 있던 기록이 이름에서 떨어져 나갑니다.
상세
이 절은 게시판 비유로 푸시를 먼저 떠올린 다음, 푸시가 무엇을 옮기는지 봅니다. 그다음 푸시가 도는 순서와 거절당하는 조건을 보고, 거절당한 뒤에 하는 일을 짚습니다. 마지막으로 버전관리 밖에서 같은 말이 어떤 뜻으로 쓰이는지 가릅니다.
게시판에 붙이러 가기
동아리 사람들이 각자 수첩에 회의 메모를 적는다고 해 봅시다. 수첩에만 적어 두면 남은 그 내용을 모릅니다. 복도 게시판에 붙여야 비로소 다른 사람이 읽습니다.
붙이러 갔더니 내가 수첩에 적는 사이에 누가 새 종이를 먼저 붙여 두었다고 해 봅시다. 내 종이를 그 위에 덮어 붙이면 먼저 붙은 내용이 가려집니다. 남의 종이를 읽고 내 메모와 합친 다음 붙이는 것이 순서입니다.
푸시가 옮기는 것
여럿이 한 프로젝트를 고치면 각자의 컴퓨터에 각자의 기록이 따로 쌓입니다. 내가 무엇을 언제 고쳤는지는 내 컴퓨터 안에만 있습니다. 그 기록을 한곳으로 보내 모으는 동작이 있어야 남이 내 작업을 받아 갈 수 있습니다.
푸시는 버전관리 도구에서 내 저장소의 새 커밋을 원격 저장소로 보내는 동작입니다. 커밋은 그때까지 고친 내용을 하나로 묶어 확정한 기록이고, 원격 저장소는 내 저장소 밖에 따로 있는 같은 프로젝트의 저장소입니다. 그래서 푸시로 건너가는 것은 파일의 최신 모습만이 아니라 그 파일이 거쳐 온 기록입니다.
보내는 일은 둘로 나뉩니다. 하나는 상대에게 없는 커밋을 실어 보내는 것입니다. 다른 하나는 상대 쪽 브랜치 이름이 방금 보낸 커밋을 가리키게 바꾸는 것입니다.
브랜치는 이력을 갈라 따로 이어 가는 한 줄기입니다. 줄기의 끝이 어느 커밋인지를 이름 하나가 가리킵니다. 그래서 브랜치 이름을 옮기는 일이 「이 줄기의 최신은 이제 이것」이라고 알리는 일이 됩니다.
둘째가 빠지면 커밋은 상대 저장소에 들어갔는데 아무도 그것을 못 찾습니다. 이름이 옮겨 가야 다음 사람이 받아 갈 때 새 커밋이 딸려 옵니다.
올리는 쪽은 상대 저장소에 붙여 둔 이름과 옮길 브랜치 이름을 함께 댑니다. 널리 쓰는 Git 에서는 이 동작을 아래 명령으로 부릅니다.
git push origin main # 원격 main 갱신
origin 이 상대 저장소에 붙여 둔 이름이고 main 이 옮길 브랜치입니다.
도는 순서
푸시는 한 번에 밀어 넣고 끝나지 않습니다. 무엇을 보낼지 정하려면 상대가 이미 가진 것을 먼저 알아야 합니다. 아래 그림이 그 주고받기를 순서대로 보입니다.
sequenceDiagram
participant 내쪽 as 내 저장소
participant 원격 as 원격 저장소
내쪽->>원격: 이 브랜치를 올리겠다
원격-->>내쪽: 내가 가진 마지막 커밋은 이것이다
내쪽->>원격: 그 뒤에 쌓인 커밋만 보낸다
원격-->>내쪽: 브랜치 이름을 새 커밋으로 옮겼다
먼저 어느 브랜치를 올릴지 알리고, 상대가 가진 마지막 커밋을 받아 옵니다. 그 뒤에 쌓인 커밋만 골라 보내므로 이미 있는 것은 다시 가지 않습니다. 마지막으로 상대가 브랜치 이름을 옮기고, 그 결과를 보내는 쪽에 알려 줍니다.
거절당하는 때
커밋은 자기 앞에 있던 커밋이 무엇인지 함께 기억합니다. 앞에 있던 커밋을 조상이라고 부릅니다. 조상을 따라 거슬러 올라가면 그 줄기가 걸어온 기록이 한 줄로 나옵니다.
받는 쪽은 이 조상 관계를 보고 요청을 받을지 정합니다. 원격이 가진 마지막 커밋이 내가 올리는 커밋의 조상이면, 내 기록은 원격 기록의 뒤를 잇기만 합니다. 이렇게 이어지는 경우를 빨리 감기라고 부릅니다.
flowchart TD
A["푸시 요청이 왔다"] --> B{"원격의 마지막 커밋이 새 커밋의 조상인가"}
B -->|그렇다| C["브랜치 이름을 새 커밋으로 옮긴다"]
B -->|아니다| D["거절한다"]
조상이 아니면 원격에만 있는 커밋이 있다는 뜻입니다. 그대로 이름을 옮기면 그 커밋은 어느 브랜치에서도 안 보이게 됩니다. 남이 올려 둔 작업이 소리 없이 사라지는 셈이라, 받는 쪽은 요청을 거절합니다.
거절당한 뒤에 하는 일
정상 순서는 받아서 합친 다음 다시 보내는 것입니다. 원격의 새 커밋을 받아 두는 동작을 페치, 받아 둔 것을 내 줄기에 합치는 동작을 병합이라고 합니다. 둘을 한 번에 하는 동작이 풀입니다.
같은 줄을 나와 상대가 서로 다르게 고쳤으면 합치는 도중에 멈춥니다. 어느 쪽을 남길지 사람이 골라야 하기 때문입니다. 이 고르는 일이 충돌 해소입니다. 합치고 나면 내 커밋이 원격의 마지막 커밋 뒤에 서게 되어, 다시 보낼 때는 조상 확인을 통과합니다.
조상 확인을 건너뛰고 이름을 그냥 옮기게 시킬 수도 있습니다. 이것이 강제 푸시입니다. 대가는 원격에만 있던 커밋이 브랜치 이름에서 떨어져 나간다는 것입니다. 그 커밋을 이미 받아 간 동료의 기록과도 어긋나서, 동료는 사라진 커밋을 들고 남습니다. 그래서 여럿이 함께 쓰는 브랜치에는 강제 푸시를 막아 두는 일이 흔합니다.
푸시가 하지 않는 것
푸시는 커밋 단위로 갑니다. 고쳐 놓고 아직 커밋하지 않은 수정은 올라가지 않습니다. 올릴 것을 먼저 커밋으로 묶어야 푸시할 것이 생깁니다.
푸시는 배포도 아닙니다. 올려 보낸 코드가 어딘가에서 돌기 시작하려면 그 뒤의 절차가 따로 있어야 합니다.
다만 그 절차를 푸시에 매달아 두는 일이 흔합니다. 어떤 일이 벌어졌을 때 저절로 도는 자동 절차를 워크플로라고 하고, 그 절차를 부르는 방아쇠를 트리거라고 합니다. 푸시를 트리거로 걸어 두면 올려 보내자마자 검사와 배포가 이어져서, 두 가지가 한 동작처럼 보이기도 합니다.
받는 쪽이 요청을 받아들일지도 푸시가 정하지 않습니다. 누가 어느 브랜치에 올릴 수 있는지는 저장소를 맡은 쪽의 접근 제어가 따로 정합니다.
버전관리 밖의 푸시
다른 분야에서도 푸시라는 말을 씁니다. 뿌리는 같습니다. 보내는 쪽이 먼저 움직인다는 것입니다. 반대로 받는 쪽이 「새것 있나」를 되풀이해 물어보러 가는 방식은 폴링이라고 부릅니다.
서버가 기다리지 않고 단말에 먼저 보내는 알림이 푸시 알림입니다.
컨테이너 이미지는 프로그램과 그것이 도는 데 필요한 것을 한 덩이로 묶어 둔 파일입니다. 그런 이미지를 모아 두고 내주는 서버를 레지스트리라고 합니다. 내가 만든 이미지를 레지스트리에 올리는 동작도 푸시라고 부릅니다. 어느 쪽이든 내 쪽에 있던 것을 상대에게 올려 보낸다는 뜻이 그대로 있습니다.
뜻이 갈리는 쓰임이 하나 있습니다. 자료구조 스택에 값을 넣는 동작도 푸시입니다. 이쪽은 위에서 눌러 넣는다는 뜻만 같고, 상대에게 보낸다는 뜻은 없습니다.
관련 항목
푸시가 상대하는 저장소와 그 별칭
원격 저장소 · 저장소 · origin · 업스트림 · 원격 추적 브랜치
푸시가 실어 나르는 기록의 단위
푸시와 짝을 이루는 주고받기 동작
푸시가 거절되거나 막히는 조건
빨리 감기 · 충돌 해소 · 보호된 브랜치 · 접근 제어 · 인증
푸시 뒤에 이어지는 협업 절차
풀 리퀘스트 · 코드 리뷰 · 병합 · 브랜치 전략 · 커밋에서 배포까지
푸시를 방아쇠로 삼는 자동화
트리거 · 워크플로 · 웹훅 · 지속적 통합 · 지속적 배포
같은 이름을 쓰는 다른 분야의 동작
푸시 알림 · 레지스트리 · 컨테이너 이미지 · 폴링 · 스택
푸시를 지원하는 버전관리 도구와 서비스
다른 이름: push · git push