안녕하세요. 인포그랩 DevOps Engineer Chris입니다. Git은 SVN과 더불어 오늘날 개발자가 널리 사용하는 버전 관리 시스템(Version Control System, 이하 VCS)입니다.

현재 Git을 매일 사용하지만 git commitgit push만 반복하고 계시나요? 그렇다면 Git의 강력한 기능을 제대로 활용하지 못하는 건지도 모릅니다.

버전 관리를 효율화하고, 개발 생산성을 끌어 올리려면 반드시 알아야 할 Git 명령어가 있습니다. git rebase -i, git reflog, git worktree 등이 그 예죠. 오늘은 실무에서 자주 사용하고, 협업과 개발 효율을 극대화하는 필수 Git 명령어 10가지를 소개하겠습니다.

Git?

먼저 리마인드 차원에서 Git의 역사와 장점을 살펴보겠습니다. Git은 리눅스 커널의 VCS로 사용하는 BitKeeper가 유료화되면서 ‘무료 오픈 소스’ 대안이 필요해 개발됐습니다. Git 개발의 목표는 아래와 같습니다.

  • 빠른 속도
  • 심플한 디자인
  • 비선형 개발 (수많은 병렬 브랜치 지원)
  • 완전 분산형 (오프라인 개발 지원)
  • 리눅스 커널과 같은 대규모 프로젝트 관리

Git의 핵심 특징은 ‘완전 분산형 구조’입니다. 모든 개발자는 각자 컴퓨터에 전체 코드 히스토리를 저장하고, 인터넷 연결 없이도 변경 사항을 추적하며 작업할 수 있죠. 그 결과, 소스를 잃어버릴 위험이 줄고, 개발자 간 협업 시 병목 현상이 최소화됩니다. 이로써 더 효율적으로 개발할 수 있고요.

이제 Git을 효과적으로 활용하도록 돕는 필수 명령어 10개와 활용법을 자세히 알아보겠습니다.

git rebase -i: 커밋 내역 정리

첫 번째로 소개할 명령어는 git rebase -i입니다. 저도 평소 자주 사용하는 명령어죠. 로컬 커밋 내역이 있는 상태로 git rebase -i를 사용하면, 아래와 같이 커밋 내역이 나오고요. 다양한 명령어로 커밋 내역을 정돈할 수 있습니다.

`git rebase -i`를 입력해 커밋 내역이 표시된 모습 | 인포그랩 GitLab
git rebase -i를 입력해 커밋 내역이 표시된 모습

이 상태로 각 커밋에 커밋 메시지 수정, 커밋 내용 삭제, 커밋 합치기도 가능한데요. 특히 ‘커밋 합치기’ 기능인 squash는 실무에서 매우 유용하죠. git rebase -i에서 squash 옵션 활용법을 예제와 함께 알아보겠습니다.

저는 위에 나온 커밋 로그 중 16e7f02 .. d391561까지 커밋을 모두 합치고, 커밋 메시지를 fix: 암복호화 모듈 프록시 로직 오류 수정으로 변경하려고 했는데요. 먼저 vi 편집기에서 아래 형태로 편집했습니다.

`git rebase -i` 상태에서 편집한 내용 | 인포그랩 GitLab
git rebase -i 상태에서 편집한 내용

이 상태에서 :wq를 실행했습니다. pick 커맨드부터 squash 커맨드까지 모든 커밋 메시지가 병합돼 기본값으로 나왔습니다. 커밋 메시지도 수정할 수 있죠.

`squash` 결과 | 인포그랩 GitLab
squash 결과

여기서 불필요한 커밋 메시지를 모두 삭제하고, 커밋 메시지를 fix: 암복호화 모듈 프록시 로직 오류 수정으로 수정했습니다.

커밋 메시지를 수정한 결과 | 인포그랩 GitLab
커밋 메시지를 수정한 결과

:wq로 빠져나와 아래와 같이 병합된 커밋 로그를 확인했습니다. 이제 커밋 정리가 완료됐습니다.

`git rebase`로 병합된 커밋 로그 | 인포그랩 GitLab
git rebase로 병합된 커밋 로그

혹자는 ‘git rebasegit merge도 있는데 왜 굳이 git rebase -i를 써야 하나?’라고 생각할 수도 있죠. git rebase로 선형적인 히스토리를 만들면 프로젝트의 히스토리를 파악하는 데 도움이 됩니다. 그러나 매번 git rebase를 활용해 프로젝트를 관리하면 개별 커밋 단위로 충돌을 해결해야 해 피로합니다.

Jira, Wiki Confluence 등을 개발한 Atlassian은 ‘git rebase를 개인 브랜치(개인 업무 공간) 정리에 사용하고, git merge를 공개 브랜치(협업 공간)에 사용하라’고 권장합니다. 그러나 git rebase -i를 활용하면, 원격 리포지터리에 push 할 최종 커밋 내역을 원하는 대로 다듬을 수 있어 더 편리합니다.

참고

git rebasegit merge 차이

특징git rebasegit merge
히스토리 형태선형적인 히스토리 생성브랜치의 병합 지점이 명시적으로 보임
병합 커밋생성되지 않음병합 시점에 새로운 병합 커밋 생성

git reflog: HEAD 이동 추적

git rebase를 실행하면, Git이 가리키는 HEAD 포인터가 트리에서 이동해 히스토리상 보이지 않습니다. 이러한 커밋은 ‘고아 커밋(Orphan Commit)’ 상태가 되고요. 30일(기본값) 커밋 만료 기간 이후, Git GC(Garbage Collector)가 이를 정리합니다.

git reflog를 사용하면, HEAD 포인터의 이동을 자세히 추적할 수 있습니다. 즉, git rebase를 실행하기 전과 후 모든 HEAD 포인터 이동 이력을 확인할 수 있죠.

`git reflog` 입력 시 로그 | 인포그랩 GitLab
git reflog 입력 시 로그

HEAD 포인터가 앞뒤로 트리를 이동한 커밋 히스토리더라도, 커밋 만료 전에 git reflog를 실행해 이전 커밋을 쉽게 찾을 수 있습니다. 이전 커밋으로 돌아가려면 git reset ${Commit SHA-1}을 실행합니다. 그러면 HEAD 포인터를 이동할 수 있습니다.

`git reflog`로 이전 커밋을 찾은 뒤, `git reset`으로 복원한 뒤 `git rebase -i` 화면 | 인포그랩 GitLab
git reflog로 이전 커밋을 찾은 뒤, git reset으로 복원한 뒤 git rebase -i 화면

git worktree: 브랜치 전환 없이 동시 작업

개발할 때 핫픽스(Hotfix) 사항이 발생하면, 기존 작업을 제쳐 두고 우선순위에 따라 다른 일을 병행하기도 하죠. 이때 git stash를 실행해 작업 중인 스테이징 영역의 내용을 보류하고, 브랜치를 이동해 동시 작업할 때가 많은데요.

git worktree를 사용하면, 전용 워킹 디렉터리를 하나 더 만들어 브랜치를 생성하고요. 워킹 디렉터리에서 다른 브랜치를 참조하며, 브랜치 전환 없이도 작업을 효율적으로 병행할 수 있습니다. git worktree 사용법을 예제로 살펴보겠습니다.

아래 명령어를 실행하면, Git 워킹 디렉터리가 proxy-dev-work라는 이름으로 생성됩니다.


# git worktree add -b ${생성 브랜치} ${워킹디렉터리 지정폴더} ${대상 브랜치}
git worktree add -b hotfix/proxy ../proxy-dev-work main
**proxy-dev-work**라는 이름으로 Git 워킹 디렉터리가 생성된 모습 | 인포그랩 GitLab
proxy-dev-work라는 이름으로 Git 워킹 디렉터리가 생성된 모습

이때 git branch -a로 브랜치 목록을 확인할 수 있습니다. 자기 워킹 디렉터리 외에 참조하는 브랜치는 “+”로, 자기 워킹 디렉터리에서 참조하는 브랜치는 “*”로 표시됩니다.

`git branch -a`로 브랜치 목록을 조회한 결과 | 인포그랩 GitLab
git branch -a로 브랜치 목록을 조회한 결과

git worktree list를 실행하면, ‘무슨 디렉터리가 어떤 브랜치 상태로 현재 의존 중인지’ 확인할 수 있죠.

`git worktree list`로 디렉터리 상태를 조회한 화면 | 인포그랩 GitLab
git worktree list로 디렉터리 상태를 조회한 화면

이렇게 같은 원격 리포지터리를 두고, 여러 브랜치를 동시에 참조하는 구조를 만들 수 있고요. 그 결과, 브랜치 전환 없이도 동시 작업을 원활하게 수행할 수 있습니다.

작업이 끝나면, git worktree remove로 사용하지 않는 worktree를 제거하면 됩니다. git worktree lock을 사용하면 해당 worktree가 Git GC 정리 대상에서 제외되며, git worktree prune*로 삭제되지 않도록 보호할 수 있습니다.

*git worktree prune: 사용하지 않는 worktree 제거

git bisect: 특정 커밋 자동 찾기

git bisect이진 탐색(Binary Search)으로 특정 커밋을 자동으로 찾습니다. 특히 버그가 발생한 커밋을 신속하게 찾는 데 도움이 되죠.

일반적으로 git bisectbad, good을 수동으로 표시해 탐색마다 범위를 지정하는 방식으로 사용하는데요. 저는 아래 테스트 스크립트를 활용해 특정 커밋을 자동으로 더 빠르게 찾는 방법을 소개하겠습니다.

아래는 나쁜 커밋을 자동으로 판단하는 테스트 스크립트입니다. 현재 테스트 스크립트 형태는 특정 커밋 번호를 지정하는 방식인데요. 특정 테스트 케이스 통과/실패로 변경됐을 때 오류가 있는 최초 커밋을 빨리 발견하는 데 유용합니다.

#!/bin/bash

# 찾고자 하는 나쁜 커밋의 SHA-1 해시
BAD_COMMIT="ca50f794fcf6eb7444585a593b000d26ea1046e2" # 이 부분을 실제 찾고자 하는 커밋 해시로 변경하세요

# 현재 커밋의 SHA-1 해시 가져오기
CURRENT_COMMIT=$(git rev-parse HEAD)

# 현재 커밋이 찾고자 하는 나쁜 커밋인지 확인
if [ "$CURRENT_COMMIT" = "$BAD_COMMIT" ]; then
echo "나쁜 커밋을 찾았습니다: $BAD_COMMIT"
exit 1 # 나쁜 커밋이면 1 반환 (git bisect bad)
else
echo "이 커밋은 나쁜 커밋이 아닙니다: $CURRENT_COMMIT"
exit 0 # 나쁜 커밋이 아니면 0 반환 (git bisect good)
fi

테스트를 위해 아래 명령어로 이진 탐색 범위를 지정해 나쁜 커밋을 자동으로 찾습니다.

git bisect start
git bisect bad ca50f794fcf6eb7444585a593b000d26ea1046e2 # 나쁜 커밋 example 를 잘못된 커밋으로 지정
git bisect good $(git rev-list --max-parents=0 HEAD) # 최초 커밋을 올바른 커밋으로 지정
git bisect run ./test-script.sh # 상대경로이면 상대경로를 명확히 넣어주어야 합니다

git bisect reset # 찾은 후 HEAD 포인터 변경

git bisect가 아래와 같이 나쁜 커밋을 1개 찾아냈습니다.

`git bisect`로 나쁜 커밋을 찾은 결과 | 인포그랩 GitLab
git bisect로 나쁜 커밋을 찾은 결과

git format-patch & git am: 패치 파일 생성, 적용

git format-patch를 사용하면, 패치 파일이 생성돼 이메일 기반 코드 리뷰에 활용할 수 있습니다.

먼저 git format-patch HEAD~3 -o ./patches를 실행하면, 각 커밋 내용이 패치 파일로 ./patches 디렉터리 아래에 생성됩니다.

`git format-patch`로 패치 파일을 생성한 모습 | 인포그랩 GitLab
git format-patch로 패치 파일을 생성한 모습

사용자는 해당 패치 커밋을 압축하거나 하나의 파일로 만들어 코드 리뷰어에게 이메일로 전송할 수 있습니다.

코드 리뷰어는 이메일에서 해당 패치 파일을 다운로드 받고요. 그다음, 자기 브랜치에서 이 파일을 git am *.patch나 단일 파일 패치 git am 0001-Update-file-aip_encrypt.go.patch적용하고, 코드 리뷰를 진행하면 됩니다.

git bundle: 리포지터리 백업

git am이 커밋 단위로 패치를 적용한다면, git bundle리포지터리 전체를 하나의 파일로 저장합니다. 이로써 네트워크가 없는 환경에서도 저장소를 공유하거나, 백업과 복원 작업을 수행할 수 있죠.

git bundle create ../master.bundle HEAD를 실행하면, 현재 HEAD 포인터가 가리키는 코드 히스토리만 번들 파일로 만들 수 있습니다.

`git bundle`로 번들 파일을 생성한 모습 | 인포그랩 GitLab
git bundle로 번들 파일을 생성한 모습

이때 만든 파일은 리포지터리이므로 git clone, git fetch를 사용해 읽을 수 있습니다. git clone을 실행하면, 아래와 같이 번들 파일에서 리포지터리를 복원할 수 있습니다. git fetch를 사용하면, 기존 저장소에서 특정 브랜치를 가져올 수 있죠.

`git clone`으로 번들링된 파일에서 리포지터리를 clone 한 모습 | 인포그랩 GitLab
git clone으로 번들링된 파일에서 리포지터리를 clone 한 모습

이렇게 번들 파일에서 생성한 리포지터리는 로컬 리포지터리 형태로 접근할 수 있습니다.

git archive: 커밋, 브랜치, 태그 아카이브

git archive는 Git 저장소에서 특정 커밋, 브랜치 또는 태그 파일을 압축해 아카이브로 생성합니다. 이로써 Git 저장소를 압축해 배포하거나, 특정 버전의 소스 코드를 보관할 수 있죠.

git archive --format=zip --output=HEAD.zip HEAD을 실행하면, 특정 커밋의 최종 상태를 압축 파일(ZIP)로 만들 수 있습니다.

git archive.git 디렉터리를 포함하지 않습니다. 이는 일반 압축 파일로 압축하므로, 압축을 해제하면 일반 파일이 됩니다.

git archive는 ‘Git 데이터를 파일로 변환한다’는 점에서 git bundle과 비슷합니다. 그러나 활용 방식은 아래와 같이 대비됩니다.

기준git archivegit bundle
포함 내용현재 파일 상태전체 저장소
(히스토리, 브랜치, 태그)
메타 데이터❌ .git 디렉터리 미포함✅ .git 디렉터리 포함
Git 명령어 지원❌ (압축 해제 후 일반 파일)✅ clonefetchpull 가능
파일 크기작음 (현재 파일만 포함)큼 (모든 히스토리 포함)
업데이트 가능성❌ 새 아카이브 생성 필요✅ 증분 번들 생성 가능
사용 예시git archive --format=zip HEADgit bundle create repo.bundle --all

git commit --amend: 마지막 커밋 수정, 파일 추가

git commit을 실행할 때 --amend 플래그를 사용하면, 마지막 커밋을 수정하거나 파일을 추가할 수 있습니다.

개발할 때 컨텍스트가 여럿이지만, 커밋 이력은 하나로 만들고 싶을 때가 있는데요. 이때 추가 수정한 파일을 --amend로 마지막 커밋에 병합하면 됩니다.

아래 예제는 서버 코드에 추가 로그 코드를 커밋한 상태입니다.

두 가지 파라미터에 로그 찍기 커밋한 모습 | 인포그랩 GitLab
두 가지 파라미터에 로그 찍기 커밋한 모습

다음과 같이 --amend 커밋을 사용하면, 몇 가지 파라미터를 코드에 추가할 수 있습니다.

amend 커밋으로 커밋 내용을 추가한 모습 | 인포그랩 GitLab
amend 커밋으로 커밋 내용을 추가한 모습

git clean: 추적 X 파일 정리

git cleanGit 저장소에서 추적되지 않는 파일을 삭제합니다. 이로써 Git이 관리하지 않는 파일을 제거해 저장소를 깔끔하게 유지할 수 있죠.

Git은 다음 3가지 상태로 구분됩니다.

  • Committed(커밋됨): 변경 사항이 로컬 리포지터리에 안전하게 저장된 상태
  • Staged(스테이지됨): 수정된 파일을 다음 커밋에 포함하기 위해 표시한 상태
  • Modified(수정됨): 커밋된 파일이 변경됐지만 아직 스테이징 영역에 추가되지 않은 상태

git clean은 위 3가지 상태에 포함되지 않은, 추적되지 않는(Untracked) 파일을 삭제합니다.

아래 예제와 옵션을 활용해 불필요한 파일을 다양한 방식으로 삭제할 수 있습니다.

git clean -fdx  # 추적되지 않은 파일/디렉터리 강제 삭제
옵션기능
-f강제 삭제
-d디렉터리 삭제 포함
-x.gitignore에 포함된 파일 삭제

맺음말

지금까지 실무에서 자주 사용하는 필수 Git 명령어 10가지를 알아봤습니다. git worktree로 작업의 컨텍스트를 분리하고, 병렬 브랜치 작업을 수행할 수 있고요. git bisect로 ‘버그 커밋이 어디까지 전파됐는지’ 파악할 수 있죠. 이 밖에도 협업과 생산성을 향상하는 유용한 Git 명령어를 많이 살펴봤습니다.

이 명령어는 Git의 동작 원리를 심도있게 파악하고 기초를 다지는 데 밑바탕이 될 것입니다. 또 협업할 때 충돌을 줄여 협업 효과를 높이는 데 도움이 될 수 있죠. 여러분도 실무에 필수 Git 명령어를 십분 활용하며, 개발 과정과 결과의 질을 업그레이드하시길 응원합니다.

참고 자료

  1. “Getting Started - A Short History of Git”, Git SCM, https://git-scm.com/book/ms/v2/Getting-Started-A-Short-History-of-Git
  2. Kenneth DuMez, “Understanding Git: The history and internals”, Graphite, 2023-11-09, https://graphite.dev/blog/understanding-git
  3. “Mastering Git”, Curate Partners, https://curatepartners.com/blogs/skills-tools-platforms/mastering-git-distributed-version-control-for-efficient-software-development/
  4. “Git 브랜치 - Rebase 하기”, Git SCM, https://git-scm.com/book/ko/v2/Git-브랜치-Rebase-하기
  5. “Merging vs. rebasing”, Atlassian, https://www.atlassian.com/git/tutorials/merging-vs-rebasing
  6. Nicola Paolucci, “Git team workflows: merge or rebase?”, Atlassian, 2013-10-28, https://www.atlassian.com/blog/git/git-team-workflows-merge-or-rebase
  7. Art Sphitz, “Git GC | Git Garbage Collection For Orphaned, Dangling Objects”, Initial Commit, 2022-10-23, https://initialcommit.com/blog/git-gc
  8. “git-worktree - Manage multiple working trees”, Git SCM, https://git-scm.com/docs/git-worktree/2.17.0
  9. “Automating Testing with Git Bisect”, Diginode, https://diginode.in/git/automating-testing-with-git-bisect/
  10. Jacob Stopak, “Git Format-Patch | Create A Patch in Git”, Initial Commit, 2022-10-25, https://initialcommit.com/blog/git-format-patch
  11. Jacob Stopak, “Git Am | Apply An Email Patch in Git”, Initial Commit, 2022-11-08, https://initialcommit.com/blog/git-am
  12. Art Sphitz, “Git Bundle | A Guide For Bundling Git Repos”, Initial Commit, 2022-11-02, https://initialcommit.com/blog/git-bundle
  13. “Git Archive: How to export a git project”, Atlassian, https://www.atlassian.com/git/tutorials/export-git-archive

SVN만 사용하는 우리 회사, Git으로 전환하고 싶으신가요? DevOps 전문가, 인포그랩과 상담하세요!