[번역] 나는 수년간 Git을 잘못 사용했다
Soshy··조회수 —

한때는 Git 명령어 세 개만 알면 된다고 생각했다.
git add .
git commit -m "update"
git push
수년간 이렇게 일했다.
어느 날, 한 시니어 개발자가 삭제된 커밋(commit)을 복구하고, 6개월 동안 뒤죽박죽 쌓인 이력을 정리하고, 잘못된 병합까지 되돌리는 모습을 봤다. 검색 한 번 하지 않고, 당황하는 기색도 없이.
그때도 나는 여전히 이런 걸 검색하고 있었다. “마지막 커밋을 취소하면서 변경 사항은 유지하는 법”
그 순간 깨달았다.
대부분의 개발자는 Git을 겨우 써먹을 만큼만 안다. 빠르게 일하는 개발자는 Git이 실제로 어떻게 작동하는지 안다.
지금부터 소개할 것은 내가 너무 늦게 배운 Git 활용법과 습관이다. 매일 일하는 방식을 실제로 바꿔 놓은 것들이다.
핵심부터 말하자면
시니어 개발자가 Git을 더 빠르게 다루는 건 명령어를 더 많이 외워서가 아니다.
Git을 타임라인(timeline), 즉 프로젝트가 지금까지 거쳐 온 모든 상태의 기록으로 이해하기 때문이다. 이 개념을 이해하고 나면 모든 명령어가 직관적으로 와닿기 시작한다.
Git은 단순한 버전 관리 도구가 아니다
오랫동안 나는 Git을 백업(backup) 시스템처럼 사용했다.
수정 → 커밋 → 푸시(push).
하지만 Git은 프로젝트의 일생을 담은 타임라인에 더 가깝다.
이걸 이해하고 나니 명령어가 더 이상 무섭지 않았다.
실수를 수습하는 것도 훨씬 쉬워졌다.
활용법 1: 가장 중요한 명령어는 git status다
뻔한 이야기처럼 들린다. 나도 수년간 무시했다.
이제는 수시로 실행한다. 커밋하기 전에도, 브랜치(branch)를 바꾸기 전에도, 리베이스(rebase)하기 전에도.
git status
Git에서 실수하는 건 대부분 현재 상태를 정확히 모를 때다. 나도 이 단계를 건너뛰었다가 .env 파일, 디버그(debug) 로그(log), 미완성 코드, 별생각 없이 찍은 스크린샷(screenshot)까지 실수로 커밋한 적이 있다.
명령어 하나, 2초면 된다. 그러고도 몇 시간을 아낀다.
활용법 2: git diff는 민망한 커밋을 막아 준다
이 명령어는 내 커밋 습관을 완전히 바꿔 놓았다.
커밋하기 전에 실행한다.
git diff
무엇이 바뀌었는지 정확히 보여 준다. 운영 환경에 푸시하기 직전에 API 키, 잘못된 import, 지우지 않고 남겨 둔 console.log를 발견한 적이 있다.
더 좋은 명령어도 있다.
git diff --staged
이미 스테이징(staging)한 파일의 변경 사항만 보여 준다. 이제는 거의 매번 커밋하기 전에 실행한다.
활용법 3: 생각 없이 git add .를 쓰지 말자
git add .는 모든 변경 사항을 스테이징한다.
그렇게 해도 괜찮을 때가 있다. 하지만 작업하다 건드린 다른 네 가지까지 함께 커밋하려는 게 아니라, 특정 변경 사항 하나만 커밋하고 싶을 때가 많다.
그럴 때는 이 명령어를 쓰자.
git add -p
변경 사항을 부분별로 추가할 수 있다. 각 변경 묶음을 검토하고, 스테이징할지 건너뛸지 결정한다.
실제로 나는 한 파일에서 로그인(login) 문제 수정, UI 재설계, 이런저런 정리 작업을 동시에 진행한 적이 있다. git add -p 덕분에 로그인 문제를 수정한 부분만 깔끔하게 커밋할 수 있었다.
커밋이 깔끔해진다. 나중에 디버깅(debugging)하기도 쉬워진다. 30초를 더 쓸 가치는 충분하다.
git add -p를 배우고 커밋은 깔끔해졌지만, 나중에는 내가 엉뚱한 문제를 해결하고 있었다는 걸 깨달았다.
애초에 Git에 나타나지도 않아야 하는 파일이 있었던 것이다. 그제야 .gitignore, .git/info/exclude, 전역 Git 무시 파일의 차이를 제대로 배웠다. 궁금한 사람을 위해 최근에 자세한 안내 글도 썼다.
Git에서 파일을 무시하는 방법은 세 가지다
대부분의 개발자는
.gitignore만 사용한다. 다른 방법은 언제 쓰면 좋을까?
활용법 4: git bisect를 쓰면 마법사가 된 기분이 든다
너무 고급 기능처럼 보여서 수년간 외면했던 명령어다.
이런 상황을 생각해 보자. 운영 환경에서 뭔가 망가졌다. 3주 전에는 잘 작동했다는 것도 안다. 하지만 그사이에 쌓인 커밋 80개 중 어느 것이 문제를 일으켰는지는 전혀 모른다.
git bisect start
git bisect bad # 현재 상태에는 문제가 있다
git bisect good v2.3.1 # 이 버전은 정상이었다
그러면 Git은 정상인 커밋과 문제가 있는 커밋 사이의 정확히 중간 지점에 있는 커밋을 체크아웃(checkout)한다.
직접 테스트(test)해 보고, 정상인지 문제가 있는지 Git에 알려 준다.
Git은 다시 탐색 범위를 절반으로 줄인다. 이 과정을 일곱 번 정도 반복하면 커밋 80개 중 하나를 찾아낼 수 있다.
한 달 동안 해결하지 못한 버그(bug)에 이 방법을 써 봤다. 정확히 어떤 커밋이 원인인지 찾는 데 11분이 걸렸다. 커밋 메시지(message)는 “사소한 정리”였다. 전혀 사소하지 않았다.
활용법 5: git stash는 구세주다
이 명령어 덕분에 위기를 넘긴 게 셀 수 없이 많다.
이런 상황이다.
기능을 절반쯤 구현했는데 긴급한 버그가 발생했다. 당장 브랜치를 바꿔야 한다.
예전 같으면 당황했을 것이다.
이제는 이렇게 한다.
git stash
작업 내용이 안전한 보관함으로 들어간다. 브랜치를 바꾸고 버그를 수정한 다음 돌아와서 실행한다.
git stash pop
모든 것이 떠나기 전 상태 그대로 돌아온다.
브랜치는 기본적으로 샌드박스(sandbox) 환경이다. git stash는 그 사이를 오갈 때 쓰는 긴급 일시 정지 버튼이다.
활용법 6: git log 덕분에 마침내 Git이 이해됐다
수년간 Git 이력은 내게 잡음처럼 보였다.
그러다 이 명령어를 쓰기 시작했다.
git log --oneline --graph --decorate
갑자기 프로젝트의 전체 타임라인이 눈에 들어왔다. 브랜치와 병합, 커밋 흐름까지 모든 것이 명확하게 펼쳐졌다.
곧바로 별칭(alias)을 만들었다.
alias gl='git log --oneline --graph --decorate'
이제 gl은 매일 작업하면서 가장 자주 쓰는 명령어 중 하나다.
아직 별칭이 익숙하지 않거나 터미널(terminal)을 거의 기본 설정 그대로 쓰고 있다면, 이 내용을 다룬 별도의 터미널 활용 글도 썼다. 「나는 수년간 터미널을 잘못 사용했다」다.
이 글과 함께 읽어 볼 만하다.
활용법 7: 브랜치는 부담 없이 만들 수 있다. 더 자주 쓰자
main에서 직접 작업하는 건 함정이다. 나는 너무 오랫동안 그렇게 했다.
이제는 거의 모든 작업에서 브랜치를 만든다.
git switch -c fix-login-bug
덕분에 훨씬 과감하게 실험하게 됐다. 안정적으로 작동하는 코드를 건드리지 않고도 아이디어를 시험하고, 대대적으로 리팩터링(refactoring)하고, 위험 부담이 있는 수정도 시도할 수 있다.
브랜치를 만드는 데는 비용이 들지 않는다. main을 망가뜨리면 비용이 든다.
브랜치는 기본적으로 샌드박스 환경이다.
말하자면 이런 셈이다.
Git 브랜치용 cd -.
활용법 8: 필요해지기 전에 git restore를 익혀 두자
이 명령어 덕분에 가슴이 철렁할 상황을 넘겼다.
실수로 엉뚱한 파일을 수정했다.
변경 사항을 일일이 되돌리는 대신 이렇게 했다.
git restore app.js
파일이 마지막 커밋 상태로 돌아간다. 즉시.
Git을 배운 첫 달에 알았더라면 좋았을 것이다.
활용법 9: git worktree는 지나치게 저평가돼 있다
레딧(Reddit)에서 「논쟁적인 의견: Worktree는 저평가돼 있다」라는 제목의 글을 보고 끝까지 읽었다. 그 의견은 맞았다.
보통 기능을 구현하다가 다른 작업으로 전환해야 하면 이렇게 했다.
- 변경 사항 임시 보관
- 브랜치 전환
- 긴급 수정
- 원래 브랜치로 복귀
- 임시 보관한 변경 사항 복원
- 충돌이 없기를 기도
git worktree는 다르다.
git worktree add ../hotfix-branch hotfix/login-timeout
이 명령어는 hotfix/login-timeout을 별도 디렉터리(directory)에 체크아웃한다.
현재 브랜치는 그대로 유지된다. 다른 디렉터리로 cd해서 버그를 수정하고 푸시한 뒤, 다시 cd로 기능을 구현하던 곳으로 돌아오면 된다.
변경 사항을 임시 보관할 필요도, 체크아웃할 필요도 없다. “아, 내가 어디까지 하고 있었더라” 하며 헤맬 일도 없다.
솔직히 말하면, 아직 이 명령어가 반사적으로 손에 익은 건 아니다. 여전히 조금 낯설다. 하지만 한 시간 넘게 다른 작업을 해야 한다면, 변경 사항을 임시 보관하고 무사하기를 비는 방식보다 훨씬 깔끔하다.
활용법 10: git reflog는 시간 여행 같다
시니어 개발자를 마법사처럼 보이게 하는 명령어다.
한번은 리베이스하다가 커밋을 영영 잃어버렸다고 생각한 적이 있다.
대부분의 개발자가 모르는 사실이 있다. Git은 거의 모든 것을 기억한다.
git reflog
최근 Git 작업을 모두 보여 준다. 삭제했다고 생각한 커밋, 없애 버린 브랜치, 리셋(reset)하면서 지나쳐 버린 상태까지도.
복구에는 명령어 하나면 된다.
git checkout <commit-id>
그 경험은 Git에서 실수하는 것에 대한 내 생각을 완전히 바꿨다. 이제는 실수가 훨씬 덜 두렵다.
git reflog는 안전망이다. 작업 후 대략 90일 동안 기록이 남는다. 무언가 사라졌다고 단정하기 전에 먼저 확인하자.
활용법 11: 좋은 커밋 메시지는 생각보다 중요하다
예전의 나:
git commit -m "fix"
미래의 나는 그때의 나를 미워했다.
지금은 이렇게 쓴다.
git commit -m "Fix token refresh issue in auth middleware"
좋은 커밋 메시지는 Git을 위한 것이 아니다. 사람을 위한 것이다. 대개는 6개월 뒤 어느 목요일 밤 11시에, 이걸 왜 바꿨는지 알아내려는 미래의 나를 위한 것이다.
활용법 12: 대화형 리베이스 덕분에 이력이 깔끔해졌다
위험하게 들린다는 이유로 수년간 git rebase를 피했다.
그러다 이 명령어를 배웠다.
git rebase -i HEAD~5
PR을 올리기 전에 커밋을 하나로 합치고, 메시지를 바꾸고, 순서를 재배치할 수 있다.
이전:
- 수정
- 또 수정
- 작은 변경
- 최종 수정
이후:
- 인증 오류 처리 개선
깔끔하고 의미 있는 커밋 하나가 된다. 팀원들도 그 차이를 알아볼 것이다.
활용법 13: git blame은 의외로 유용하다
무서운 이름과는 달리 말이다.
git blame app.js
각 줄을 누가, 언제, 어떤 커밋에서 변경했는지 보여 준다.
나는 이 명령어로 운영 환경의 버그를 유발한 정확한 커밋을 추적하고, 이상한 코드가 존재하는 이유를 파악하고, 서로 연결돼 있는 줄 몰랐던 관련 커밋을 찾았다.
말하자면 코드 고고학이다. 찾던 것을 발견했을 때 느끼는 만족감도 의외로 크다.
활용법 14: 별칭 덕분에 번거로움이 줄었다
긴 명령어를 입력하는 데 지쳤다.
그래서 별칭을 추가했다.
alias gs='git status'
alias ga='git add'
alias gc='git commit -m'
alias gp='git push'
alias gl='git log --oneline --graph --decorate'
별칭 하나로 아끼는 시간은 고작 3초 정도일 수 있다. 하지만 하루 50번씩 몇 달을 쓰면 그 시간이 쌓인다. 더 중요한 건 번거로움이 줄어들면, 실행해야 할 명령어를 실제로 실행하게 된다는 점이다.
터미널 활용법에 관한 글도 썼다.
나는 수년간 터미널을 잘못 사용했다
시니어 개발자는 터미널을 어떻게 다르게 사용할까?
진짜 달라진 점
수년간 나는 Git을 잘 다룬다는 것이 명령어를 외우는 일이라고 생각했다.
이제는 대부분 여기에 달려 있다고 생각한다.
문제가 생겼을 때 침착함을 유지하는 것.
내가 아는 뛰어난 개발자들은 Git을 두려워하지 않는다.
복구하는 방법을 이해하기 때문이다.
진짜 차이는 거기에 있다.
타자를 빨리 치는 것도 아니다.
화려한 작업 방식도 아니다.
자신감이다.
그리고 솔직히 Git에 대한 자신감은 대부분 다음을 배우는 데서 나온다.
- 변경 사항을 확인하는 방법
- 실수를 되돌리는 방법
- 안전하게 복구하는 방법
이것들을 알고 나면 Git이 더 이상 위험하게 느껴지지 않는다.
다음 명령어부터 시작하자.
git diff
git stash
git reflog
이 세 개만으로도 셀 수 없이 많은 시간을 아낄 수 있다.
마무리
대부분의 개발자는 Git을 백업 도구처럼 쓴다.
더 능숙한 개발자는 Git을 타임머신(time machine)처럼 쓴다.