사전 푸시
용어함정

푸시

gabury1고친 사람 github-actions[bot]

푸시는 여러 분야가 저마다 다른 동작에 붙여 쓰는 이름입니다. 버전관리에서는 내 컴퓨터에 쌓은 작업 기록을 함께 쓰는 저장소로 올려 보내는 동작입니다. 알림을 주고받는 분야에서는 받는 쪽이 묻기 전에 보내는 쪽이 먼저 실어 보내는 방식입니다. 「푸시했다」는 말은 어느 분야 이야기인지부터 정해야 뜻이 섭니다.

쉽고 빠른 이해

무슨 일을 하는 말인가 — 「푸시」는 분야마다 다른 동작을 부릅니다. 버전관리에서 쓰는 푸시는 내 컴퓨터에 쌓인 작업 기록을 팀이 함께 쓰는 저장소로 올리는 일입니다. 같은 말이 컨테이너 이미지를 올릴 때, 휴대폰에 알림을 보낼 때, 값을 쌓아 두는 자료구조에 값을 넣을 때도 쓰입니다.

왜 이렇게 하나 — 작업 기록을 올려 보내지 않으면 동료가 받아 갈 수 없습니다. 알림을 보내는 맥락의 푸시는 배터리가 한정된 휴대폰을 위한 것입니다. 연결 하나를 열어 둡니다. 메시지가 생길 때마다 그 연결로 받습니다.

어떻게 도나 — 작업 기록을 올리는 푸시를 예로 듭니다.

  1. 상대 저장소에 쌓인 마지막 기록이 무엇인지 받아 옵니다
  2. 그 기록이 내 기록의 앞부분이면 상대에게 없는 기록만 보냅니다
  3. 상대 저장소의 마지막 기록을 내 마지막 기록으로 바꿉니다

대가 — 상대 저장소에 내가 모르는 기록이 생겼으면 푸시가 거절됩니다. 그 기록을 먼저 받아 합친 다음 다시 보내야 합니다. 확인을 건너뛰고 강제로 밀어 넣으면 상대에게만 있던 기록을 잃을 수 있습니다.

상세

이 절은 「푸시했다」는 말에서 무엇이 정해지지 않는지부터 봅니다. 그다음 버전관리의 푸시를 따라갑니다. 무엇이 건너가는지와 보내기 전에 무엇을 확인하는지까지 봅니다.

말만으로는 정해지지 않는 두 가지

「푸시했다」를 들으면 두 가지를 물어야 합니다. 무엇이 움직였나, 그리고 누가 받았나입니다. 두 답이 맥락마다 다릅니다.

맥락 움직이는 것 받는 상대
버전관리 쌓아 둔 작업 기록과 그 기록을 가리키는 이름의 새 위치 함께 쓰는 저장소
컨테이너 레지스트리 프로그램을 돌릴 파일을 묶어 둔 이미지 이미지를 맡아 두는 서비스
메시지·알림 전달 메시지나 지표 메시지를 받는 기기 · 지표를 모으는 서버
자료구조 값 하나 없음. 값을 쌓아 두는 구조의 끝에 얹힐 뿐입니다

마지막 줄에서 뜻이 가장 크게 갈립니다. 자료구조의 푸시에는 보내는 일이 아예 없습니다. 그래서 「푸시」를 하나의 동작으로 묶어 이해하면 어느 한 맥락에서는 틀리게 됩니다. 네 맥락의 뜻과 거기 쓰이는 낱말은 「맥락별 뜻」 절이 하나씩 풉니다. 이 절의 아래는 버전관리의 푸시만 봅니다.

버전관리의 푸시가 옮기는 것

버전관리 도구는 고친 내용을 커밋이라는 단위로 쌓습니다. 커밋은 그때까지 고친 내용을 하나로 묶어 확정한 기록입니다. 내 컴퓨터에 있는 저장소가 로컬 저장소입니다. 같은 프로젝트를 다른 곳에 두고 함께 쓰는 저장소가 원격 저장소입니다. 로컬 저장소에만 있는 커밋은 다른 사람이 받아 갈 수 없습니다. 원격 저장소로 올려야 함께 일하는 사람들이 받아 갑니다.

커밋을 가리키는 이름이 참조입니다. 브랜치와 태그가 대표적인 참조입니다. 브랜치가 가리키는 맨 끝 커밋이 브랜치 끝입니다. 새 커밋을 쌓으면 브랜치 끝이 그 커밋으로 옮겨 갑니다.

푸시는 로컬 저장소의 참조로 원격 저장소의 참조를 갱신하는 동작입니다. 이때 원격에 아직 없는 데이터를 함께 보냅니다. 건너가는 것은 두 갈래입니다.

  • 객체 — 객체는 Git 이 커밋과 파일 내용을 담아 두는 단위입니다. 내 브랜치 끝에서 거슬러 닿을 수 있는 객체 가운데 원격에 빠진 것만 보냅니다
  • 참조의 새 위치 — 원격 저장소의 브랜치 끝을 내 브랜치 끝 커밋으로 옮깁니다

보내는 객체는 브랜치 끝에서 거슬러 닿는 것뿐입니다. 그래서 파일을 고쳐 놓고 커밋하지 않은 수정은 건너가지 않습니다.

가장 짧은 꼴은 원격 저장소 이름과 브랜치 이름을 대는 것입니다.

터미널
git push origin main

origin 은 원격 저장소에 붙여 둔 이름입니다. 이 명령은 로컬 main 브랜치를 origin 의 main 브랜치로 푸시합니다. 어느 로컬 참조를 어느 원격 참조로 보낼지 짝지은 것이 refspec 입니다.

로컬에 커밋 셋이 쌓였고 원격에는 그중 첫 커밋만 있는 경우를 그려 봅니다.

flowchart TD
    subgraph 로컬전 ["푸시 전 · 로컬 저장소"]
        L1["커밋 1"] --> L2["커밋 2"]
        L2 --> L3["커밋 3"]
        LM(["main"]) -.-> L3
    end
    subgraph 원격전 ["푸시 전 · origin"]
        R1["커밋 1"]
        RM(["main"]) -.-> R1
    end
    subgraph 원격후 ["푸시 뒤 · origin"]
        N1["커밋 1"] --> N2["커밋 2 · 이번에 건너간 객체"]
        N2 --> N3["커밋 3 · 이번에 건너간 객체"]
        NM(["main"]) -.-> N3
    end

점선은 참조가 어느 커밋을 가리키는지입니다. 건너간 것은 원격에 없던 커밋 2 와 커밋 3, 그리고 main 의 새 위치입니다. 커밋 1 은 원격에 이미 있어 보내지 않습니다.

보내기 전에 확인하는 조상 관계

커밋은 바로 앞 커밋을 부모로 기억합니다. 부모를 따라 거슬러 올라가며 만나는 커밋이 모두 그 커밋의 조상입니다.

푸시는 먼저 원격 저장소에서 그 브랜치의 끝이 어느 커밋인지 받아 옵니다. 그 커밋이 내 브랜치 끝의 조상인지 봅니다. 조상이면 원격에 없는 객체를 넣고 원격의 브랜치 끝을 옮깁니다. 조상이 아니면 푸시는 실패합니다.

sequenceDiagram
    participant 로컬 as 로컬 저장소
    participant 원격 as 원격 저장소
    로컬->>원격: 이 브랜치의 끝이 어느 커밋인가
    원격-->>로컬: 브랜치 끝 커밋
    Note over 로컬: 그 커밋이 내 브랜치 끝의 조상인지 본다
    Note over 로컬: 조상이 아니면 여기서 실패한다
    로컬->>원격: 원격에 없는 객체를 보낸다
    로컬->>원격: 브랜치 끝을 내 커밋으로 옮긴다
    원격-->>로컬: 참조마다 갱신 결과

원격의 브랜치 끝이 내 브랜치 끝의 조상이면, 내 기록은 원격 기록 뒤를 잇기만 합니다. 어떤 커밋을 가리키던 참조가 그 커밋의 자손을 가리키게 바뀌는 갱신을 패스트 포워드 갱신이라고 부릅니다. 이때 새 커밋이 딛고 선 기록은 앞 커밋이 딛고 선 기록을 전부 품습니다. 패스트 포워드 갱신은 이력을 하나도 잃지 않습니다.

조상이 아닌 경우는 「실패」 절에서 봅니다. 왜 생기는지 · 무엇이 찍히는지 · 어떻게 넘어가는지가 거기 있습니다.

맥락별 뜻

이 절은 「푸시」가 쓰이는 네 맥락을 표로 가릅니다. 그다음 버전관리 밖의 세 맥락을 하나씩 봅니다. 버전관리는 「상세」 절에서 봤습니다.

맥락 뜻 출처
버전관리 로컬 저장소의 참조로 원격 저장소의 참조를 갱신하고, 원격에 없는 객체를 함께 보내는 동작 Git 용어집의 push 항목 · git-push 매뉴얼
컨테이너 레지스트리 이미지를 이루는 블롭과 매니페스트를 레지스트리에 올리는 동작 Docker 의 docker image push 문서 · OCI(Open Container Initiative) 배포 명세
메시지·알림 전달 받는 쪽이 메시지마다 요청하지 않아도 보내는 쪽이 메시지가 생길 때마다 실어 보내는 전달 방식. 받는 쪽이 가져가는 풀과 맞세워진다 RFC(Request for Comments) 8030 웹 푸시 · Prometheus 의 자주 묻는 질문 문서
자료구조 스택 맨 위에 값을 하나 얹는 연산 Java 21 표준 라이브러리의 Deque · Stack 문서

컨테이너 레지스트리

컨테이너 이미지는 프로그램과 그 프로그램이 도는 데 필요한 파일을 한 덩이로 묶은 것입니다. 레지스트리는 이런 이미지를 올려 두고 받아 가는 서비스입니다. 이 맥락의 푸시는 내가 만든 이미지를 레지스트리에 올리는 동작입니다. Docker 에서는 Docker Hub 에도, 직접 띄운 레지스트리에도 docker image push 로 올립니다.

올라가는 것은 블롭과 매니페스트 두 가지입니다. 블롭은 레지스트리가 저장하는 내용 덩어리입니다. 매니페스트는 이미지를 이루는 블롭들을 가리켜 하나로 묶는 JSON(JavaScript Object Notation) 문서입니다.

flowchart TD
    M["매니페스트 · 마지막에 올린다"] --> B1["블롭 · 먼저 올린다"]
    M --> B2["블롭 · 먼저 올린다"]
    M --> B3["블롭 · 먼저 올린다"]

블롭은 다이제스트로 찾아갑니다. 다이제스트는 내용에서 계산해 낸 값입니다. 매니페스트는 블롭 말고 다른 매니페스트도 가리킬 수 있습니다.

올리는 순서는 대개 받아 가는 순서와 거꾸로입니다. 받아 갈 때는 매니페스트를 먼저 받고 그것이 가리키는 블롭을 받습니다. 올릴 때는 이미지를 이루는 블롭을 먼저 올립니다. 매니페스트는 마지막에 올립니다.

레이어는 이미지를 이루는 파일 묶음 한 겹입니다. Docker 데몬은 기본으로 레이어 다섯 개를 한꺼번에 올립니다. 레지스트리가 저장하는 블롭이 레이어와 어떻게 대응하는지는 자료에 없습니다(미확인).

메시지와 알림 전달

휴대폰 같은 기기는 걸려 오는 전화나 새 메시지 같은 실시간 이벤트를 제때 받아야 합니다. 그런데 이런 기기는 대개 전력이 한정돼 있습니다. 모든 실시간 이벤트를 연결 하나로 모으면 네트워크와 무선 자원을 아낄 수 있습니다. 서비스 하나가 이벤트를 다 모읍니다. 도착하는 대로 앱에 나눠 줍니다. 이렇게 제때 실어 보내는 일이 푸시입니다.

웹 푸시에는 세 행위자가 있습니다.

행위자 하는 일
사용자 에이전트 메시지를 받는 기기와 그 안의 소프트웨어
푸시 서비스 메시지를 사용자 에이전트에 전달하는 서비스
애플리케이션 서버 대개 서버에서 도는 애플리케이션의 한 부분. 메시지 전달을 요청한다

처음에 사용자 에이전트가 구독을 만듭니다. 구독은 사용자 에이전트가 푸시 서비스에 만들어 두는 메시지 수신 등록입니다. 모든 메시지가 어느 구독에 묶입니다.

사용자 에이전트는 이 구독을 애플리케이션 서버에 건넵니다. 그때 메시지를 맡길 주소도 함께 건네줍니다. 애플리케이션 서버는 그 주소로 POST 요청을 보내 메시지 전달을 맡깁니다. 메시지 내용은 그 요청의 본문에 담깁니다.

지표를 모아 감시하는 분야에서도 푸시는 풀과 맞세워지는 방식 이름입니다. 여기서 움직이는 것은 지표입니다. 받는 상대는 지표를 모으는 서버입니다.

푸시라면 지표를 내놓는 프로그램이 먼저 움직여 값을 보냅니다. 풀이라면 지표를 모으는 서버가 먼저 움직여 값을 가져갑니다.

Prometheus 는 HTTP(HyperText Transfer Protocol)로 당겨 오는 풀을 씁니다. 당겨 오는 쪽을 고른 이유는 셋입니다.

  • 지표를 모으는 서버를 필요할 때 더 띄울 수 있습니다. 당겨 오면 새로 띄운 서버가 값을 내놓는 프로그램에 찾아가 값을 받아 옵니다. 개발하면서 노트북에 하나 띄우는 경우가 그렇습니다
  • 값을 내놓는 프로그램이 내려갔는지 더 쉽고 확실하게 알 수 있습니다
  • 웹 브라우저로 그 프로그램에 직접 가서 상태를 들여다볼 수 있습니다

다만 당겨 오는 쪽이 조금 낫다는 정도입니다. 무엇으로 감시할지 고를 때 큰 요소로 볼 것은 아닙니다.

꼭 밀어 보내야 하는 경우도 있습니다. Pushgateway 는 그런 경우를 위해 Prometheus 가 따로 내놓는 구성 요소입니다.

스택

스택은 나중에 넣은 값을 먼저 꺼내는 LIFO(Last-In-First-Out) 자료구조입니다. 이 맥락의 푸시는 스택 맨 위에 값을 하나 얹는 연산입니다. 맨 위 값을 꺼내는 연산이 팝입니다. 꺼내지 않고 들여다보는 연산은 피크입니다.

덱은 양쪽 끝에서 넣고 뺄 수 있는 자료구조입니다. Java 의 Deque 를 스택으로 쓰면 푸시와 팝이 덱의 앞쪽 끝에서 일어납니다. 그래서 Deque 의 push 는 앞쪽에 넣는 addFirst 와 같습니다.

이 맥락에는 상대가 없습니다. 값은 한 자료구조 안에서 맨 위로 들어갈 뿐입니다. 어디로도 보내지지 않습니다.

경계

받는 쪽이 먼저 요청을 걸어 두고 기다려도 푸시입니까? 맞습니다. 웹 푸시가 그 경우입니다.

사용자 에이전트는 구독 주소로 GET 요청을 보내 새 메시지를 달라고 해 둡니다. 푸시 서비스는 이 요청에 답하지 않습니다. 요청을 열린 채로 둡니다.

애플리케이션 서버가 메시지를 보내면, 푸시 서비스는 처음 받은 그 요청에 묶어 HTTP/2 서버 푸시를 일으킵니다. HTTP/2 서버 푸시는 클라이언트가 보내지 않은 요청을 서버가 지어 붙이는 기능입니다. 서버는 그 요청의 응답으로 내용을 보냅니다.

서버가 지어 붙인 요청은 PUSH_PROMISE 라는 HTTP/2 메시지에 실려 받는 쪽으로 먼저 건너갑니다. 메시지 하나가 그 응답 하나에 담깁니다.

sequenceDiagram
    participant 에이전트 as 사용자 에이전트
    participant 서비스 as 푸시 서비스
    participant 앱서버 as 애플리케이션 서버
    에이전트->>서비스: 구독을 만든다
    에이전트->>앱서버: 구독을 건넨다
    에이전트->>서비스: GET 으로 새 메시지를 요청해 둔다
    Note over 서비스: 답하지 않고 요청을 열어 둔다
    앱서버->>서비스: POST 로 메시지를 맡긴다
    서비스-->>에이전트: 서버 푸시로 메시지를 보낸다
    앱서버->>서비스: POST 로 다음 메시지를 맡긴다
    서비스-->>에이전트: 같은 요청에 묶어 또 보낸다

연결과 첫 요청은 받는 쪽인 사용자 에이전트가 열었습니다. 그래도 메시지마다 먼저 움직이는 쪽은 푸시 서비스입니다. 사용자 에이전트는 메시지가 올 때마다 새로 묻지 않습니다. 메시지마다 붙는 요청은 푸시 서비스가 스스로 지어 붙인 것입니다.

그래서 이 맥락에서 푸시를 가르는 기준은 연결을 누가 열었나가 아닙니다. 메시지 하나하나를 누가 먼저 움직여 보내나입니다.

실패

이 절은 푸시가 깨지는 조건을 맥락마다 봅니다. 대부분은 버전관리 맥락입니다. 레지스트리와 스택의 경우는 끝에 짧게 붙입니다.

패스트 포워드가 아닌 갱신의 거절

나와 다른 사람이 같은 커밋 X 에서 출발했다고 합시다. 나는 커밋 B 로 이어지는 기록을 쌓았습니다. 다른 사람은 커밋 A 로 이어지는 기록을 쌓았습니다. 그 기록을 먼저 원격 저장소에 푸시했습니다.

flowchart TD
    X["커밋 X · 둘이 같이 출발한 커밋"] --> A["커밋 A · 다른 사람이 먼저 푸시"]
    X --> B["커밋 B · 내가 쌓은 커밋"]

다른 사람의 푸시는 X 를 가리키던 원격 브랜치를 A 로 옮겼습니다. A 는 X 의 자손이니 패스트 포워드 갱신입니다. 이제 내가 푸시하면 A 를 가리키는 브랜치를 B 로 바꾸려 하게 됩니다. A 는 B 의 조상이 아닙니다. 이 갱신을 허락하면 A 가 들여온 변경을 잃습니다. 모두가 B 위에서 작업을 이어 가게 되기 때문입니다. 그래서 푸시는 기본으로 패스트 포워드가 아닌 갱신을 막습니다.

받는 참조의 종류마다 규칙이 다릅니다.

푸시가 갱신하려는 참조 규칙
브랜치 패스트 포워드 갱신만 받는다. 원격의 브랜치 끝이 보내는 커밋의 조상이어야 한다. 보내는 것은 커밋이어야 한다
태그 갱신을 모두 거절한다. 보내는 것은 어떤 객체여도 된다

혼자 쓰는 저장소에서도 거절을 만납니다. 커밋 P 를 이미 푸시했습니다. 그 뒤 git commit --amend 로 P 를 고쳐 커밋 Q 를 만든 경우입니다. P 를 푸시했다는 것을 잊고 Q 를 푸시하면 패스트 포워드가 아니어서 거절됩니다.

거절 뒤에 하는 일

내 작업도 남의 작업도 잃지 않으려면, 먼저 원격의 기록을 받아 와야 합니다. 원격의 새 커밋을 받아 오는 동작이 페치입니다. 그다음 양쪽 변경을 다 담은 기록을 만들어 다시 푸시합니다. 길은 둘입니다.

  • 풀 — git pull 은 원격 기록을 받아 와 내 브랜치에 합칩니다. A 와 B 를 둘 다 부모로 갖는 병합 커밋 C 가 생깁니다. 같은 곳을 양쪽이 다르게 고쳤으면 합치는 도중에 멈춥니다. 그때는 사람이 양쪽을 보고 정리해야 합니다. 이 일을 충돌 해소라고 부릅니다
  • 리베이스 — git pull --rebase 는 X 에서 B 까지의 내 변경을 A 위로 옮깁니다. 옮긴 변경은 새 커밋 D 가 됩니다
flowchart TD
    subgraph 병합 ["git pull · 병합 커밋 C"]
        X1["X"] --> A1["A"]
        X1 --> B1["B"]
        A1 --> C1["C"]
        B1 --> C1
    end
    subgraph 리베이스 ["git pull --rebase · 새 커밋 D"]
        X2["X"] --> A2["A"]
        X2 --> B2["B · 옮기기 전의 커밋"]
        A2 --> D2["D · B 의 변경을 A 위에 다시 만든 커밋"]
    end

리베이스한 뒤 다시 푸시할 때 보내는 커밋은 D 입니다. 어느 쪽이든 A 는 새 커밋의 조상이 됩니다. 그래서 A 를 C 나 D 로 바꾸는 갱신은 패스트 포워드가 됩니다. 다시 한 푸시는 받아들여집니다.

출력에 찍히는 상태

git push 는 결과를 표 꼴로 찍습니다. 한 줄이 참조 하나의 상태입니다.

<flag> <summary> <from> -> <to> (<reason>)

맨 앞 한 글자가 그 참조가 어떻게 됐는지 알려 줍니다.

글자 뜻
(빈칸) 패스트 포워드로 푸시했다
+ 강제 갱신에 성공했다
- 참조를 지웠다
* 새 참조를 푸시했다
! 거절됐거나 푸시에 실패했다
= 이미 같아서 푸시할 필요가 없었다

요약은 모든 줄에 붙습니다. 실패한 줄의 요약은 실패가 어디서 났는지를 가립니다.

요약 뜻
rejected Git 이 그 참조를 아예 보내지 않았다. 대개 패스트 포워드가 아닌데 강제하지 않은 경우다
remote rejected 원격 저장소가 갱신을 거부했다
remote failure 원격 저장소가 갱신 성공을 알려 오지 않았다

remote rejected 는 대개 원격 저장소의 훅이 일으킵니다. 훅은 저장소에 정해진 일이 생길 때 저절로 도는 스크립트입니다. 원격 저장소에 훅을 걸어 두면 푸시가 들어올 때마다 그 스크립트가 돕니다.

원격 저장소의 안전 설정이 거부할 수도 있습니다. 체크아웃은 저장소에서 어느 브랜치를 골라 작업 파일로 펼쳐 둔 것을 말합니다. 그런 설정이 아래 셋입니다.

설정 막는 것
receive.denyCurrentBranch 체크아웃된 브랜치로의 푸시
receive.denyNonFastForwards 강제 갱신
receive.denyDeletes · receive.denyDeleteCurrent 참조 지우기

remote failure 는 원격 저장소의 일시 오류나 네트워크 연결 끊김, 그 밖의 일시 장애 탓일 수 있습니다. 줄 끝 괄호 안의 이유는 사람이 읽으라고 붙인 설명입니다.

강제로 넘기기

--force 를 주거나 refspec 앞에 + 를 붙이면 앞에서 본 브랜치·태그 규칙을 넘길 수 있습니다. 원격의 브랜치 끝이 보내는 커밋의 조상인지 보는 확인도 함께 꺼집니다. 그 대가로 원격 저장소가 커밋을 잃을 수 있어 조심해서 써야 합니다.

앞서 본 고쳐 쓴 커밋의 경우에도 그렇습니다. 그 사이 누구도 커밋 P 를 받아 가 그 위에 작업하지 않았다고 확신할 때만 --force 를 씁니다. git push --force 는 이력을 잃을 생각일 때 쓰는 방법입니다.

강제로도 넘기지 못하는 것이 둘 있습니다.

  • 브랜치가 커밋이 아닌 객체를 받게 만들 수는 없습니다
  • 원격 저장소가 설정으로 거부하는 푸시는 강제해도 받지 않습니다

--force-with-lease 는 더 좁게 넘깁니다. 이미 푸시한 기록을 리베이스해서 다시 올려야 하는 경우를 생각해 봅시다. 내가 리베이스하는 사이 다른 사람이 원래 기록 위에 커밋을 얹었을 수 있습니다. 그러면 원격의 브랜치 끝이 그 커밋으로 나아가 있습니다. 이때 그냥 --force 로 밀면 그 사람의 작업을 잃습니다.

--force-with-lease 는 원격 참조의 지금 값이 내가 예상한 값일 때만 갱신합니다. 다르면 푸시가 실패합니다. 참조를 잠그지 않고 「임대」를 잡아 두는 셈입니다. 그 임대가 아직 유효할 때만 원격 참조가 갱신됩니다.

원격 추적 브랜치는 원격 브랜치가 어느 커밋을 가리키는지 내 저장소에 기억해 둔 참조입니다. 예상한 값을 따로 대지 않으면 이 참조에 기억된 값이 예상한 값이 됩니다.

--force 는 이 임대 확인까지 함께 끕니다.

레지스트리와 스택에서 깨지는 조건

레지스트리는 매니페스트를 받을 때, 그 매니페스트의 일부 필드가 가리키는 매니페스트나 블롭이 레지스트리에 없으면 거부할 수 있습니다. 이 이유로 거부할 때는 반드시 MANIFEST_BLOB_UNKNOWN 오류를 하나 이상 돌려줍니다. 푸시에 매여 정해진 거절은 이 하나입니다.

MANIFEST_BLOB_UNKNOWN 은 레지스트리가 돌려주는 오류 코드 목록의 한 줄입니다. 그 목록에는 요청 종류를 가리지 않고 쓰이는 값도 있습니다. 인증이 필요하다는 UNAUTHORIZED, 요청한 리소스에 접근이 거부됐다는 DENIED, 함께 보낸 다이제스트가 올린 내용과 맞지 않는다는 DIGEST_INVALID 가 그런 값입니다. 이 값들이 푸시 때 언제 돌아오는지는 따로 정해져 있지 않습니다.

스택에도 실패가 있습니다. Java 의 Deque.push 는 용량 제한을 어기지 않고 바로 넣을 수 있을 때만 넣습니다. 지금 빈칸이 없으면 IllegalStateException 을 던집니다.

관련 항목

버전관리의 푸시가 갱신하는 참조

참조 · 브랜치 · 태그 · HEAD · refspec · 원격 추적 브랜치

버전관리의 푸시가 오가는 저장소

저장소 · 로컬 저장소 · 원격 저장소 · origin · 업스트림

푸시 앞뒤에 쓰는 git 동작

커밋 · 페치 · 풀 · 병합 · 병합 커밋 · 리베이스

버전관리의 푸시를 거절하는 조건과 장치

패스트 포워드 · 강제 푸시 · 충돌 해소 · 훅 · 보호된 브랜치

레지스트리 푸시가 올리는 이미지의 구성 요소

레지스트리 · 컨테이너 이미지 · 레이어 · 블롭 · 매니페스트 · 다이제스트

이미지 푸시를 정한 도구와 표준

Docker · Docker Hub · OCI

푸시 방식으로 메시지를 나르는 구성 요소

푸시 알림 · 웹 푸시 · 푸시 서비스 · 사용자 에이전트 · 애플리케이션 서버 · 구독 · HTTP/2 · 서버 푸시

푸시와 맞세워지는 가져오기 방식

폴링 · 롱 폴링 · 풀 모델 · Pushgateway · Prometheus · 수집기

스택에서 푸시와 짝을 이루는 연산과 구조

스택 · 팝 · 피크 · LIFO · 덱

버전관리의 푸시가 속하는 상위 분류와 흐름

버전관리 · Git · 분산 버전관리 · 커밋에서 배포까지

다른 이름: push · git push