Skip to content

Split View: Git 완전 정복: 주니어부터 시니어까지, 실무에서 바로 쓰는 Git 고급 워크플로우

✨ Learn with Quiz
|

Git 완전 정복: 주니어부터 시니어까지, 실무에서 바로 쓰는 Git 고급 워크플로우

들어가며

Git은 모든 개발자의 일상 도구입니다. 하지만 대부분의 개발자가 git add, git commit, git push 정도만 사용하고, Git이 제공하는 강력한 기능의 10%도 활용하지 못합니다. 이 가이드는 주니어 개발자부터 시니어 개발자까지, Git의 내부 구조부터 실무에서 즉시 적용 가능한 고급 워크플로우를 총정리합니다.

코드 리뷰에서 "이 커밋 히스토리 좀 정리해주세요"라는 피드백을 받은 적이 있나요? 머지 충돌이 나면 당황한 적은요? 이 글을 끝까지 읽으면 그런 상황에서 자신 있게 대처할 수 있습니다.


1. Git 내부 구조 이해 — Objects, DAG, 그리고 .git 디렉토리

Git을 진정으로 마스터하려면 내부 동작 원리를 이해해야 합니다. Git은 단순한 버전 관리 도구가 아니라, content-addressable 파일 시스템 위에 구축된 정교한 시스템입니다.

1.1 Git의 4가지 객체 (Objects)

Git 내부에는 4가지 핵심 객체가 존재합니다.

Blob (Binary Large Object): 파일의 내용을 저장합니다. 파일 이름이나 경로는 포함하지 않고, 순수한 파일 내용만 담습니다.

# 파일 내용의 해시값 확인
echo "Hello, Git!" | git hash-object --stdin
# 결과: 예시 af5626b4a114abcb82d63db7c8082c3c4756e51b

# 실제 저장된 객체 타입 확인
git cat-file -t af5626b
# 결과: blob

# 내용 확인
git cat-file -p af5626b
# 결과: Hello, Git!

Tree: 디렉토리 구조를 나타냅니다. Blob과 다른 Tree를 참조하여 파일 시스템의 스냅샷을 구성합니다.

# 최신 커밋의 트리 확인
git cat-file -p HEAD^{tree}
# 100644 blob a906cb2a4a904a15...    README.md
# 040000 tree 99f1a6d12cb4b6f1...    src
# 100644 blob 47c6340d6459e058...    package.json

Commit: 특정 시점의 스냅샷입니다. Tree 객체를 가리키며, 작성자, 커밋 메시지, 부모 커밋 정보를 포함합니다.

# 커밋 내부 구조 확인
git cat-file -p HEAD
# tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0
# parent 8bd3ad5e1234567890abcdef1234567890abcdef
# author John Doe <john@example.com> 1711100000 +0900
# committer John Doe <john@example.com> 1711100000 +0900
#
# feat: add user authentication

Tag: 특정 커밋에 이름을 붙이는 객체입니다. 릴리스 버전 관리에 사용됩니다.

# 어노테이티드 태그 생성
git tag -a v1.0.0 -m "Release version 1.0.0"

# 태그 객체 확인
git cat-file -p v1.0.0

1.2 SHA-1 해싱과 콘텐츠 주소 지정

Git은 모든 객체를 SHA-1 해시로 식별합니다. 동일한 내용은 항상 동일한 해시를 생성하므로, 중복 저장이 자연스럽게 방지됩니다.

# 동일한 내용 = 동일한 해시
echo "same content" | git hash-object --stdin
echo "same content" | git hash-object --stdin
# 두 결과가 완전히 동일합니다

이것이 의미하는 바는 중요합니다. 100개의 브랜치에서 같은 파일을 가지고 있더라도, Git은 그 파일을 단 한 번만 저장합니다.

1.3 DAG (Directed Acyclic Graph) — 커밋 그래프

Git의 커밋 히스토리는 DAG(방향 비순환 그래프) 구조입니다. 각 커밋은 부모 커밋을 가리키며, 순환이 없습니다.

    A---B---C---D  (main)
         \
          E---F---G  (feature)
               \
                H---I  (hotfix)

이 구조를 이해하면 merge, rebase, cherry-pick이 실제로 무엇을 하는지 직관적으로 알 수 있습니다.

# 그래프 형태로 로그 보기
git log --oneline --graph --all --decorate

# 더 깔끔한 출력
git log --oneline --graph --all --format="%h %s (%ar)"

1.4 .git 디렉토리 해부

.git/
  HEAD          # 현재 브랜치를 가리키는 포인터
  config        # 저장소별 설정
  refs/
    heads/      # 로컬 브랜치들 (각 파일 = 커밋 해시)
    tags/       # 태그들
    remotes/    # 원격 브랜치 추적
  objects/      # 모든 Git 객체 (blob, tree, commit, tag)
    pack/       # 압축된 객체 팩 파일
  hooks/        # Git 훅 스크립트
  index         # 스테이징 영역 (바이너리)
# HEAD가 가리키는 곳 확인
cat .git/HEAD
# ref: refs/heads/main

# main 브랜치가 가리키는 커밋
cat .git/refs/heads/main
# a1b2c3d4e5f6...

# 객체 저장소 크기 확인
git count-objects -vH

2. 브랜칭 전략 비교 — 팀에 맞는 워크플로우 선택하기

2.1 Git Flow

2010년 Vincent Driessen이 제안한 전통적인 브랜칭 모델입니다.

main ─────────────────────────────── (프로덕션)
  └── develop ────────────────────── (개발 통합)
        │        │
        │   feature/login ────────── (기능 개발)
        └── release/1.0 ──────────── (릴리스 준비)
              └── hotfix/bug-123 ──── (긴급 수정)

장점: 명확한 릴리스 관리, 프로덕션 안정성 보장 단점: 브랜치가 많아 복잡, 머지 충돌 빈번, CI/CD와 어울리지 않음

# Git Flow 초기화 (git-flow 확장)
git flow init

# 기능 개발 시작
git flow feature start user-auth

# 기능 개발 완료
git flow feature finish user-auth

2.2 GitHub Flow

GitHub에서 사용하는 단순한 브랜칭 모델입니다.

main ────────────────────────────────
  │              │
  └── feature ───┘ (PR + 머지)

규칙: main은 항상 배포 가능 상태. 기능 브랜치에서 작업 후 PR을 통해 머지.

# 1. 브랜치 생성
git checkout -b feature/add-search

# 2. 작업 및 커밋
git add .
git commit -m "feat: add search functionality"

# 3. 푸시
git push -u origin feature/add-search

# 4. PR 생성 (GitHub CLI)
gh pr create --title "Add search functionality" --body "..."

# 5. 리뷰 후 머지
gh pr merge --squash

2.3 Trunk-Based Development (트렁크 기반 개발)

Google, Meta 등 대규모 팀에서 사용하는 방식입니다. 모든 개발자가 main(trunk)에 직접 또는 매우 짧은 수명의 브랜치를 통해 자주 통합합니다.

main ──A──B──C──D──E──F──G──H──I──
            \─/     \─/
         (1-2일)  (1-2일)

핵심 원칙:

  • 브랜치 수명은 최대 1~2일
  • 하루에 여러 번 main에 통합
  • Feature Flag로 미완성 기능 관리
  • 강력한 CI/CD 파이프라인 필수

2.4 전략 비교표

항목Git FlowGitHub FlowTrunk-Based
복잡도높음낮음중간
브랜치 수명길다 (주~월)중간 (일~주)짧다 (시간~일)
릴리스 주기정기 릴리스수시 배포지속 배포
팀 규모10명 이상2~15명제한 없음
CI/CD 의존도낮음중간높음
추천 상황모바일 앱, 패키지SaaS, 웹앱대규모 조직

2.5 브랜치 명명 규칙

# 기능 개발
feature/JIRA-123-add-login
feature/user-profile-page

# 버그 수정
bugfix/JIRA-456-fix-null-pointer
fix/login-redirect-loop

# 핫픽스
hotfix/security-patch-xss
hotfix/v1.2.1

# 릴리스
release/1.0.0
release/2026-q1

# 실험/개인
experiment/new-search-algo
chore/update-dependencies

3. 리베이스 마스터 클래스

3.1 Rebase vs Merge — 언제 무엇을 쓸까

Merge: 두 브랜치의 히스토리를 보존하면서 합칩니다.

git checkout main
git merge feature/login
# 머지 커밋이 생성됨
    A---B---C---D---M  (main, 머지 커밋 M)
         \         /
          E---F---G  (feature)

Rebase: 커밋을 다른 베이스 위에 재적용합니다.

git checkout feature/login
git rebase main
# feature의 커밋이 main 위에 재적용됨
    A---B---C---D  (main)
                 \
                  E'---F'---G'  (feature, 재적용됨)

3.2 Interactive Rebase (대화형 리베이스) 완전 가이드

# 최근 5개 커밋을 대화형으로 편집
git rebase -i HEAD~5

에디터가 열리면 다음과 같은 화면이 나타납니다.

pick a1b2c3d feat: add user model
pick d4e5f6g fix: typo in user model
pick h7i8j9k feat: add user controller
pick l0m1n2o WIP: debugging
pick p3q4r5s feat: add user routes

사용 가능한 명령어:

명령약자설명
pickp커밋을 그대로 사용
rewordr커밋 메시지만 변경
edite커밋을 수정 (파일 변경 가능)
squashs이전 커밋과 합치고 메시지 편집
fixupf이전 커밋과 합치고 메시지 버림
dropd커밋 삭제

실전 예시 — 지저분한 히스토리 정리:

pick a1b2c3d feat: add user model
fixup d4e5f6g fix: typo in user model
pick h7i8j9k feat: add user controller
drop l0m1n2o WIP: debugging
pick p3q4r5s feat: add user routes

결과: 5개 커밋이 깔끔한 3개 커밋으로 정리됩니다.

3.3 Autosquash 워크플로우

fixup! 접두사를 사용하면 자동으로 정리됩니다.

# 원래 커밋
git commit -m "feat: add user model"

# 나중에 수정이 필요할 때
git commit --fixup HEAD
# 또는 특정 커밋 지정
git commit --fixup a1b2c3d

# 자동으로 정리
git rebase -i --autosquash main

3.4 리베이스의 황금 규칙

절대 공유된 브랜치(main, develop)를 리베이스하지 마세요. 리베이스는 커밋 해시를 변경하므로, 다른 사람들의 로컬 저장소와 충돌이 발생합니다.

# 안전한 리베이스 (개인 feature 브랜치)
git checkout feature/my-task
git rebase main

# 위험한 리베이스 (절대 하지 마세요)
git checkout main
git rebase feature/something  # 금지!

4. 실무 시나리오 20개 — 문제 발생 시 해결법

시나리오 1: 커밋 메시지를 잘못 작성했을 때

# 방금 한 커밋의 메시지 수정 (아직 푸시 안 함)
git commit --amend -m "feat: add user authentication"

# 에디터에서 메시지 수정
git commit --amend

시나리오 2: 잘못된 브랜치에 커밋했을 때

# 상황: main에 커밋해야 할 것을 feature 브랜치에 했다
# 방법 1: cherry-pick
git checkout main
git cherry-pick abc1234
git checkout feature/wrong-branch
git reset --hard HEAD~1

# 방법 2: stash 활용
git reset --soft HEAD~1
git stash
git checkout correct-branch
git stash pop
git commit -m "correct commit message"

시나리오 3: 푸시 후 되돌리기 — revert vs reset

# 안전한 방법: revert (새 커밋으로 되돌림, 히스토리 보존)
git revert abc1234
git push

# 위험한 방법: reset + force push (히스토리 변경)
git reset --hard HEAD~1
git push --force-with-lease
# force-with-lease는 다른 사람의 커밋을 덮어쓰지 않도록 보호

revert vs reset 비교:

항목revertreset
히스토리보존 (새 커밋 추가)변경 (커밋 삭제)
안전성안전위험
공유 브랜치사용 가능사용 금지
되돌리기revert의 revert 가능reflog으로만 복구

시나리오 4: 머지 충돌 해결 전략

# 충돌 발생 시
git merge feature/login
# CONFLICT (content): Merge conflict in src/auth.js

# 충돌 파일 확인
git status

# 충돌 마커 구조
# <<<<<<< HEAD
# 현재 브랜치의 코드
# =======
# 머지하려는 브랜치의 코드
# >>>>>>> feature/login

# 수동 해결 후
git add src/auth.js
git commit -m "resolve merge conflict in auth module"

머지 충돌 최소화 전략:

# 1. main을 자주 가져와서 동기화
git fetch origin
git rebase origin/main

# 2. 작은 단위로 자주 커밋하고 머지
# 3. 팀 내 파일 소유권 명확히 하기 (CODEOWNERS)
# 4. 머지 도구 설정
git config --global merge.tool vimdiff
git mergetool

시나리오 5: 특정 커밋으로 버그 추적 — git bisect

# bisect 시작
git bisect start

# 현재(버그 있음)를 bad로 표시
git bisect bad

# 버그 없던 커밋을 good으로 표시
git bisect good v1.0.0

# Git이 중간 커밋을 체크아웃 — 테스트 후 판단
git bisect good  # 또는 git bisect bad

# 반복하면 버그를 유발한 정확한 커밋을 찾음
# 결과: abc1234 is the first bad commit

# bisect 종료
git bisect reset

자동 bisect (테스트 스크립트 활용):

# 테스트 스크립트가 실패하면 bad, 성공하면 good
git bisect start HEAD v1.0.0
git bisect run npm test
# 자동으로 버그 커밋을 찾아줍니다!

시나리오 6: 대용량 파일을 실수로 커밋했을 때

# 방법 1: git-filter-repo (추천)
pip install git-filter-repo
git filter-repo --path large-file.zip --invert-paths

# 방법 2: BFG Repo-Cleaner
java -jar bfg.jar --delete-files large-file.zip
git reflog expire --expire=now --all
git gc --prune=now --aggressive

# 예방: .gitignore 설정
echo "*.zip" >> .gitignore
echo "*.tar.gz" >> .gitignore
echo "node_modules/" >> .gitignore

시나리오 7: 파일의 일부만 스테이징 — git add -p

# 파일의 변경사항을 hunk 단위로 선택적 스테이징
git add -p src/utils.js

# 각 hunk에 대해:
# y - 이 hunk 스테이징
# n - 이 hunk 건너뛰기
# s - 더 작은 hunk로 분할
# e - 수동으로 편집
# q - 종료

시나리오 8: 작업 중 긴급 핫픽스 — stash + worktree

# 방법 1: stash
git stash push -m "WIP: feature work"
git checkout main
git checkout -b hotfix/critical-bug
# 핫픽스 작업...
git checkout feature/my-work
git stash pop

# 방법 2: worktree (더 나은 방법!)
git worktree add ../hotfix-workspace main
cd ../hotfix-workspace
git checkout -b hotfix/critical-bug
# 핫픽스 작업...
cd ../main-workspace
git worktree remove ../hotfix-workspace

시나리오 9: 여러 커밋을 하나로 합치기

# 방법 1: interactive rebase
git rebase -i HEAD~4
# 첫 번째를 pick, 나머지를 squash로 변경

# 방법 2: soft reset
git reset --soft HEAD~4
git commit -m "feat: complete user authentication"

# 방법 3: merge --squash
git checkout main
git merge --squash feature/user-auth
git commit -m "feat: add user authentication system"

시나리오 10: 서브모듈 관리

# 서브모듈 추가
git submodule add https://github.com/org/shared-lib.git libs/shared

# 클론 시 서브모듈 포함
git clone --recurse-submodules https://github.com/org/project.git

# 서브모듈 업데이트
git submodule update --remote --merge

# 모든 서브모듈에 명령 실행
git submodule foreach 'git checkout main && git pull'

시나리오 11: 삭제된 브랜치 복구

# reflog에서 삭제된 브랜치의 마지막 커밋 찾기
git reflog
# abc1234 HEAD@{5}: checkout: moving from deleted-branch to main

# 브랜치 복구
git checkout -b recovered-branch abc1234

시나리오 12: 특정 파일만 다른 브랜치에서 가져오기

# main 브랜치의 특정 파일을 현재 브랜치로 가져오기
git checkout main -- src/config.js

# 특정 커밋의 파일 가져오기
git checkout abc1234 -- src/config.js

시나리오 13: 커밋 날짜 변경

# 마지막 커밋의 날짜를 현재 시간으로 변경
GIT_COMMITTER_DATE="$(date)" git commit --amend --date="$(date)"

# 특정 날짜로 변경 (리베이스 중)
GIT_COMMITTER_DATE="2026-03-22T10:00:00" git commit --amend --date="2026-03-22T10:00:00"

시나리오 14: Git blame으로 코드 추적

# 파일의 각 줄을 누가 마지막으로 수정했는지 확인
git blame src/auth.js

# 특정 줄 범위만 확인
git blame -L 10,20 src/auth.js

# 공백 변경 무시
git blame -w src/auth.js

# 코드 이동도 추적
git blame -M src/auth.js
git blame -C src/auth.js

시나리오 15: git log 고급 활용

# 특정 파일의 변경 이력
git log --follow -p -- src/auth.js

# 특정 문자열이 추가/삭제된 커밋 찾기 (pickaxe)
git log -S "API_KEY" --oneline

# 특정 저자의 커밋만
git log --author="John" --oneline --since="2026-01-01"

# 통계 정보 포함
git log --stat --oneline -10

# 커밋 간 diff
git log -p --oneline -3

시나리오 16: 원격 저장소 관리

# 여러 원격 저장소 추가
git remote add upstream https://github.com/original/repo.git
git remote add deploy git@deploy-server:app.git

# upstream 동기화 (포크 프로젝트)
git fetch upstream
git rebase upstream/main

# 원격 브랜치 정리
git remote prune origin
git fetch --prune

시나리오 17: Git 태그로 릴리스 관리

# 어노테이티드 태그 생성
git tag -a v2.0.0 -m "Release 2.0.0: new payment system"

# 특정 커밋에 태그
git tag -a v1.5.0 abc1234 -m "Hotfix release"

# 태그 푸시
git push origin v2.0.0
git push origin --tags

# 태그 삭제
git tag -d v2.0.0-beta
git push origin --delete v2.0.0-beta

시나리오 18: Git hooks로 자동화

# .husky/pre-commit
#!/bin/sh
npx lint-staged

# .husky/commit-msg
#!/bin/sh
npx commitlint --edit "$1"

# .husky/pre-push
#!/bin/sh
npm test

시나리오 19: 패치 파일 생성 및 적용

# 패치 생성
git diff > my-changes.patch
git format-patch -3  # 최근 3개 커밋을 패치 파일로

# 패치 적용
git apply my-changes.patch
git am *.patch  # format-patch로 생성된 패치 적용

시나리오 20: git reflog — 최후의 보루

# reflog 확인 (HEAD의 이동 기록)
git reflog

# 실수로 reset --hard 한 경우 복구
git reflog
# abc1234 HEAD@{2}: commit: important work
git reset --hard abc1234

# 실수로 삭제한 stash 복구
git fsck --unreachable | grep commit
git stash apply abc1234

5. Conventional Commits + Semantic Versioning

5.1 Conventional Commits 형식

type(scope): description

[optional body]

[optional footer(s)]

타입 목록:

타입설명예시
feat새로운 기능feat: add login page
fix버그 수정fix: resolve null pointer in auth
docs문서 변경docs: update API documentation
style코드 스타일 (포맷팅 등)style: fix indentation
refactor리팩토링refactor: extract auth middleware
perf성능 개선perf: optimize database queries
test테스트 추가/수정test: add unit tests for auth
build빌드 시스템 변경build: update webpack config
ciCI 설정 변경ci: add GitHub Actions workflow
chore기타 잡무chore: update dependencies

Breaking Change 표기:

feat(api)!: change authentication endpoint

BREAKING CHANGE: /auth/login is now /api/v2/auth/login

5.2 Commitlint + Husky 설정

# 패키지 설치
npm install -D @commitlint/cli @commitlint/config-conventional husky

# commitlint 설정
echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js

# Husky 초기화
npx husky init
echo 'npx commitlint --edit "$1"' > .husky/commit-msg

5.3 자동 Changelog 생성

# conventional-changelog 설치
npm install -D conventional-changelog-cli

# CHANGELOG.md 생성
npx conventional-changelog -p angular -i CHANGELOG.md -s

# package.json에 스크립트 추가
{
  "scripts": {
    "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
    "release": "standard-version"
  }
}

5.4 Semantic Release — 완전 자동화

npm install -D semantic-release
{
  "release": {
    "branches": ["main"],
    "plugins": [
      "@semantic-release/commit-analyzer",
      "@semantic-release/release-notes-generator",
      "@semantic-release/changelog",
      "@semantic-release/npm",
      "@semantic-release/github",
      "@semantic-release/git"
    ]
  }
}

커밋 타입에 따라 자동으로 버전이 결정됩니다:

  • fix: -> PATCH (1.0.0 -> 1.0.1)
  • feat: -> MINOR (1.0.0 -> 1.1.0)
  • BREAKING CHANGE -> MAJOR (1.0.0 -> 2.0.0)

6. GitHub Actions CI/CD

6.1 기본 워크플로우

name: CI Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20, 22]

    steps:
      - uses: actions/checkout@v4

      - name: Use Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'

      - run: npm ci
      - run: npm run lint
      - run: npm test
      - run: npm run build

6.2 PR 자동 검사 워크플로우

name: PR Checks

on:
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  lint-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - run: npm ci

      - name: Lint
        run: npm run lint

      - name: Type Check
        run: npx tsc --noEmit

      - name: Unit Tests
        run: npm test -- --coverage

      - name: Build
        run: npm run build

      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          file: ./coverage/lcov.info

6.3 자동 라벨링

name: Auto Label

on:
  pull_request:
    types: [opened, edited]

jobs:
  label:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/labeler@v5
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}
# .github/labeler.yml
frontend:
  - changed-files:
      - any-glob-to-any-file: 'src/components/**'

backend:
  - changed-files:
      - any-glob-to-any-file: 'src/api/**'

documentation:
  - changed-files:
      - any-glob-to-any-file: 'docs/**'

tests:
  - changed-files:
      - any-glob-to-any-file: '**/*.test.*'

6.4 Dependabot 설정

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: 'npm'
    directory: '/'
    schedule:
      interval: 'weekly'
      day: 'monday'
    open-pull-requests-limit: 10
    labels:
      - 'dependencies'
    groups:
      dev-dependencies:
        dependency-type: 'development'
      production-dependencies:
        dependency-type: 'production'

  - package-ecosystem: 'github-actions'
    directory: '/'
    schedule:
      interval: 'weekly'

6.5 배포 워크플로우

name: Deploy

on:
  push:
    tags:
      - 'v*'

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - run: npm ci
      - run: npm run build

      - name: Deploy to Production
        run: |
          echo "Deploying version $GITHUB_REF_NAME"
          # 실제 배포 명령어

7. Monorepo 전략

7.1 Monorepo vs Multirepo

항목MonorepoMultirepo
코드 공유쉬움패키지 발행 필요
의존성 관리통합 관리개별 관리
CI/CD복잡 (선택적 빌드)단순 (저장소별)
코드 리뷰전체 맥락 파악 가능저장소별로 분산
권한 관리복잡저장소별 설정
대표 사례Google, Meta, MicrosoftNetflix, Spotify

7.2 도구 비교 — Nx vs Turborepo vs Lerna

# Nx 초기화
npx create-nx-workspace@latest my-workspace

# Turborepo 초기화
npx create-turbo@latest

# 프로젝트 구조 예시
my-monorepo/
  apps/
    web/          # Next.js 웹앱
    mobile/       # React Native 앱
    api/          # Express API 서버
  packages/
    ui/           # 공유 UI 컴포넌트
    utils/        # 공유 유틸리티
    config/       # 공유 설정
  turbo.json
  package.json

7.3 CODEOWNERS 설정

# .github/CODEOWNERS

# 전체 프로젝트 기본 리뷰어
* @team-lead

# 프론트엔드
/apps/web/        @frontend-team
/packages/ui/     @frontend-team

# 백엔드
/apps/api/        @backend-team
/packages/utils/  @backend-team

# DevOps
/.github/         @devops-team
/docker/          @devops-team
*.yml             @devops-team

7.4 선택적 빌드와 테스트

{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    },
    "test": {
      "dependsOn": ["build"]
    },
    "lint": {}
  }
}
# 변경된 패키지만 빌드
npx turbo run build --filter=...[HEAD^1]

# 특정 앱만 빌드
npx turbo run build --filter=web

# 의존성 포함 빌드
npx turbo run build --filter=web...

8. Git 성능 최적화

8.1 Shallow Clone (얕은 클론)

# 최근 1개 커밋만 클론 (CI/CD에서 유용)
git clone --depth 1 https://github.com/org/repo.git

# 나중에 전체 히스토리가 필요하면
git fetch --unshallow

8.2 Partial Clone (부분 클론)

# Blob 없이 클론 (필요할 때 다운로드)
git clone --filter=blob:none https://github.com/org/repo.git

# Tree도 제외 (가장 빠른 클론)
git clone --filter=tree:0 https://github.com/org/repo.git

8.3 Sparse Checkout (희소 체크아웃)

# sparse checkout 활성화
git sparse-checkout init --cone

# 특정 디렉토리만 체크아웃
git sparse-checkout set apps/web packages/ui

# 디렉토리 추가
git sparse-checkout add apps/api

# 설정 확인
git sparse-checkout list

8.4 Git LFS (Large File Storage)

# Git LFS 설치 및 초기화
git lfs install

# 대용량 파일 추적
git lfs track "*.psd"
git lfs track "*.zip"
git lfs track "assets/videos/*"

# .gitattributes 확인
cat .gitattributes
# *.psd filter=lfs diff=lfs merge=lfs -text

# LFS 상태 확인
git lfs ls-files
git lfs status

8.5 Git Maintenance (자동 유지보수)

# 자동 유지보수 활성화
git maintenance start

# 수동으로 가비지 컬렉션
git gc --aggressive

# 객체 압축 상태 확인
git count-objects -vH

# 불필요한 참조 정리
git remote prune origin
git reflog expire --expire=30.days

8.6 Scalar — 대규모 저장소 관리 (Microsoft)

# Scalar 설치 (Git 2.38+에 포함)
scalar register

# 또는 새로 클론
scalar clone https://github.com/org/massive-repo.git

# Scalar가 자동으로 설정하는 것들:
# - Partial clone
# - Sparse checkout
# - Background maintenance
# - Filesystem monitor
# - Commit graph

9. AI + Git (2025~2026)

9.1 AI 커밋 메시지 생성

# Claude Code에서 자동 커밋 메시지 생성
claude commit

# GitHub Copilot CLI
gh copilot suggest "write a commit message for these changes"

# aicommits 도구
npm install -g aicommits
aicommits

9.2 AI 코드 리뷰

GitHub Copilot for Pull Requests는 PR을 자동으로 분석하여 리뷰 코멘트를 생성합니다. 코드의 잠재적 버그, 성능 이슈, 보안 취약점을 찾아줍니다.

# GitHub Copilot 자동 리뷰 활성화 (저장소 설정에서)
# Settings > Code review > Copilot code review > Enable

9.3 Claude Code와 Git 워크플로우

Claude Code는 Git 작업을 직접 도와줍니다.

# 변경사항 리뷰 요청
claude "review my staged changes"

# 커밋 메시지 작성
claude "write a conventional commit message for my changes"

# PR 설명 생성
claude "write a PR description for the current branch"

# 머지 충돌 해결
claude "help me resolve the merge conflicts in src/auth.js"

9.4 자동 PR 설명 생성

name: Auto PR Description

on:
  pull_request:
    types: [opened]

jobs:
  describe:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Generate PR description
        uses: anthropics/claude-code-action@v1
        with:
          prompt: |
            Analyze the diff of this PR and generate a clear description
            including what changed, why, and any risks.

실력 점검 퀴즈

퀴즈 1: Git 객체

Q: git에서 파일 이름은 어떤 객체에 저장될까요? Blob일까요, Tree일까요?

A: Tree 객체에 저장됩니다.

Blob은 파일의 순수 내용만 저장하며, 파일 이름과 경로 정보는 Tree 객체가 관리합니다. 이 설계 덕분에 파일 이름이 바뀌어도 내용이 같으면 같은 Blob을 재사용할 수 있습니다.

# Tree 객체에서 파일 이름 확인
git cat-file -p HEAD^{tree}
# 100644 blob a906cb...    README.md  <-- 파일 이름은 Tree에!

퀴즈 2: Rebase 안전성

Q: 다음 중 안전한 리베이스 작업은? (A: main에서 feature 리베이스, B: feature에서 main 리베이스, C: 둘 다 안전)

A: B가 안전합니다.

리베이스의 황금 규칙은 "공유된 브랜치를 리베이스하지 마라"입니다. main은 공유 브랜치이므로 main 위에서 리베이스하면 안 됩니다. 반면 feature는 개인 브랜치이므로 main의 최신 커밋 위에 feature의 커밋을 재적용하는 것은 안전합니다.

퀴즈 3: Reset 모드

Q: git reset의 세 가지 모드(--soft, --mixed, --hard)의 차이점은 무엇일까요?

A:

  • --soft: HEAD만 이동. 스테이징 영역과 작업 디렉토리는 그대로. 커밋만 취소하고 싶을 때.
  • --mixed (기본값): HEAD 이동 + 스테이징 영역 초기화. 작업 디렉토리는 그대로. 커밋과 스테이징을 취소할 때.
  • --hard: HEAD 이동 + 스테이징 영역 + 작업 디렉토리 모두 초기화. 모든 변경사항 버릴 때. (위험!)
# 예시: 최근 3개 커밋을 취소하되 코드는 유지
git reset --soft HEAD~3
# 이제 변경사항이 스테이징된 상태로 남아있음

퀴즈 4: Cherry-pick 충돌

Q: cherry-pick 중 충돌이 발생하면 어떻게 해결하나요?

A: merge 충돌과 동일한 방식으로 해결합니다.

git cherry-pick abc1234
# CONFLICT 발생

# 1. 충돌 파일 수정
# 2. 스테이징
git add resolved-file.js

# 3. cherry-pick 계속
git cherry-pick --continue

# 또는 cherry-pick 취소
git cherry-pick --abort

퀴즈 5: Monorepo에서의 선택적 CI

Q: Monorepo에서 프론트엔드 코드만 변경했을 때, 백엔드 테스트도 돌려야 할까요?

A: 의존성에 따라 다릅니다.

프론트엔드가 백엔드 패키지에 의존하지 않는다면, 백엔드 테스트를 돌릴 필요가 없습니다. 이것이 Nx나 Turborepo 같은 도구가 제공하는 "영향 분석(affected analysis)"의 핵심입니다.

# Nx: 변경에 영향받는 프로젝트만 테스트
npx nx affected --target=test

# Turborepo: 변경된 패키지와 의존 패키지만 빌드
npx turbo run build --filter=...[HEAD^1]

하지만 공유 패키지(utils, config 등)가 변경되었다면, 그 패키지에 의존하는 모든 프로젝트의 테스트를 돌려야 합니다.


참고 자료

공식 문서

  1. Git 공식 문서 — 가장 정확한 레퍼런스
  2. GitHub Docs — GitHub 기능 가이드
  3. Pro Git Book (한국어) — 무료 온라인 서적

브랜칭 전략

  1. Trunk Based Development — TBD 공식 사이트
  2. GitHub Flow Guide
  3. A successful Git branching model — Git Flow 원문

CI/CD

  1. GitHub Actions 공식 문서
  2. Semantic Release — 자동 버전 관리
  3. Conventional Commits — 커밋 규약

Monorepo

  1. Nx 공식 문서
  2. Turborepo 공식 문서
  3. Monorepo Tools — 도구 비교

성능

  1. Git LFS — 대용량 파일 관리
  2. Scalar — 대규모 저장소
  3. git-filter-repo — 히스토리 정리

심화

  1. Oh Shit, Git!?! — Git 실수 복구 가이드
  2. Learn Git Branching — 시각적 Git 학습
  3. Git Internals (PDF) — Git 내부 구조

마무리

Git은 단순한 도구가 아니라 개발 문화를 형성하는 핵심 인프라입니다. 깔끔한 커밋 히스토리는 팀의 협업 능력을 반영하고, 효과적인 브랜칭 전략은 배포 안정성을 결정합니다.

이 글에서 다룬 내용을 한 번에 모두 익힐 필요는 없습니다. 먼저 일상적으로 사용하는 명령어의 동작 원리를 이해하고, 점차 고급 기법을 적용해 보세요. 특히 interactive rebase와 bisect는 한번 익히면 생산성이 크게 올라가는 도구입니다.

기억하세요: 좋은 커밋 히스토리는 미래의 자신과 동료에게 보내는 편지입니다. 오늘부터 Conventional Commits를 시작해 보는 것은 어떨까요?

Git Mastery: Advanced Workflows Every Developer Needs — From Junior to Senior

Introduction

Git is every developer's daily companion. Yet most developers only use git add, git commit, git push and barely tap into 10% of what Git offers. This guide covers everything from Git internals to advanced workflows that you can apply immediately in your day-to-day work, whether you are a junior or a senior engineer.

Have you ever received code review feedback saying "please clean up this commit history"? Have you ever panicked when a merge conflict appeared? By the end of this article, you will handle those situations with confidence.


1. Understanding Git Internals — Objects, DAG, and the .git Directory

To truly master Git, you need to understand how it works under the hood. Git is not just a version control tool; it is a sophisticated system built on top of a content-addressable filesystem.

1.1 The Four Git Objects

There are four core object types inside Git.

Blob (Binary Large Object): Stores file contents. It does not include the filename or path — only the raw content.

# Check the hash of file contents
echo "Hello, Git!" | git hash-object --stdin
# Result: e.g. af5626b4a114abcb82d63db7c8082c3c4756e51b

# Verify the object type
git cat-file -t af5626b
# Result: blob

# View the content
git cat-file -p af5626b
# Result: Hello, Git!

Tree: Represents a directory structure. It references blobs and other trees to build a snapshot of the filesystem.

# Inspect the tree of the latest commit
git cat-file -p HEAD^{tree}
# 100644 blob a906cb2a4a904a15...    README.md
# 040000 tree 99f1a6d12cb4b6f1...    src
# 100644 blob 47c6340d6459e058...    package.json

Commit: A snapshot at a point in time. It points to a tree object and includes the author, commit message, and parent commit references.

# Inspect a commit's internals
git cat-file -p HEAD
# tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0
# parent 8bd3ad5e1234567890abcdef1234567890abcdef
# author John Doe <john@example.com> 1711100000 +0900
# committer John Doe <john@example.com> 1711100000 +0900
#
# feat: add user authentication

Tag: A named reference to a specific commit. Used for release version management.

# Create an annotated tag
git tag -a v1.0.0 -m "Release version 1.0.0"

# Inspect the tag object
git cat-file -p v1.0.0

1.2 SHA-1 Hashing and Content Addressing

Git identifies every object by its SHA-1 hash. Identical content always produces the same hash, which naturally prevents duplicate storage.

# Same content = same hash
echo "same content" | git hash-object --stdin
echo "same content" | git hash-object --stdin
# Both results are identical

The implication is significant: even if 100 branches contain the same file, Git stores it only once.

1.3 DAG (Directed Acyclic Graph) — The Commit Graph

Git's commit history forms a DAG (Directed Acyclic Graph). Each commit points to its parent(s), and no cycles exist.

    A---B---C---D  (main)
         \
          E---F---G  (feature)
               \
                H---I  (hotfix)

Understanding this structure makes merge, rebase, and cherry-pick operations intuitive.

# View the log as a graph
git log --oneline --graph --all --decorate

# Cleaner output
git log --oneline --graph --all --format="%h %s (%ar)"

1.4 Anatomy of the .git Directory

.git/
  HEAD          # Pointer to the current branch
  config        # Repository-specific settings
  refs/
    heads/      # Local branches (each file = a commit hash)
    tags/       # Tags
    remotes/    # Remote branch tracking
  objects/      # All Git objects (blob, tree, commit, tag)
    pack/       # Compressed object pack files
  hooks/        # Git hook scripts
  index         # Staging area (binary)
# Check what HEAD points to
cat .git/HEAD
# ref: refs/heads/main

# See the commit that main points to
cat .git/refs/heads/main
# a1b2c3d4e5f6...

# Check object store size
git count-objects -vH

2. Branching Strategies — Choosing the Right Workflow for Your Team

2.1 Git Flow

A traditional branching model proposed by Vincent Driessen in 2010.

main ─────────────────────────────── (production)
  └── develop ────────────────────── (development integration)
        │        │
        │   feature/login ────────── (feature development)
        └── release/1.0 ──────────── (release preparation)
              └── hotfix/bug-123 ──── (emergency fix)

Pros: Clear release management, production stability guaranteed Cons: Too many branches, frequent merge conflicts, poor fit with CI/CD

# Initialize Git Flow (git-flow extension)
git flow init

# Start feature development
git flow feature start user-auth

# Complete feature development
git flow feature finish user-auth

2.2 GitHub Flow

A simple branching model used by GitHub.

main ────────────────────────────────
  │              │
  └── feature ───┘ (PR + merge)

Rule: main is always deployable. Work on feature branches, merge via PR.

# 1. Create a branch
git checkout -b feature/add-search

# 2. Work and commit
git add .
git commit -m "feat: add search functionality"

# 3. Push
git push -u origin feature/add-search

# 4. Create PR (GitHub CLI)
gh pr create --title "Add search functionality" --body "..."

# 5. Merge after review
gh pr merge --squash

2.3 Trunk-Based Development

Used by large-scale teams at Google, Meta, and others. All developers integrate frequently into main (the trunk), either directly or through very short-lived branches.

main ──A──B──C──D──E──F──G──H──I──
            \─/     \─/
         (1-2 days)(1-2 days)

Core principles:

  • Branch lifespan is at most 1-2 days
  • Integrate into main multiple times per day
  • Feature flags manage incomplete features
  • A robust CI/CD pipeline is mandatory

2.4 Strategy Comparison

AspectGit FlowGitHub FlowTrunk-Based
ComplexityHighLowMedium
Branch lifespanLong (weeks-months)Medium (days-weeks)Short (hours-days)
Release cadenceScheduled releasesOn-demand deploysContinuous deployment
Team size10+ people2-15 peopleAny size
CI/CD dependencyLowMediumHigh
Recommended forMobile apps, packagesSaaS, web appsLarge organizations

2.5 Branch Naming Conventions

# Feature development
feature/JIRA-123-add-login
feature/user-profile-page

# Bug fixes
bugfix/JIRA-456-fix-null-pointer
fix/login-redirect-loop

# Hotfixes
hotfix/security-patch-xss
hotfix/v1.2.1

# Releases
release/1.0.0
release/2026-q1

# Experimental / maintenance
experiment/new-search-algo
chore/update-dependencies

3. The Rebase Master Class

3.1 Rebase vs Merge — When to Use Which

Merge: Preserves the history of both branches while combining them.

git checkout main
git merge feature/login
# A merge commit is created
    A---B---C---D---M  (main, merge commit M)
         \         /
          E---F---G  (feature)

Rebase: Replays commits on top of a different base.

git checkout feature/login
git rebase main
# Feature commits are replayed on top of main
    A---B---C---D  (main)
                 \
                  E'---F'---G'  (feature, replayed)

3.2 Interactive Rebase — The Complete Guide

# Interactively edit the last 5 commits
git rebase -i HEAD~5

Your editor will open with something like this:

pick a1b2c3d feat: add user model
pick d4e5f6g fix: typo in user model
pick h7i8j9k feat: add user controller
pick l0m1n2o WIP: debugging
pick p3q4r5s feat: add user routes

Available commands:

CommandShorthandDescription
pickpUse the commit as-is
rewordrChange the commit message only
editeModify the commit (can change files)
squashsMerge with previous commit, edit message
fixupfMerge with previous commit, discard message
dropdRemove the commit

Real-world example — cleaning up messy history:

pick a1b2c3d feat: add user model
fixup d4e5f6g fix: typo in user model
pick h7i8j9k feat: add user controller
drop l0m1n2o WIP: debugging
pick p3q4r5s feat: add user routes

Result: 5 commits become 3 clean commits.

3.3 The Autosquash Workflow

Using the fixup! prefix allows automatic cleanup.

# Original commit
git commit -m "feat: add user model"

# Later, when a fix is needed
git commit --fixup HEAD
# Or target a specific commit
git commit --fixup a1b2c3d

# Automatic cleanup
git rebase -i --autosquash main

3.4 The Golden Rule of Rebase

Never rebase shared branches (main, develop). Rebase changes commit hashes, which causes conflicts with everyone else's local repositories.

# Safe rebase (personal feature branch)
git checkout feature/my-task
git rebase main

# Dangerous rebase (never do this!)
git checkout main
git rebase feature/something  # Forbidden!

4. 20 Real-World Scenarios — Problem-Solving Recipes

Scenario 1: Wrong Commit Message

# Fix the most recent commit message (not yet pushed)
git commit --amend -m "feat: add user authentication"

# Open editor to edit message
git commit --amend

Scenario 2: Committed to the Wrong Branch

# Situation: committed to feature branch instead of main
# Method 1: cherry-pick
git checkout main
git cherry-pick abc1234
git checkout feature/wrong-branch
git reset --hard HEAD~1

# Method 2: stash
git reset --soft HEAD~1
git stash
git checkout correct-branch
git stash pop
git commit -m "correct commit message"

Scenario 3: Undoing After Push — revert vs reset

# Safe method: revert (creates a new commit, preserves history)
git revert abc1234
git push

# Dangerous method: reset + force push (rewrites history)
git reset --hard HEAD~1
git push --force-with-lease
# force-with-lease protects against overwriting others' commits

revert vs reset comparison:

Aspectrevertreset
HistoryPreserved (new commit added)Rewritten (commits removed)
SafetySafeDangerous
Shared branchesOK to useDo not use
UndoingCan revert the revertRecovery via reflog only

Scenario 4: Merge Conflict Resolution Strategies

# When a conflict occurs
git merge feature/login
# CONFLICT (content): Merge conflict in src/auth.js

# Check conflicted files
git status

# Conflict marker structure
# <<<<<<< HEAD
# Code from current branch
# =======
# Code from the merging branch
# >>>>>>> feature/login

# After manual resolution
git add src/auth.js
git commit -m "resolve merge conflict in auth module"

Strategies for minimizing merge conflicts:

# 1. Sync with main frequently
git fetch origin
git rebase origin/main

# 2. Commit and merge in small increments
# 3. Establish clear file ownership (CODEOWNERS)
# 4. Configure merge tools
git config --global merge.tool vimdiff
git mergetool

Scenario 5: Bug Tracking with git bisect

# Start bisect
git bisect start

# Mark current state as bad (has bug)
git bisect bad

# Mark a known good commit
git bisect good v1.0.0

# Git checks out a middle commit — test and decide
git bisect good  # or git bisect bad

# Repeat until the exact bad commit is found
# Result: abc1234 is the first bad commit

# End bisect
git bisect reset

Automated bisect with test scripts:

# Test script failure = bad, success = good
git bisect start HEAD v1.0.0
git bisect run npm test
# Automatically finds the bug-introducing commit!

Scenario 6: Accidentally Committed a Large File

# Method 1: git-filter-repo (recommended)
pip install git-filter-repo
git filter-repo --path large-file.zip --invert-paths

# Method 2: BFG Repo-Cleaner
java -jar bfg.jar --delete-files large-file.zip
git reflog expire --expire=now --all
git gc --prune=now --aggressive

# Prevention: .gitignore
echo "*.zip" >> .gitignore
echo "*.tar.gz" >> .gitignore
echo "node_modules/" >> .gitignore

Scenario 7: Staging Parts of a File — git add -p

# Selectively stage changes by hunk
git add -p src/utils.js

# For each hunk:
# y - stage this hunk
# n - skip this hunk
# s - split into smaller hunks
# e - manually edit
# q - quit

Scenario 8: Urgent Hotfix While Working — stash + worktree

# Method 1: stash
git stash push -m "WIP: feature work"
git checkout main
git checkout -b hotfix/critical-bug
# Work on the hotfix...
git checkout feature/my-work
git stash pop

# Method 2: worktree (the better approach!)
git worktree add ../hotfix-workspace main
cd ../hotfix-workspace
git checkout -b hotfix/critical-bug
# Work on the hotfix...
cd ../main-workspace
git worktree remove ../hotfix-workspace

Scenario 9: Squashing Multiple Commits into One

# Method 1: interactive rebase
git rebase -i HEAD~4
# Change first to pick, rest to squash

# Method 2: soft reset
git reset --soft HEAD~4
git commit -m "feat: complete user authentication"

# Method 3: merge --squash
git checkout main
git merge --squash feature/user-auth
git commit -m "feat: add user authentication system"

Scenario 10: Submodule Management

# Add a submodule
git submodule add https://github.com/org/shared-lib.git libs/shared

# Clone with submodules
git clone --recurse-submodules https://github.com/org/project.git

# Update submodules
git submodule update --remote --merge

# Run command across all submodules
git submodule foreach 'git checkout main && git pull'

Scenario 11: Recover a Deleted Branch

# Find the last commit of the deleted branch in reflog
git reflog
# abc1234 HEAD@{5}: checkout: moving from deleted-branch to main

# Recover the branch
git checkout -b recovered-branch abc1234

Scenario 12: Checkout a Specific File from Another Branch

# Get a specific file from the main branch
git checkout main -- src/config.js

# Get a file from a specific commit
git checkout abc1234 -- src/config.js

Scenario 13: Changing Commit Dates

# Change the last commit date to now
GIT_COMMITTER_DATE="$(date)" git commit --amend --date="$(date)"

# Set a specific date (during rebase)
GIT_COMMITTER_DATE="2026-03-22T10:00:00" git commit --amend --date="2026-03-22T10:00:00"

Scenario 14: Code Tracking with git blame

# See who last modified each line
git blame src/auth.js

# Check a specific line range
git blame -L 10,20 src/auth.js

# Ignore whitespace changes
git blame -w src/auth.js

# Track code movement
git blame -M src/auth.js
git blame -C src/auth.js

Scenario 15: Advanced git log

# File change history (follows renames)
git log --follow -p -- src/auth.js

# Find commits that added/removed a specific string (pickaxe)
git log -S "API_KEY" --oneline

# Filter by author
git log --author="John" --oneline --since="2026-01-01"

# Include statistics
git log --stat --oneline -10

# Diff between commits
git log -p --oneline -3

Scenario 16: Managing Multiple Remotes

# Add multiple remotes
git remote add upstream https://github.com/original/repo.git
git remote add deploy git@deploy-server:app.git

# Sync with upstream (forked project)
git fetch upstream
git rebase upstream/main

# Clean up remote branches
git remote prune origin
git fetch --prune

Scenario 17: Release Management with Tags

# Create an annotated tag
git tag -a v2.0.0 -m "Release 2.0.0: new payment system"

# Tag a specific commit
git tag -a v1.5.0 abc1234 -m "Hotfix release"

# Push tags
git push origin v2.0.0
git push origin --tags

# Delete a tag
git tag -d v2.0.0-beta
git push origin --delete v2.0.0-beta

Scenario 18: Automation with Git Hooks

# .husky/pre-commit
#!/bin/sh
npx lint-staged

# .husky/commit-msg
#!/bin/sh
npx commitlint --edit "$1"

# .husky/pre-push
#!/bin/sh
npm test

Scenario 19: Creating and Applying Patches

# Create a patch
git diff > my-changes.patch
git format-patch -3  # Create patch files from the last 3 commits

# Apply a patch
git apply my-changes.patch
git am *.patch  # Apply patches created by format-patch

Scenario 20: git reflog — The Last Resort

# View the reflog (HEAD movement history)
git reflog

# Recover after an accidental reset --hard
git reflog
# abc1234 HEAD@{2}: commit: important work
git reset --hard abc1234

# Recover an accidentally dropped stash
git fsck --unreachable | grep commit
git stash apply abc1234

5. Conventional Commits + Semantic Versioning

5.1 Conventional Commits Format

type(scope): description

[optional body]

[optional footer(s)]

Type reference:

TypeDescriptionExample
featNew featurefeat: add login page
fixBug fixfix: resolve null pointer in auth
docsDocumentationdocs: update API documentation
styleCode style (formatting, etc.)style: fix indentation
refactorRefactoringrefactor: extract auth middleware
perfPerformance improvementperf: optimize database queries
testAdding/modifying teststest: add unit tests for auth
buildBuild system changesbuild: update webpack config
ciCI configurationci: add GitHub Actions workflow
choreMiscellaneous taskschore: update dependencies

Breaking Change notation:

feat(api)!: change authentication endpoint

BREAKING CHANGE: /auth/login is now /api/v2/auth/login

5.2 Commitlint + Husky Setup

# Install packages
npm install -D @commitlint/cli @commitlint/config-conventional husky

# Configure commitlint
echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js

# Initialize Husky
npx husky init
echo 'npx commitlint --edit "$1"' > .husky/commit-msg

5.3 Automated Changelog Generation

# Install conventional-changelog
npm install -D conventional-changelog-cli

# Generate CHANGELOG.md
npx conventional-changelog -p angular -i CHANGELOG.md -s
{
  "scripts": {
    "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
    "release": "standard-version"
  }
}

5.4 Semantic Release — Full Automation

npm install -D semantic-release
{
  "release": {
    "branches": ["main"],
    "plugins": [
      "@semantic-release/commit-analyzer",
      "@semantic-release/release-notes-generator",
      "@semantic-release/changelog",
      "@semantic-release/npm",
      "@semantic-release/github",
      "@semantic-release/git"
    ]
  }
}

Versions are determined automatically based on commit types:

  • fix: -> PATCH (1.0.0 -> 1.0.1)
  • feat: -> MINOR (1.0.0 -> 1.1.0)
  • BREAKING CHANGE -> MAJOR (1.0.0 -> 2.0.0)

6. GitHub Actions CI/CD

6.1 Basic Workflow

name: CI Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20, 22]

    steps:
      - uses: actions/checkout@v4

      - name: Use Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'

      - run: npm ci
      - run: npm run lint
      - run: npm test
      - run: npm run build

6.2 PR Check Workflow

name: PR Checks

on:
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  lint-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - run: npm ci

      - name: Lint
        run: npm run lint

      - name: Type Check
        run: npx tsc --noEmit

      - name: Unit Tests
        run: npm test -- --coverage

      - name: Build
        run: npm run build

      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          file: ./coverage/lcov.info

6.3 Auto-Labeling

name: Auto Label

on:
  pull_request:
    types: [opened, edited]

jobs:
  label:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/labeler@v5
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}
# .github/labeler.yml
frontend:
  - changed-files:
      - any-glob-to-any-file: 'src/components/**'

backend:
  - changed-files:
      - any-glob-to-any-file: 'src/api/**'

documentation:
  - changed-files:
      - any-glob-to-any-file: 'docs/**'

tests:
  - changed-files:
      - any-glob-to-any-file: '**/*.test.*'

6.4 Dependabot Configuration

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: 'npm'
    directory: '/'
    schedule:
      interval: 'weekly'
      day: 'monday'
    open-pull-requests-limit: 10
    labels:
      - 'dependencies'
    groups:
      dev-dependencies:
        dependency-type: 'development'
      production-dependencies:
        dependency-type: 'production'

  - package-ecosystem: 'github-actions'
    directory: '/'
    schedule:
      interval: 'weekly'

6.5 Deployment Workflow

name: Deploy

on:
  push:
    tags:
      - 'v*'

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - run: npm ci
      - run: npm run build

      - name: Deploy to Production
        run: |
          echo "Deploying version $GITHUB_REF_NAME"
          # Actual deployment commands here

7. Monorepo Strategies

7.1 Monorepo vs Multirepo

AspectMonorepoMultirepo
Code sharingEasyRequires package publishing
Dependency managementUnifiedIndividual
CI/CDComplex (selective builds)Simple (per repository)
Code reviewFull context availableDistributed across repos
Permission managementComplexPer-repository settings
Notable usersGoogle, Meta, MicrosoftNetflix, Spotify

7.2 Tool Comparison — Nx vs Turborepo vs Lerna

# Initialize Nx
npx create-nx-workspace@latest my-workspace

# Initialize Turborepo
npx create-turbo@latest

# Example project structure
my-monorepo/
  apps/
    web/          # Next.js web app
    mobile/       # React Native app
    api/          # Express API server
  packages/
    ui/           # Shared UI components
    utils/        # Shared utilities
    config/       # Shared configuration
  turbo.json
  package.json

7.3 CODEOWNERS Configuration

# .github/CODEOWNERS

# Default reviewers for the entire project
* @team-lead

# Frontend
/apps/web/        @frontend-team
/packages/ui/     @frontend-team

# Backend
/apps/api/        @backend-team
/packages/utils/  @backend-team

# DevOps
/.github/         @devops-team
/docker/          @devops-team
*.yml             @devops-team

7.4 Selective Builds and Tests

{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    },
    "test": {
      "dependsOn": ["build"]
    },
    "lint": {}
  }
}
# Build only changed packages
npx turbo run build --filter=...[HEAD^1]

# Build a specific app
npx turbo run build --filter=web

# Build including dependencies
npx turbo run build --filter=web...

8. Git Performance Optimization

8.1 Shallow Clone

# Clone only the latest commit (useful for CI/CD)
git clone --depth 1 https://github.com/org/repo.git

# Fetch full history later if needed
git fetch --unshallow

8.2 Partial Clone

# Clone without blobs (downloaded on demand)
git clone --filter=blob:none https://github.com/org/repo.git

# Exclude trees as well (fastest clone)
git clone --filter=tree:0 https://github.com/org/repo.git

8.3 Sparse Checkout

# Enable sparse checkout
git sparse-checkout init --cone

# Checkout specific directories only
git sparse-checkout set apps/web packages/ui

# Add another directory
git sparse-checkout add apps/api

# Check current configuration
git sparse-checkout list

8.4 Git LFS (Large File Storage)

# Install and initialize Git LFS
git lfs install

# Track large files
git lfs track "*.psd"
git lfs track "*.zip"
git lfs track "assets/videos/*"

# Check .gitattributes
cat .gitattributes
# *.psd filter=lfs diff=lfs merge=lfs -text

# Check LFS status
git lfs ls-files
git lfs status

8.5 Git Maintenance (Automated Housekeeping)

# Enable automatic maintenance
git maintenance start

# Manual garbage collection
git gc --aggressive

# Check object compression status
git count-objects -vH

# Clean up stale references
git remote prune origin
git reflog expire --expire=30.days

8.6 Scalar — Managing Massive Repositories (Microsoft)

# Register with Scalar (included in Git 2.38+)
scalar register

# Or clone fresh
scalar clone https://github.com/org/massive-repo.git

# Scalar automatically configures:
# - Partial clone
# - Sparse checkout
# - Background maintenance
# - Filesystem monitor
# - Commit graph

9. AI + Git (2025-2026)

9.1 AI Commit Message Generation

# Auto-generate commit messages with Claude Code
claude commit

# GitHub Copilot CLI
gh copilot suggest "write a commit message for these changes"

# aicommits tool
npm install -g aicommits
aicommits

9.2 AI Code Review

GitHub Copilot for Pull Requests automatically analyzes PRs and generates review comments. It identifies potential bugs, performance issues, and security vulnerabilities.

# Enable GitHub Copilot auto-review (in repository settings)
# Settings > Code review > Copilot code review > Enable

9.3 Claude Code and Git Workflows

Claude Code directly assists with Git operations.

# Request a review of staged changes
claude "review my staged changes"

# Write a commit message
claude "write a conventional commit message for my changes"

# Generate a PR description
claude "write a PR description for the current branch"

# Resolve merge conflicts
claude "help me resolve the merge conflicts in src/auth.js"

9.4 Automated PR Descriptions

name: Auto PR Description

on:
  pull_request:
    types: [opened]

jobs:
  describe:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Generate PR description
        uses: anthropics/claude-code-action@v1
        with:
          prompt: |
            Analyze the diff of this PR and generate a clear description
            including what changed, why, and any risks.

Knowledge Check Quiz

Quiz 1: Git Objects

Q: In which object are filenames stored — Blob or Tree?

A: Filenames are stored in the Tree object.

Blobs store only the raw file contents. Filenames and path information are managed by Tree objects. This design means that even if a file is renamed, Git can reuse the same blob as long as the content is unchanged.

# See filenames in a tree object
git cat-file -p HEAD^{tree}
# 100644 blob a906cb...    README.md  <-- Filename lives in the Tree!

Quiz 2: Rebase Safety

Q: Which of the following is a safe rebase operation? (A: rebase feature on main, B: rebase main on feature, C: Both)

A: B is safe.

The golden rule of rebase is "never rebase shared branches." main is a shared branch, so rebasing on main is dangerous. However, feature is a personal branch, so replaying its commits on top of the latest main is safe.

Quiz 3: Reset Modes

Q: What are the differences between the three reset modes (--soft, --mixed, --hard)?

A:

  • --soft: Moves HEAD only. Staging area and working directory are untouched. Use when you just want to undo a commit.
  • --mixed (default): Moves HEAD + resets staging area. Working directory is untouched. Use when you want to undo a commit and unstage.
  • --hard: Moves HEAD + resets staging area + resets working directory. All changes are discarded. (Dangerous!)
# Example: undo the last 3 commits but keep the code
git reset --soft HEAD~3
# Changes are now staged and ready to be recommitted

Quiz 4: Cherry-pick Conflicts

Q: How do you resolve a conflict during cherry-pick?

A: Resolve it the same way as a merge conflict.

git cherry-pick abc1234
# CONFLICT occurs

# 1. Fix the conflicted file
# 2. Stage it
git add resolved-file.js

# 3. Continue cherry-pick
git cherry-pick --continue

# Or abort the cherry-pick
git cherry-pick --abort

Quiz 5: Selective CI in a Monorepo

Q: In a monorepo, if only frontend code changed, should you also run backend tests?

A: It depends on dependencies.

If the frontend does not depend on any backend packages, there is no need to run backend tests. This is the core value of "affected analysis" provided by tools like Nx and Turborepo.

# Nx: test only projects affected by changes
npx nx affected --target=test

# Turborepo: build only changed packages and dependents
npx turbo run build --filter=...[HEAD^1]

However, if a shared package (utils, config, etc.) was modified, you must test all projects that depend on it.


References

Official Documentation

  1. Git Official Docs — The definitive reference
  2. GitHub Docs — GitHub feature guides
  3. Pro Git Book — Free online book

Branching Strategies

  1. Trunk Based Development — Official TBD site
  2. GitHub Flow Guide
  3. A successful Git branching model — Original Git Flow article

CI/CD

  1. GitHub Actions Docs
  2. Semantic Release — Automated versioning
  3. Conventional Commits — Commit conventions

Monorepo

  1. Nx Docs
  2. Turborepo Docs
  3. Monorepo Tools — Tool comparison

Performance

  1. Git LFS — Large file management
  2. Scalar — Massive repositories
  3. git-filter-repo — History cleanup

Deep Dives

  1. Oh Shit, Git!?! — Git mistake recovery guide
  2. Learn Git Branching — Visual Git learning
  3. Git Internals (PDF) — Git internal architecture

Conclusion

Git is not just a tool — it is core infrastructure that shapes development culture. A clean commit history reflects a team's collaboration capability, and an effective branching strategy determines deployment stability.

You do not need to learn everything in this article at once. Start by understanding the principles behind commands you use daily, then gradually adopt more advanced techniques. Interactive rebase and bisect, in particular, are tools that dramatically boost productivity once you learn them.

Remember: a good commit history is a letter to your future self and your colleagues. Why not start with Conventional Commits today?