SPACE LOGBOOK

OCEAN LOGBOOK

Git Pull 과 Git Reset ..합치느냐 덮어쓰느냐..

2026.08.28

Git Pull 과 Git Reset ..합치느냐 덮어쓰느냐..

2026.08.28

서버-로컬 다중 PC로 작업하다가 git fetch와 git pull, 그리고 git reset --hard origin/main의 차이가 헷갈려서 파고든 대화를 정리했다. “오리진/메인이 도대체 뭘 가리키는 건지”부터 “왜 어떤 구조에서는 Git이 사고를 막아주고, 어떤 구조에서는 조용히 데이터가 사라지는지”까지, 개념이 잡히는 순서대로 정리해본다.

1. 로컬 저장소의 3개 포인터부터 이해하기

Git을 다루다 헷갈리는 대부분의 원인은 로컬 저장소 안에 성격이 다른 3가지 상태가 동시에 존재한다는 걸 놓치기 때문이다.

로컬 저장소 안의 3가지 상태 origin/main 원격 추적 브랜치 “GitHub에 뭐가 있는지” 로컬에 캐시해둔 사진 main (로컬 브랜치) 내 작업 포인터 “내 커밋 히스토리가 어디까지 왔는지” 가리키는 포인터 워킹트리 + 스테이징 실제 폴더 지금 내 폴더에 실제로 존재하는 파일들 (커밋 전 포함) ▶ git fetch 는 ①번(origin/main)만 갱신한다 — ②, ③번은 그대로 남는다 그래서 fetch 직후엔 “①은 앞서있는데 ②는 그대로”인 상태가 된다

그림 1. origin/main · main · 워킹트리는 서로 갱신 시점이 다른 별개의 상태다.

핵심: git fetch는 ①번 “사진”만 최신화할 뿐, ②번 내 작업 포인터나 ③번 실제 파일은 건드리지 않는다. 이 갭을 어떻게 메울지가 pull이냐 reset --hard냐의 갈림길이다.

2. git pull vs git reset –hard — 동작 방식 차이

git pull = fetch + merge

git pull은 fetch로 origin/main을 최신화한 뒤, 그 내용을 로컬 main에 병합한다. 내 로컬 히스토리와 원격 히스토리를 둘 다 유지하며 합치는 방식이라, 같은 부분을 양쪽에서 건드렸으면 충돌이 나고 사람이 수동으로 풀어야 한다.

git reset –hard origin/main = 강제 동기화

반면 이건 병합을 시도하지 않는다. 로컬 main 포인터를 origin/main 지점으로 강제 이동시키고, 워킹트리도 그 지점 내용으로 강제 덮어쓰기한다. 이 과정에서 로컬에만 있던 커밋이나 저장 안 한 변경사항은 흔적 없이 사라진다.

git pull vs git reset –hard git pull 1. fetch로 origin/main 갱신 2. 로컬 main과 병합(merge) 시도 3. 로컬 변경사항은 보존됨 4. 충돌 나면 수동 해결 필요 5. 병합 커밋이 남을 수 있음 → 안전하지만 지저분해질 수 있음 git reset –hard origin/main 1. main 포인터를 origin/main 지점으로 강제 이동 (병합 없음) 2. 워킹트리를 그 지점 내용으로 강제 덮어쓰기 3. 로컬 커밋·변경사항 전부 소실 → 깨끗하지만 되돌리기 어려움

그림 2. pull은 “합친다”, reset –hard는 “덮어쓴다”.

git pullgit reset –hard origin/main
로컬 커밋/변경사항보존하며 병합 시도전부 버림
충돌 가능성있음 (수동 해결 필요)없음 (애초에 합치지 않음)
결과 히스토리병합 커밋이 남을 수 있음origin과 완전히 동일
안전성로컬 작업 보존됨되돌리기 어려움 — 위험
서버-로컬 두 PC를 오가며 작업하는 구조라면, “이 PC에 남아있던 stale한 변경사항”이 원격 커밋과 충돌하거나 히스토리를 지저분하게 만드는 걸 막기 위해 매번 fetch && reset --hard로 완전히 새로 시작하는 편이 안전하다. 단, 워킹트리가 clean한 상태인지 먼저 확인하는 게 전제다.

3. origin/main은 GitHub가 아니라 “로컬에 찍어둔 사진”이다

가장 헷갈리기 쉬운 지점이다. origin/main은 GitHub 서버에 실시간으로 접속해서 확인하는 게 아니라, .git 폴더 안에 저장된 스냅샷이다.

비유하면 이렇다 — GitHub 서버의 진짜 main은 친구 집 냉장고 안의 실제 내용물이고, 내 로컬의 origin/main은 친구가 마지막으로 보내준 냉장고 사진이다. 이 사진은 내가 fetch/pull/push를 실행하는 순간에만 갱신된다.

갱신 시점이 서로 다르다 GitHub 서버의 진짜 main 누군가 push할 때마다 실시간 갱신 fetch 로컬의 origin/main (사진) 내가 fetch/pull/push 실행할 때만 갱신 reset/merge 로컬 main (내 작업 포인터) commit/merge/reset 실행할 때만 갱신 그 사이 GitHub가 바뀌어도, fetch 전까지 로컬의 “사진”은 예전 그대로다

그림 3. fetch는 사진만 새로 찍어오는 동작이다.

git reset --hard origin/main이 두 단계로 이뤄진다는 점도 짚어둘 만하다.

  1. 포인터 이동: 로컬 main을 origin/main이 가리키는 커밋 지점으로 옮김 (아직 파일은 안 건드림)
  2. 워킹트리 동기화: 그 지점 내용에 맞춰 실제 폴더 파일을 강제로 덮어씀 (없던 파일은 삭제, 있던 파일은 교체)

4. 일반적인 다중 PC 구조에서 벌어지는 일

여러 PC가 각자 GitHub에 직접 commit·push하는 구조에서, 한 PC가 fetch/pull을 깜빡하고 작업 후 push를 시도하면 어떻게 될까.

결론: 안전하다. commit 자체는 로컬 작업이라 문제없이 성공하지만, push 시점에 Git이 “내가 알던 origin/main과 실제 origin/main이 다르다(non-fast-forward)”는 걸 자동으로 감지해서 push를 거부한다.
! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs
hint: Updates were rejected because the tip of your current branch is behind

경고 후 그대로 진행되는 게 아니라, 애초에 반영 자체가 안 된다. 해결하려면 fetch 후 pull(merge/rebase)로 원격 커밋을 먼저 반영하고, 충돌을 풀고 나서 다시 push하면 된다. (단, --force를 쓰면 이 안전장치를 무시하고 강제로 덮어쓸 수 있다 — 이건 예외적으로 위험한 경우다.)

5. 서버 단일 push 구조에서 벌어지는 일 (진짜 위험 구간)

그런데 로컬이 작업장, 서버가 배포·commit·push 지점인 구조 — 즉 여러 PC(A, B)가 각자 서버에 배포하고, 서버 한 곳에서만 commit+push하며, 다른 PC들은 fetch && reset --hard origin/main으로 동기화만 하는 구조 — 에서는 이야기가 완전히 달라진다.

서버 단일 push 구조에서의 사고 시나리오 ① A PC에서 작업 → 서버 배포 → 서버 commit+push GitHub main = 서버 main = A의 변경사항 포함, 완전 일치 ② B PC, fetch/pull 빼먹고 (구버전 기준) 작업 → 서버에 배포 (A의 변경사항이 반영 안 된 파일로 서버 워킹트리 덮어씀) ③ 서버에서 commit → push 서버의 main은 origin/main 바로 위였으므로 fast-forward 왜 Git이 못 막나 Git이 검사하는 건 “커밋 히스토리의 흐름”이지, “파일 내용에 A의 변경사항이 들어있는지”가 아니다. 서버 입장에선 그저 “내 마지막 커밋 위에 새 커밋을 얹었다”로 보일 뿐 → push 성공. 결과: 새 커밋의 스냅샷엔 A의 변경사항이 빠진 (구버전) 파일이 담긴 채로 GitHub 최신이 됨 이후 다른 PC가 reset –hard로 동기화하면 전부 “A 변경사항 없는” 상태로 맞춰짐 (단, reflog로 A의 커밋 객체 자체는 한동안 복구 가능)

그림 4. 일반 구조는 push가 “거부”되지만, 이 구조는 조용히 “덮어써진다”.

핵심 차이: 여러 명이 각자 GitHub에 직접 push하는 구조는 Git이 충돌을 감지해서 막아준다. 반면 서버 단일 push점 + 여러 PC가 구버전 기준으로 배포하는 구조는 Git이 감지할 방법이 없어서 조용히 덮어써진다. A의 작업은 브랜치 최신 상태(HEAD)만 보면 반영 안 된 것처럼 보이고, 아무도 눈치채지 못하면 사실상 유실된 것과 같다.

이게 바로 “새 작업 시작 전 반드시 fetch && reset --hard로 동기화”를 원칙으로 못박아야 하는 이유다. B PC가 서버에 배포하기 전에 먼저 origin/main 최신 상태로 맞춰뒀다면, 애초에 A의 변경사항이 담긴 상태 위에서 작업했을 것이고 이런 사고 자체가 성립하지 않는다.

정리

  • git fetch는 로컬의 origin/main “사진”만 최신화한다 — 내 작업 포인터나 파일은 그대로다.
  • git pull은 로컬 변경사항을 보존하며 병합한다 — 안전하지만 충돌·병합 커밋이 생길 수 있다.
  • git reset --hard origin/main은 로컬 상태를 전부 버리고 origin 기준으로 강제 동기화한다 — 깨끗하지만 되돌리기 어렵다.
  • 여러 명이 직접 GitHub에 push하는 일반 구조는 non-fast-forward 거부로 안전이 보장된다.
  • 서버가 유일한 push 지점인 구조에서는 이 안전장치가 작동하지 않으므로, 작업 시작 전 동기화를 습관화하는 것 외엔 답이 없다.

댓글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

이 기록은 여기까지지만,
생각은 아직 이동 중이다