가이드

git push: 변경 사항을 원격 저장소로 보내는 방법

업데이트: · Skillaria.Top

git push는 현재 브랜치에서 원격 저장소에 아직 없는 로컬 커밋을 보내고, 서버의 브랜치 포인터를 최신 커밋으로 옮깁니다. 처음 올릴 때는 git push -u origin 브랜치이름을 쓰고, 이후에는 git push만 입력하면 됩니다. 서버에 다른 사람의 커밋이 있다면 force push 대신 git pull로 먼저 받아야 합니다.

git push가 하는 일

git push는 현재 로컬 브랜치에서 아직 서버에 없는 커밋을 가져가 원격 저장소(보통 origin)로 보내고, 그곳의 브랜치 포인터를 여러분의 마지막 커밋으로 옮깁니다. 작업 폴더의 파일과 커밋하지 않은 변경은 함께 가지 않습니다. 보내지는 것은 히스토리, 즉 커밋뿐입니다.

push는 "동기화"가 아니라는 점을 기억하세요. 한 방향으로만 움직입니다. 로컬 저장소에서 원격 저장소로 보냅니다. 남의 변경을 받으려면 git fetch나 git pull이 필요합니다. 이 방향을 혼동하면 초보자가 겪는 문제의 대부분이 생깁니다.

Git에는 또 하나의 규칙이 있습니다. push는 기본적으로 빨리 감기(fast-forward)만 허용합니다. 서버의 기존 히스토리를 이어 가는 커밋이라면 서버는 받아들입니다. 다른 사람이 먼저 커밋을 올렸다면 여러분의 push는 거부됩니다. 이것은 오류가 아니라 보호 장치입니다.

문법

git push [<remote>] [<로컬-브랜치>[:<원격-브랜치>]]

인수 없이 git push를 실행하면 Git은 현재 브랜치를 upstream(추적 브랜치)으로 설정된 곳에 보냅니다. upstream이 아직 없거나 다른 이름으로 보내고 싶을 때는 명시적으로 씁니다.

git push origin main                  # 로컬 main을 origin/main으로
git push origin feature/login         # feature/login 브랜치 보내기
git push origin fix:hotfix-123        # 로컬 fix를 서버에서는 hotfix-123으로
git push origin HEAD                  # 현재 브랜치를 같은 이름으로

처음으로 변경 사항 올리기

새 프로젝트를 GitHub나 GitLab에 올리는 전형적인 과정입니다. 먼저 서버에 빈 저장소를 만들고, 로컬 저장소와 연결한 뒤 브랜치를 보냅니다.

git init
git add .
git commit -m "첫 커밋"
git branch -M main
git remote add origin git@github.com:user/project.git
git push -u origin main

-u 옵션(전체 이름 --set-upstream)은 로컬 main을 원격 origin/main과 연결합니다. 그 뒤로는 브랜치에서 git push와 git pull만 입력하면 되고, git status는 서버보다 몇 개의 커밋이 앞서거나 뒤처졌는지 보여 줍니다.

remote가 어디를 가리키는지는 git remote -v로 확인합니다. 주소가 잘못되었다면 저장소를 지우고 다시 만들지 말고 git remote set-url origin <새-주소>로 고치세요.

새 브랜치 올리기

작업용 브랜치를 만들고 커밋한 뒤 git push를 입력하면 Git이 이렇게 알려 줍니다.

fatal: The current branch feature/login has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin feature/login

이것은 오류라기보다 안내입니다. 이 브랜치의 짝이 서버에 아직 없다는 뜻입니다. 제안된 명령을 실행하거나, 짧게 git push -u origin feature/login을 쓰세요. 새 브랜치마다 이 과정을 자동으로 하게 하려면 설정을 켜세요(Git 2.37부터 사용 가능).

git config --global push.autoSetupRemote true

push가 거부될 때: rejected, non-fast-forward

팀 작업에서 가장 흔한 상황입니다.

! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'github.com:user/project.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally.

동료가 여러분보다 먼저 커밋을 올렸습니다. 여러분의 히스토리와 서버의 히스토리가 갈라졌고, Git이 push를 받아들였다면 동료의 커밋이 사라졌을 것입니다. 올바른 해결은 먼저 변경을 받아서 내 커밋을 그 위에 올리는 것입니다.

git pull --rebase       # 남의 커밋을 받고 내 커밋을 그 위에 다시 쌓기
# 충돌이 나면: 파일 수정, git add, git rebase --continue
git push

--rebase 대신 일반 git pull을 써도 됩니다. 그러면 병합 커밋이 생깁니다. 두 방식 모두 올바릅니다. 대부분의 팀은 내 로컬 커밋에는 rebase를 써서 히스토리를 일직선으로 유지하는 것을 선호합니다. 중요한 것은 거부를 --force로 "해결"하지 않는 것입니다.

git push --force와 --force-with-lease

강제 push는 자기 브랜치의 히스토리를 의도적으로 다시 썼을 때 필요합니다. 예를 들어 git commit --amend, git rebase를 했거나, 리뷰 전에 커밋을 합쳤을 때입니다. 일반 push는 그런 브랜치를 받아들이지 않습니다. 서버의 옛 커밋이 새 커밋의 조상이 아니기 때문입니다.

git push --force-with-lease

--force-with-lease는 서버의 브랜치가 로컬 Git이 마지막으로 본 위치(origin/<브랜치> 참조)에 그대로 있을 때만 덮어씁니다. 동료가 그 사이에 무언가를 올렸다면 명령은 거부되고, 그들의 작업은 지워지지 않습니다. 일반 --force(-f)는 이런 확인 없이 아무 말 없이 모두 덮어씁니다.

주의할 점: 그 전에 git fetch를 하면 origin/<브랜치> 참조가 갱신되어 보호 장치가 작동하지 않습니다. Git은 여러분이 이미 그 커밋을 봤다고 판단하기 때문입니다. 따라서 무엇이 도착했는지 확인하지 않고 fetch한 뒤 바로 force push하지 마세요.

명령동작사용할 때
git push빨리 감기만 허용항상 기본값
git push --force-with-lease확인을 거친 덮어쓰기내 브랜치를 rebase나 amend한 뒤
git push --force확인 없이 덮어쓰기거의 쓰지 않음. 공용 브랜치에서는 절대 금지

공용 브랜치(main, develop)에는 강제 push를 하면 안 됩니다. 옛 히스토리를 이미 받아 간 모든 사람의 히스토리가 서버와 어긋나기 때문입니다. 보통 이런 브랜치는 저장소 설정으로 보호되어 있어서, 서버는 protected branch hook declined로 응답합니다.

태그 보내기

일반 git push는 태그를 보내지 않습니다. 명시적으로 지정해야 합니다.

git tag -a v1.2.0 -m "릴리스 1.2.0"
git push origin v1.2.0          # 태그 하나
git push --tags                 # 로컬의 모든 태그
git push --follow-tags          # 커밋과, 그 커밋을 가리키는 주석 태그

--follow-tags는 --tags보다 편합니다. 개인적으로 만든 가벼운 태그는 빼고, 보내는 커밋에 붙은 주석 태그만 보내기 때문입니다.

서버의 브랜치 삭제하기

삭제도 push로 합니다.

git push origin --delete feature/login
# 예전 문법, 같은 동작:
git push origin :feature/login

로컬 브랜치는 그대로 남으며, git branch -d feature/login으로 따로 지웁니다. 동료들의 컴퓨터에는 origin/feature/login 참조가 남아 있으므로, 그들은 git fetch --prune을 실행해야 합니다.

유용한 git push 옵션

옵션용도
-u, --set-upstream원격 브랜치를 upstream으로 기억
-n, --dry-run무엇이 보내질지만 보여 주고 실제로는 보내지 않음
--all로컬의 모든 브랜치 보내기
--tags / --follow-tags태그 보내기
-d, --delete서버의 브랜치나 태그 삭제
--force-with-lease안전한 히스토리 덮어쓰기
-v, --verbose자세한 출력
--no-verifypre-push 훅 건너뛰기(이유를 알고 쓸 때만)

git push에서 자주 나는 오류와 해결법

Permission denied (publickey)

서버가 SSH 키를 받아 주지 않았습니다. 키가 있는지(ls ~/.ssh), 공개 키가 계정에 등록되어 있는지, 연결이 되는지(ssh -T git@github.com)를 확인하세요. remote가 HTTPS로 되어 있다면 Git은 키 대신 로그인과 토큰을 묻습니다. git remote -v를 보세요.

HTTPS에서 Authentication failed

GitHub와 많은 서비스는 오래전부터 HTTPS 작업에 계정 비밀번호를 받지 않습니다. 개인 액세스 토큰을 쓰거나 SSH로 바꿔야 합니다. 자격 증명 도우미로 토큰을 저장할 수 있습니다: git config --global credential.helper store(평문으로 저장) 또는 운영체제의 자격 증명 관리자.

src refspec main does not match any

그 이름의 브랜치가 없습니다. 대개 빈 저장소에 아직 커밋이 없거나, 브랜치 이름이 master인 경우입니다. git branch를 확인하고, 커밋을 하거나 git branch -M main으로 이름을 바꾸세요.

Everything up-to-date인데 변경은 있음

커밋을 하지 않았습니다. git push는 파일이 아니라 커밋을 보냅니다. git status로 확인한 뒤 git add와 git commit을 하세요. 또는 기대한 것과 다른 브랜치에 있는 것일 수도 있습니다.

파일이 너무 큼

서비스마다 파일 크기 제한이 있습니다(GitHub는 100MB). 이미 히스토리에 들어간 큰 파일은 새 커밋으로 지워도 소용없습니다. 히스토리를 다시 쓰거나, 아직 보내지 않은 커밋에서 빼야 합니다(git reset --soft 후 커밋을 다시 함). 큰 바이너리 파일에는 Git LFS를 쓰세요.

push를 포함한 작업 순서

  1. git switch -c feature/x — 작업용 브랜치를 만듭니다.
  2. 작은 논리 단위로 커밋합니다.
  3. git fetch와 git rebase origin/main으로 최신 주 브랜치를 가져옵니다.
  4. git push -u origin feature/x — 브랜치를 올립니다.
  5. pull request를 열고, 리뷰 후 추가 커밋을 만들어 일반 git push를 합니다.
  6. 히스토리를 다시 썼다면 git push --force-with-lease를 씁니다.

보내기 전에 서버로 무엇이 갈지 확인하는 것이 좋습니다. git log origin/main..HEAD --oneline은 서버에 아직 없는 커밋을 보여 줍니다.

한 번만 설정해 두면 좋은 것들

몇 가지 전역 설정이 push의 놀라움을 대부분 없앱니다. push.default는 인수 없이 git push를 했을 때의 동작을 정합니다. 최신 버전의 기본값은 simple로, 현재 브랜치만 같은 이름의 upstream으로 보냅니다. 보통 바꿀 필요는 없지만, 오래된 설정에 같은 이름의 모든 브랜치를 한꺼번에 보내는 matching이 남아 있지 않은지는 확인하세요.

git config --global push.default simple
git config --global push.autoSetupRemote true
git config --global push.followTags true
git config --global pull.rebase true

push.followTags는 --follow-tags를 기본 동작으로 만들고, pull.rebase는 push가 거부되어 먼저 남의 변경을 받아야 할 때 불필요한 병합 커밋을 없앱니다.

pre-push 훅

.git/hooks/pre-push 파일은 push 전에 실행됩니다. 0이 아닌 코드로 끝나면 push는 일어나지 않습니다. 보통 테스트나 린터를 돌려서, 깨진 코드가 서버로 가지 않게 합니다. --no-verify로 훅을 건너뛰는 것은 훅이 왜 실패하는지 정확히 알 때만 하세요.

정리

git push는 여러분의 커밋을 원격 저장소에 게시하며, 한 방향으로만 동작합니다. 브랜치를 처음 올릴 때는 -u를 쓰고, 거부되면 먼저 pull로 남의 변경을 받고, 다시 쓴 자기 브랜치의 히스토리는 --force-with-lease로만 보내세요. 원격 저장소, push, pull, fetch를 실습하는 과정은 Git 강의에서 다룹니다.

자주 묻는 질문

git push는 커밋하지 않은 변경도 보내나요?

아니요. push는 커밋만 보냅니다. 작업 폴더와 인덱스의 변경은 커밋하기 전까지 로컬에 남습니다.

push를 되돌리려면 어떻게 하나요?

내 브랜치라면 로컬에서 커밋을 되돌리고(git reset) git push --force-with-lease로 보낼 수 있습니다. 공용 브랜치라면 git revert로 반대 방향의 커밋을 만들고 일반 push를 하는 것이 안전합니다.

커밋 하나만 보낼 수 있나요?

네, 해시를 지정하세요. 예를 들어 git push origin abc1234:main은 그 커밋까지의 히스토리를 보내고, 그 이후의 커밋은 보내지 않습니다. 조상 없이 커밋만 보낼 수는 없습니다.

git push와 git push origin main은 무엇이 다른가요?

인수 없이 쓰면 Git은 현재 브랜치에 설정된 upstream을 씁니다. 명시적인 형태는 remote와 브랜치를 직접 지정하므로, upstream이 설정되지 않아도 동작합니다.

두 저장소에 동시에 push하려면 어떻게 하나요?

두 번째 remote를 추가하고 이름으로 각각 보내거나, git remote set-url --add --push origin 주소로 origin에 push 주소를 여러 개 등록하세요. 이때 원래 주소도 함께 등록해야 합니다. 그렇지 않으면 두 번째 주소로만 push됩니다.

영상으로 이 주제 배우기

이 주제는 “Git 완전 정복: 초보에서 능숙한 개발자로” 강의에서 실습과 함께 자세히 다룹니다.

강의로 이동