사례로 보는 Git 트러블 슈팅
작성일:
Git은 강력하지만, 복잡한 상황에서는 개발자를 당황하게 만드는 경우가 많습니다. “앗, 방금 뭘 한 거지?” 싶은 순간은 누구에게나 찾아옵니다. 이 글에서는 checkout, stash, branch 등 핵심 명령어를 중심으로, 실제 현업에서 자주 겪는 20가지 트러블 슈팅 사례와 해결 방안을 구체적으로 정리했습니다.
Part 1: checkout 관련 문제 - “코드가 뒤섞였어요!”
checkout은 브랜치를 바꾸거나 특정 버전으로 돌아갈 때 사용하지만, 이 과정에서 많은 실수가 발생합니다.
사례 1: 다른 브랜치로 이동하려는데, 작업하던 내용이 있어서 막힐 때
- 문제 상황:
feature/new-login브랜치에서 작업하던 중, 급하게hotfix/bug-report브랜치로 이동해야 합니다.git checkout hotfix/bug-report를 입력하니 “error: Your local changes to the following files would be overwritten by checkout…” 메시지가 나옵니다. - 해결 방안: 아직 커밋하기 애매한 작업 내용을 임시 저장 공간(
stash)에 저장합니다.# 현재 작업 내용을 스택에 임시 저장 git stash > `git stash`: 현재 작업 디렉터리의 변경된 파일(Tracked files)을 임시로 스택에 저장합니다. `push`, `pop`, `apply`, `list` 등의 하위 명령어를 통해 관리할 수 있습니다. # 원하는 브랜치로 이동하여 작업 수행 git checkout hotfix/bug-report # ... 핫픽스 작업 ... git commit -m "Fix: Critical bug" git push origin hotfix/bug-report # 원래 브랜치로 복귀 git checkout feature/new-login # 임시 저장했던 작업 내용 다시 적용 git stash pop
사례 2: 실수로 파일을 삭제했는데, 커밋은 아직 안 했을 때 (rm a.txt)
- 문제 상황:
git rm이 아닌rm명령어로 파일을 삭제했습니다.git status에 “deleted: a.txt”로 표시됩니다. - 해결 방안:
checkout을 사용해 현재 브랜치(HEAD)의 마지막 커밋 상태에서 해당 파일을 복원합니다.# a.txt 파일을 마지막 커밋 상태로 복원 git checkout HEAD -- a.txtgit checkout <commit> -- <file>: 특정 커밋 상태의 특정 파일을 현재 작업 디렉터리로 복원합니다.--는 브랜치/커밋과 파일 경로를 명확하게 구분하는 역할을 합니다.– 를 사용하면 브랜치 이름과 파일 이름을 명확히 구분할 수 있어 안전합니다.
사례 3: 브랜치를 옮기지 않고 다른 브랜치의 파일 내용만 보고 싶을 때
- 문제 상황: 현재 브랜치는
feature인데,main브랜치의config.yml파일 내용과 비교하고 싶습니다. 브랜치를 통째로 옮기기엔 부담스럽습니다. - 해결 방안:
git show또는git checkout을 특정 파일에 대해서만 실행합니다.# 1. git show 사용 (단순 조회) git show main:path/to/config.yml > `git show <branch>:<path/to/file>`: 브랜치를 변경하지 않고, 다른 브랜치에 있는 파일의 내용을 터미널에 출력합니다. # 2. git checkout 사용 (현재 작업 디렉터리로 가져오기) git checkout main -- path/to/config.yml # 위 명령은 `main` 브랜치의 `config.yml`을 현재 디렉터리로 가져와 덮어씁니다. # 주의: 현재 작업 디렉터리의 내용이 변경됩니다.
사례 4: “Detached HEAD” 상태가 되었을 때
- 문제 상황:
git checkout <commit-hash>나git checkout origin/main처럼 브랜치가 아닌 포인터를 직접 체크아웃하면 “You are in ‘detached HEAD’ state.” 라는 메시지가 나옵니다. 이 상태에서 작업하고 커밋하면 해당 커밋은 어떤 브랜치에도 속하지 않게 되어 나중에 잃어버릴 수 있습니다. - 해결 방안: 현재 “Detached HEAD” 상태에서 새로운 브랜치를 만들어 작업을 이어갑니다.
# 현재 위치에서 'temp-work' 라는 새 브랜치를 생성 git checkout -b temp-workgit checkout -b <new-branch>: 현재 위치(커밋)에서 새로운 브랜치를 생성하고, 즉시 해당 브랜치로 전환합니다.이제 ‘temp-work’ 브랜치에서 안전하게 커밋을 이어갈 수 있습니다.
git add . git commit -m “Add new feature from detached state” ```
사례 5: 과거의 특정 파일 버전 하나만 현재 브랜치로 가져오고 싶을 때
- 문제 상황:
config.js파일의 과거 버전이 필요합니다. 3개의 커밋 전(HEAD~3) 버전의config.js만 현재 작업 내용에 덮어쓰고 싶습니다. - 해결 방안:
git checkout에 커밋 해시와 파일 경로를 지정합니다.# 3개 커밋 전 버전의 'config.js'를 현재 디렉터리로 가져옴 git checkout HEAD~3 -- src/config.jsgit checkout <commit> -- <file>: 특정 커밋에 해당하는 파일의 버전으로 현재 작업 디렉터리의 파일을 덮어씁니다.
Part 2: stash 관련 문제 - “작업 내용이 사라졌어요!”
stash는 매우 유용한 임시 저장 기능이지만, 여러 개가 쌓이면 혼란스러워집니다.
사례 6: Stash 이름 없이 저장해서 뭐가 뭔지 모를 때
- 문제 상황: 여러 번
git stash를 실행했더니stash@{0},stash@{1}… 목록만 있고, 어떤 내용이 저장되었는지 기억나지 않습니다. -
해결 방안 1:
git stash list로 목록을 보고,git stash show로 각 내용을 확인합니다.git stash list # stash@{0}: WIP on feature/login: ... # stash@{1}: WIP on feature/login: ... # stash@{1}의 변경 내용 요약 보기 git stash show stash@{1} # stash@{1}의 변경 내용 상세 보기 (diff) git stash show -p stash@{1}git stash list: 현재 저장된 모든 stash 목록을 최신 순으로 보여줍니다. - 해결 방안 2: 처음부터
git stash push -m "메시지"로 저장합니다. (git stash save는 더 이상 권장되지 않습니다.)git stash push -m "Finish login UI, before API integration"
사례 7: Untracked 파일(새로 만든 파일)은 stash에 포함되지 않았을 때
- 문제 상황: 새로 만든 파일(
new-component.js)과 기존 파일 수정을 함께git stash했는데, 브랜치를 옮겼다 돌아와stash pop하니 새로 만든 파일이 사라졌습니다. - 해결 방안:
stash시-u또는--include-untracked옵션을 사용합니다.# Untracked 파일까지 포함하여 stash git stash -u
사례 8: 다른 브랜치에서 작업한 내용을 현재 브랜치에 적용하고 싶을 때
- 문제 상황:
feature/A에서 작업하던 내용을 커밋 없이stash하고,feature/B로 넘어왔습니다.feature/A의 작업 내용 일부가feature/B에서도 필요합니다. - 해결 방안:
git stash branch를 사용해 stash로부터 새로운 브랜치를 생성합니다.# 현재 `feature/B` 브랜치에서... # 가장 최근의 stash (stash@{0}) 내용으로 'temp-branch' 생성 git stash branch temp-branch stash@{0} # 이제 'temp-branch'는 'feature/A'에서 작업하던 내용을 커밋할 수 있는 상태가 됩니다. # 여기서 필요한 내용을 커밋한 뒤, 'feature/B'에서 `cherry-pick` 등으로 가져올 수 있습니다.
사례 9: stash pop 실행 시 충돌(Conflict)이 발생했을 때
- 문제 상황:
stash를 한 이후, 현재 브랜치에서stash에 저장된 내용과 동일한 라인을 수정했습니다.git stash pop을 하니 충돌이 발생합니다. - 해결 방안: 일반적인
merge충돌과 동일하게 해결합니다.- 충돌이 발생한 파일을 열어
<<<<<,=====,>>>>>마커를 확인하고 코드를 올바르게 수정합니다. - 수정한 파일을
git add합니다. git stash drop을 실행하여 적용된 stash를 스택에서 제거합니다. (pop은 충돌 시 자동으로 drop되지 않습니다.)
# 1. 코드 수정 후 git add . # 2. 변경 사항 커밋 (필요 시) git commit -m "Resolve stash conflict" # 3. 적용된 stash 제거 git stash drop - 충돌이 발생한 파일을 열어
사례 10: 실수로 stash clear를 실행하여 모든 stash가 사라졌을 때
- 문제 상황: 중요한 내용이
stash에 있었는데,git stash clear로 모두 삭제해버렸습니다. - 해결 방안: Git이 아직 가비지 컬렉션(GC)을 실행하지 않았다면,
fsck로 복구할 수 있습니다.# Git 데이터베이스에서 유효성을 검사하며 "dangling" 객체를 찾음 git fsck --no-reflogs # ... 수많은 해시값 출력 ... # 위에서 찾은 "dangling commit" 해시들을 하나씩 확인하여 stash 내용을 찾음 git show <dangling-commit-hash> # 원하는 stash를 찾았다면, 해당 커밋에서 브랜치를 만들어 복구 git branch recovered-stash <dangling-commit-hash>이 방법은 복잡하고 항상 성공하는 것은 아니므로, 중요한 작업은
stash보다는 커밋으로 남기는 습관이 좋습니다.
Part 3: branch 및 병합 관련 문제 - “브랜치가 꼬였어요!”
브랜치 전략은 협업의 핵심이며, 가장 많은 문제가 발생하는 곳이기도 합니다.
사례 11: main 브랜치에 직접 커밋해버렸을 때
- 문제 상황:
feature브랜치에서 작업해야 할 내용을 실수로main브랜치에 커밋했습니다. 아직push는 하지 않았습니다. - 해결 방안:
main브랜치를 이전 커밋으로 되돌리고, 새 브랜치를 만들어 해당 커밋을 가져옵니다.# 1. 현재 `main` 브랜치에서... # 올바른 `feature` 브랜치를 생성 git branch feature/my-work # 2. `main` 브랜치를 이전 상태로 되돌림 (실수로 한 커밋 제거) # origin/main, 즉 원격 저장소의 상태로 강제 리셋 git reset --hard origin/main # 3. 이제 새 브랜치로 이동하여 작업을 이어감 git checkout feature/my-work
사례 12: 실수로 push까지 해버린 커밋을 되돌리고 싶을 때
- 문제 상황:
main에 잘못 커밋하고push까지 해버렸습니다. 다른 팀원들이pull받기 전에 빨리 조치해야 합니다. - 해결 방안:
revert를 사용하여 “잘못된 커밋을 되돌리는 새로운 커밋”을 만듭니다.# `main` 브랜치에서... # 되돌리고 싶은 마지막 커밋을 타겟으로 `revert` 실행 git revert HEAD # revert 커밋 메시지 저장 후, 변경 사항을 push git push origin mainreset --hard후push -f를 하는 것은 팀원들의 히스토리를 엉망으로 만들 수 있으므로, 공유된 브랜치에서는 절대적으로 피해야 합니다.revert가 안전한 대안입니다.
사례 13: 브랜치 이름을 변경하고 싶을 때 (로컬 & 원격)
- 문제 상황: 브랜치 이름에 오타가 있거나, 컨벤션을 따르지 않아 변경하고 싶습니다. (예:
feture/login->feature/login) - 해결 방안:
# 1. 현재 브랜치 이름 변경 git branch -m feature/login # 2. 원격의 옛날 브랜치 삭제 git push origin --delete feture/login # 3. 새로 바꾼 이름의 브랜치를 원격에 push git push origin -u feature/login
사례 14: 다른 브랜치의 특정 커밋 하나만 현재 브랜치로 가져오고 싶을 때
- 문제 상황:
hotfix브랜치에서 수정한 버그 픽스 커밋(a1b2c3d)이 현재 개발 중인feature브랜치에도 즉시 필요합니다. - 해결 방안:
cherry-pick을 사용합니다.# `feature` 브랜치에서... git cherry-pick a1b2c3d
사례 15: 너무 자잘하게 나눠진 커밋들을 하나로 합치고 싶을 때 (Push 전)
- 문제 상황: “Fix typo”, “Add comment”, “Refactor” 등 의미 없는 커밋이 너무 많아 PR 올리기 전에 정리하고 싶습니다.
- 해결 방안:
rebase -i(interactive rebase)를 사용합니다.# 합치고 싶은 커밋의 *이전* 커밋 해시를 지정 (예: 최근 3개) git rebase -i HEAD~3- 위 명령을 실행하면 편집기가 열리고, 커밋 목록이 나옵니다.
- 맨 위 커밋은
pick으로 두고, 나머지 커밋 앞의pick을s또는squash로 변경합니다. - 저장하고 닫으면, 커밋 메시지를 새로 작성하는 창이 열립니다. 하나의 깔끔한 메시지로 정리합니다.
Part 4: 기타 고급 트러블 슈팅
사례 16: 실수로 git add 한 파일을 다시 내리고 싶을 때
- 문제 상황: 커밋하고 싶지 않은 파일(
secret.key)을 실수로 Staging Area에 추가(add)했습니다. - 해결 방안:
git reset을 사용합니다.# 특정 파일만 Staging Area에서 내림 git reset HEAD secret.key # 모든 파일을 Staging Area에서 내림 git reset
사례 17: 마지막 커밋 메시지에 오타가 있을 때
- 문제 상황: 방금 커밋했는데, 메시지에 오타가 있습니다. 아직
push는 안했습니다. - 해결 방안:
commit --amend를 사용합니다.git commit --amend # 편집기가 열리면 메시지를 수정하고 저장주의:
push한 커밋에는 절대 사용하면 안 됩니다.
사례 18: 브랜치를 삭제했는데, 다시 복구해야 할 때
- 문제 상황:
git branch -D my-feature로 브랜치를 강제 삭제했는데, 아직main에 병합되지 않은 중요한 커밋이 있었습니다. - 해결 방안:
reflog를 사용하여 Git의 모든 참조 변경 기록을 확인합니다.git reflog # ... # a1b2c3d HEAD@{5}: checkout: moving from my-feature to main # ... # 삭제된 브랜치의 마지막 커밋 해시(a1b2c3d)를 찾았다면, # 해당 커밋에서 브랜치를 다시 생성 git checkout -b recovered-feature a1b2c3d
사례 19: 현재 작업 내용 전체를 특정 과거 커밋 시점으로 되돌리고 싶을 때
- 문제 상황: 최근 몇 개의 커밋이 완전히 잘못된 방향으로 진행되어,
a1b2c3d커밋 시점으로 모든 것을 되돌리고 싶습니다. 로컬에서의 변경 사항이므로 히스토리가 깨져도 상관없습니다. - 해결 방안:
reset --hard를 사용합니다. (경고: 복구 불가능한 데이터 손실을 유발할 수 있습니다!)# 모든 변경사항, 커밋을 버리고 'a1b2c3d' 상태로 돌아감 git reset --hard a1b2c3d
사례 20: pull을 받았더니 관련 없는 커밋 히스토리가 잔뜩 병합될 때
- 문제 상황:
main브랜치에서feature브랜치를 따서 작업 후,feature브랜치에서git pull origin main을 실행했습니다. 의도와 달리 수많은main의 커밋들이Merge branch 'main' into feature라는 메시지와 함께 들어왔습니다. - 해결 방안:
pull시rebase옵션을 사용하거나,git config로 기본값으로 설정합니다.Rebase는main의 변경사항을 내 브랜치의 “베이스”로 다시 설정하여, 내 커밋들을 그 위에 차곡차곡 쌓아줍니다. 히스토리가 훨씬 깔끔해집니다.# 1. 일회성으로 rebase pull 실행 git pull origin main --rebase # 2. 향후 모든 pull을 rebase로 실행하도록 설정 git config --global pull.rebase true
사례 21: 특정 커밋을 찾기 위해 로그를 효율적으로 검색하고 싶을 때
- 문제 상황: 프로젝트 히스토리가 길어져서 특정 기능이 언제 추가되었는지, 특정 파일이 언제 마지막으로 수정되었는지 찾기 어렵습니다.
- 해결 방안:
git log의 다양한 옵션을 활용하여 히스토리를 필터링합니다.# 한 줄로 로그를 깔끔하게 보기 git log --oneline --graphgit log --oneline: 각 커밋을 한 줄로 요약하여 보여줍니다.--graph옵션을 함께 사용하면 브랜치의 분기 및 병합 히스토리를 시각적으로 파악하기 좋습니다.# 특정 파일의 변경 이력만 보기 git log -p -- src/components/Button.js # 특정 기간 동안의 커밋만 보기 git log --since="2 weeks ago" # 특정 작성자의 커밋만 보기 git log --author="cdecl" # 커밋 메시지에서 특정 키워드로 검색하기 git log --grep="keyword" # 두 커밋 사이의 모든 커밋 보기 git log a1b2c3d..f4e5d6c
Git 트러블 슈팅의 핵심은 “무엇을 하려 했는가”와 “실제로 무슨 일이 일어났는가”의 차이를 이해하는 것입니다. 이 글에서 소개된 사례들을 통해, 예상치 못한 상황에 더 자신감 있게 대처할 수 있기를 바랍니다.
댓글남기기