git push가 거부되며 “Updates were rejected because the tip of your current branch is behind"라는 메시지를 처음 만나면, 무엇을 어떻게 해야 할지 막막할 수 있다. 이 장은 push의 기본 동작과, 그 거부가 실제로는 협업 중 데이터 손실을 막는 안전장치라는 점을 다룬다.
개요
git push는 로컬 브랜치의 커밋을 원격 저장소에 반영한다.
| |
-u(--set-upstream)는 로컬 브랜치와 원격 브랜치 사이에 “이 로컬 브랜치는 저 원격 브랜치를 추적한다"는 관계를 기록한다. 이 관계가 한 번 설정되면 이후에는 git push, git pull(19장)을 인자 없이 실행해도 Git이 어느 원격의 어느 브랜치를 대상으로 할지 스스로 판단한다.
기본 개념
Upstream 추적 관계는 06장에서 다룬 git status의 “ahead N / behind N” 표시, 09장의 git log --graph --all에서 보이는 origin/main 참조와도 연결된다. 이 관계 덕분에 Git은 로컬 브랜치가 원격보다 몇 커밋 앞서 있는지, 뒤처져 있는지를 계산할 수 있다. git clone(18장)으로 저장소를 복제하면 기본 브랜치에는 이 관계가 자동으로 설정되지만, 로컬에서 새로 만든 브랜치(11장의 git branch, git switch -c)는 처음 push할 때 명시적으로 -u를 붙여줘야 한다.
종류/세부
Push가 거부되는 경우와 그 이유
원격 브랜치가 로컬이 알고 있는 상태보다 앞서 있으면(다른 사람이 먼저 push했거나, 다른 컴퓨터에서 먼저 작업했을 때) push가 거부된다.
| |
이 거부는 버그가 아니라 다른 사람의 커밋을 실수로 덮어쓰지 못하게 막는 안전장치다. 올바른 대응은 먼저 원격의 최신 내용을 받아와 반영(git fetch + merge/rebase, 19장)한 뒤 다시 push하는 것이다.
| |
강제 push와 --force-with-lease
15장에서 다룬 리베이스처럼 이미 push된 커밋을 로컬에서 재작성했다면, 일반 push는 거부되고 강제 push가 필요하다.
| |
--force는 원격 브랜치를 로컬 상태로 완전히 덮어쓰며, 그사이 다른 사람이 push한 커밋이 있었다면 그 커밋을 통째로 지워버릴 수 있다. --force-with-lease는 push하기 전에 “내가 마지막으로 확인한 원격 상태와 지금 원격의 실제 상태가 같은가"를 검사해, 다른 사람이 그사이 push했다면 실패시킨다. 리베이스 후 강제 push가 필요한 상황에서는 --force-with-lease를 기본으로 쓰는 것이 안전하다.
| 옵션 | 다른 사람의 push 여부 확인 | 위험도 |
|---|---|---|
--force | 확인하지 않음 | 높음(타인의 작업을 덮어쓸 수 있음) |
--force-with-lease | 확인함(예상과 다르면 거부) | 낮음(개인 브랜치에서 안전하게 사용 가능) |
브랜치와 태그 삭제
원격 브랜치나 태그(22장)를 삭제할 때도 push 명령을 쓴다.
| |
주의사항·함정
메인 브랜치에 --force를 실행하면 팀 전체에 영향을 준다: main이나 develop처럼 여러 사람이 공유하는 브랜치에 강제 push를 하면, 그 브랜치를 기준으로 작업하던 모든 사람의 히스토리가 어긋난다. 대부분의 팀은 GitHub 등에서 이런 브랜치에 강제 push 자체를 금지하는 보호 규칙(branch protection)을 걸어둔다.
-u 없이 push한 브랜치는 다음에도 매번 전체 인자를 입력해야 한다: upstream이 설정되지 않은 브랜치에서 git push만 실행하면 Git이 오류를 내며 “어디로 push할지 모르겠다"고 알려준다. 처음 push할 때 -u를 붙이는 습관이 이후 작업을 편하게 만든다.
로컬 브랜치를 삭제해도 원격 브랜치는 남아 있다: 11장에서 다룬 git branch -d는 로컬 참조만 지운다. 원격에 올린 브랜치까지 정리하려면 위에서 설명한 git push origin --delete가 별도로 필요하다.
![[Git] 18. git clone](/post/git/git-clone-command-copy-repository/wordcloud_hu_d317ea1a5439c059.webp)
![[Git] 19. git fetch vs git pull](/post/git/git-fetch-vs-git-pull/wordcloud_hu_98e312a4a7ccfd67.webp)
![[Git] 21. Fork와 Pull Request 워크플로(GitHub Flow)](/post/git/fork-pull-request-workflow-github-flow/wordcloud_hu_681908459f6099a8.webp)
![[Git] 22. git tag — 릴리스 태깅](/post/git/git-tag-command-release-tagging/wordcloud_hu_f2c27c45177f6c2c.webp)
![[Git] 17. git remote — 원격 저장소 등록](/post/git/git-remote-command-managing-remote-repositories/wordcloud_hu_ea9e4da91cbf1c73.webp)
![[Git] 08. git commit — 커밋 작성 규칙](/post/git/git-commit-command-writing-good-commits/wordcloud_hu_b27461e0d97eff2a.webp)
![[Git] 11. 브랜치 개념과 git branch](/post/git/git-branch-concept-and-command/wordcloud_hu_d0c627d2bdd2eb50.webp)