09. κ³ κΈ‰ Git 기법

09. κ³ κΈ‰ Git 기법

ν•™μŠ΅ λͺ©ν‘œ

  • Git Hooksλ₯Ό ν™œμš©ν•œ μžλ™ν™”
  • Submodules둜 μ™ΈλΆ€ μ˜μ‘΄μ„± 관리
  • Worktrees둜 μ—¬λŸ¬ 브랜치 λ™μ‹œ μž‘μ—…
  • Git λ‚΄λΆ€ ꡬ쑰와 μ €μˆ˜μ€€ λͺ…λ Ήμ–΄ 이해

λͺ©μ°¨

  1. Git Hooks
  2. Git Submodules
  3. Git Worktrees
  4. κ³ κΈ‰ λͺ…λ Ήμ–΄
  5. Git λ‚΄λΆ€ ꡬ쑰
  6. νŠΈλŸ¬λΈ”μŠˆνŒ…
  7. μ—°μŠ΅ 문제

1. Git Hooks

1.1 Git Hooks κ°œμš”

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     Git Hooks μ’…λ₯˜                           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                             β”‚
β”‚  ν΄λΌμ΄μ–ΈνŠΈ ν›… (둜컬):                                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  컀밋 μ›Œν¬ν”Œλ‘œμš°:                                    β”‚   β”‚
β”‚  β”‚  β€’ pre-commit    : 컀밋 μ „ (린트, ν…ŒμŠ€νŠΈ)           β”‚   β”‚
β”‚  β”‚  β€’ prepare-commit-msg : 컀밋 λ©”μ‹œμ§€ μ€€λΉ„            β”‚   β”‚
β”‚  β”‚  β€’ commit-msg    : 컀밋 λ©”μ‹œμ§€ 검증                  β”‚   β”‚
β”‚  β”‚  β€’ post-commit   : 컀밋 ν›„                          β”‚   β”‚
β”‚  β”‚                                                      β”‚   β”‚
β”‚  β”‚  이메일 μ›Œν¬ν”Œλ‘œμš°:                                  β”‚   β”‚
β”‚  β”‚  β€’ applypatch-msg                                   β”‚   β”‚
β”‚  β”‚  β€’ pre-applypatch                                   β”‚   β”‚
β”‚  β”‚  β€’ post-applypatch                                  β”‚   β”‚
β”‚  β”‚                                                      β”‚   β”‚
β”‚  β”‚  기타:                                               β”‚   β”‚
β”‚  β”‚  β€’ pre-rebase    : rebase μ „                        β”‚   β”‚
β”‚  β”‚  β€’ post-checkout : checkout ν›„                      β”‚   β”‚
β”‚  β”‚  β€’ post-merge    : merge ν›„                         β”‚   β”‚
β”‚  β”‚  β€’ pre-push      : push μ „                          β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                             β”‚
β”‚  μ„œλ²„ ν›… (리λͺ¨νŠΈ):                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  β€’ pre-receive   : push λ°›κΈ° μ „                     β”‚   β”‚
β”‚  β”‚  β€’ update        : 각 브랜치 μ—…λ°μ΄νŠΈ μ „            β”‚   β”‚
β”‚  β”‚  β€’ post-receive  : push 받은 ν›„                     β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

1.2 κΈ°λ³Έ Hook μ„€μ •

# Hook μœ„μΉ˜
ls .git/hooks/
# pre-commit.sample, commit-msg.sample, ...

# Hook ν™œμ„±ν™” (μƒ˜ν”Œμ—μ„œ .sample 제거)
cp .git/hooks/pre-commit.sample .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit

# λ˜λŠ” 직접 생성
touch .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit

1.3 pre-commit Hook 예제

#!/bin/bash
# .git/hooks/pre-commit

echo "Running pre-commit checks..."

# 1. 린트 검사
echo "Running ESLint..."
npm run lint
if [ $? -ne 0 ]; then
    echo "❌ ESLint failed. Please fix the errors."
    exit 1
fi

# 2. νƒ€μž… 검사
echo "Running TypeScript check..."
npm run type-check
if [ $? -ne 0 ]; then
    echo "❌ TypeScript check failed."
    exit 1
fi

# 3. λ‹¨μœ„ ν…ŒμŠ€νŠΈ
echo "Running tests..."
npm test -- --watchAll=false
if [ $? -ne 0 ]; then
    echo "❌ Tests failed."
    exit 1
fi

# 4. 민감 정보 검사
echo "Checking for secrets..."
if git diff --cached --name-only | xargs grep -l -E "(password|secret|api_key)\s*=" 2>/dev/null; then
    echo "❌ Potential secrets detected!"
    exit 1
fi

# 5. 파일 크기 검사
echo "Checking file sizes..."
MAX_SIZE=5242880  # 5MB
for file in $(git diff --cached --name-only); do
    if [ -f "$file" ]; then
        size=$(wc -c < "$file")
        if [ $size -gt $MAX_SIZE ]; then
            echo "❌ File $file is too large ($size bytes)"
            exit 1
        fi
    fi
done

echo "βœ… All pre-commit checks passed!"
exit 0

1.4 commit-msg Hook 예제

#!/bin/bash
# .git/hooks/commit-msg

COMMIT_MSG_FILE=$1
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")

# Conventional Commits ν˜•μ‹ 검사
# type(scope): description
PATTERN="^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{1,100}$"

if ! echo "$COMMIT_MSG" | head -1 | grep -qE "$PATTERN"; then
    echo "❌ Invalid commit message format!"
    echo ""
    echo "Commit message must follow Conventional Commits:"
    echo "  <type>(<scope>): <description>"
    echo ""
    echo "Types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert"
    echo ""
    echo "Examples:"
    echo "  feat(auth): add login functionality"
    echo "  fix(api): resolve null pointer exception"
    echo "  docs: update README"
    echo ""
    exit 1
fi

# λ©”μ‹œμ§€ 길이 검사
FIRST_LINE=$(echo "$COMMIT_MSG" | head -1)
if [ ${#FIRST_LINE} -gt 72 ]; then
    echo "❌ First line must be 72 characters or less"
    exit 1
fi

echo "βœ… Commit message is valid!"
exit 0

1.5 pre-push Hook 예제

#!/bin/bash
# .git/hooks/pre-push

REMOTE=$1
URL=$2

# main/master 브랜치둜 직접 push λ°©μ§€
PROTECTED_BRANCHES="main master"
CURRENT_BRANCH=$(git symbolic-ref HEAD | sed 's!refs/heads/!!')

for branch in $PROTECTED_BRANCHES; do
    if [ "$CURRENT_BRANCH" = "$branch" ]; then
        echo "❌ Direct push to $branch is not allowed!"
        echo "Please create a pull request instead."
        exit 1
    fi
done

# 전체 ν…ŒμŠ€νŠΈ μ‹€ν–‰
echo "Running full test suite before push..."
npm run test:ci
if [ $? -ne 0 ]; then
    echo "❌ Tests failed. Push aborted."
    exit 1
fi

# λΉŒλ“œ 검증
echo "Verifying build..."
npm run build
if [ $? -ne 0 ]; then
    echo "❌ Build failed. Push aborted."
    exit 1
fi

echo "βœ… All pre-push checks passed!"
exit 0

1.6 Husky둜 Hook 관리

# Husky μ„€μΉ˜
npm install husky -D
npx husky init

# package.json에 prepare 슀크립트 μΆ”κ°€
# "prepare": "husky"

# pre-commit hook μΆ”κ°€
echo "npm run lint && npm test" > .husky/pre-commit

# commit-msg hook μΆ”κ°€
npm install @commitlint/cli @commitlint/config-conventional -D
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg

# commitlint.config.js
# module.exports = { extends: ['@commitlint/config-conventional'] };
// lint-staged.config.js
module.exports = {
  '*.{js,jsx,ts,tsx}': [
    'eslint --fix',
    'prettier --write',
    'jest --findRelatedTests --passWithNoTests'
  ],
  '*.{json,md,yml,yaml}': [
    'prettier --write'
  ],
  '*.css': [
    'stylelint --fix',
    'prettier --write'
  ]
};

2. Git Submodules

2.1 Submodules κ°œμš”

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Git Submodules                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                             β”‚
β”‚  메인 μ €μž₯μ†Œ                                                β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  my-project/                                         β”‚   β”‚
β”‚  β”‚  β”œβ”€β”€ src/                                           β”‚   β”‚
β”‚  β”‚  β”œβ”€β”€ tests/                                         β”‚   β”‚
β”‚  β”‚  β”œβ”€β”€ .gitmodules      ← μ„œλΈŒλͺ¨λ“ˆ μ„€μ •              β”‚   β”‚
β”‚  β”‚  └── libs/                                          β”‚   β”‚
β”‚  β”‚      β”œβ”€β”€ shared-ui/   ← μ„œλΈŒλͺ¨λ“ˆ (μ™ΈλΆ€ μ €μž₯μ†Œ)     β”‚   β”‚
β”‚  β”‚      └── common-utils/← μ„œλΈŒλͺ¨λ“ˆ (μ™ΈλΆ€ μ €μž₯μ†Œ)     β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                             β”‚
β”‚  νŠΉμ§•:                                                      β”‚
β”‚  β€’ μ™ΈλΆ€ μ €μž₯μ†Œλ₯Ό ν•˜μœ„ λ””λ ‰ν† λ¦¬λ‘œ 포함                       β”‚
β”‚  β€’ νŠΉμ • 컀밋에 고정됨                                       β”‚
β”‚  β€’ 독립적인 버전 관리                                       β”‚
β”‚  β€’ 곡유 라이브러리, μ˜μ‘΄μ„± 관리에 유용                      β”‚
β”‚                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2.2 Submodule κΈ°λ³Έ λͺ…λ Ή

# μ„œλΈŒλͺ¨λ“ˆ μΆ”κ°€
git submodule add https://github.com/example/shared-ui.git libs/shared-ui

# .gitmodules 파일 생성됨
# [submodule "libs/shared-ui"]
#     path = libs/shared-ui
#     url = https://github.com/example/shared-ui.git

# νŠΉμ • 브랜치 좔적
git submodule add -b develop https://github.com/example/lib.git libs/lib

# μ„œλΈŒλͺ¨λ“ˆμ΄ μžˆλŠ” μ €μž₯μ†Œ 클둠
git clone --recursive https://github.com/example/main-project.git

# λ˜λŠ” 클둠 ν›„ μ΄ˆκΈ°ν™”
git clone https://github.com/example/main-project.git
git submodule init
git submodule update

# λ˜λŠ” ν•œ λ²ˆμ—
git submodule update --init --recursive

2.3 Submodule μ—…λ°μ΄νŠΈ

# μ„œλΈŒλͺ¨λ“ˆ μ—…λ°μ΄νŠΈ (μ„€μ •λœ μ»€λ°‹μœΌλ‘œ)
git submodule update

# μ„œλΈŒλͺ¨λ“ˆμ„ μ΅œμ‹ μœΌλ‘œ μ—…λ°μ΄νŠΈ
git submodule update --remote

# νŠΉμ • μ„œλΈŒλͺ¨λ“ˆλ§Œ μ—…λ°μ΄νŠΈ
git submodule update --remote libs/shared-ui

# λͺ¨λ“  μ„œλΈŒλͺ¨λ“ˆμ—μ„œ λͺ…λ Ή μ‹€ν–‰
git submodule foreach 'git checkout main && git pull'

# μ„œλΈŒλͺ¨λ“ˆ μƒνƒœ 확인
git submodule status
# -abc1234 libs/shared-ui (v1.0.0)    ← - λŠ” μ΄ˆκΈ°ν™” μ•ˆ 됨
# +def5678 libs/common-utils (heads/main)  ← + λŠ” λ‹€λ₯Έ 컀밋

# 변경사항 컀밋
cd libs/shared-ui
git checkout main
git pull
cd ../..
git add libs/shared-ui
git commit -m "Update shared-ui submodule"

2.4 Submodule 제거

# 1. .gitmodulesμ—μ„œ ν•­λͺ© 제거
git config -f .gitmodules --remove-section submodule.libs/shared-ui

# 2. .git/configμ—μ„œ ν•­λͺ© 제거
git config --remove-section submodule.libs/shared-ui

# 3. μŠ€ν…Œμ΄μ§•μ—μ„œ 제거
git rm --cached libs/shared-ui

# 4. .git/modulesμ—μ„œ 제거
rm -rf .git/modules/libs/shared-ui

# 5. μž‘μ—… λ””λ ‰ν† λ¦¬μ—μ„œ 제거
rm -rf libs/shared-ui

# 6. 컀밋
git commit -m "Remove shared-ui submodule"

2.5 Submodule μ£Όμ˜μ‚¬ν•­

# ⚠️ μ„œλΈŒλͺ¨λ“ˆ λ‚΄μ—μ„œ 브랜치 확인
cd libs/shared-ui
git branch
# * (HEAD detached at abc1234)  ← Detached HEAD!

# μ„œλΈŒλͺ¨λ“ˆμ—μ„œ μž‘μ—…ν•˜λ €λ©΄ 브랜치둜 체크아웃
git checkout main
# 이제 λ³€κ²½ κ°€λŠ₯

# ⚠️ Pull μ‹œ μ„œλΈŒλͺ¨λ“ˆ μžλ™ μ—…λ°μ΄νŠΈ
git pull --recurse-submodules

# λ˜λŠ” μ„€μ •
git config --global submodule.recurse true

# ⚠️ μ„œλΈŒλͺ¨λ“ˆ λ³€κ²½ ν›„ 메인 μ €μž₯μ†Œμ—μ„œ 컀밋 ν•„μš”
git status
# modified:   libs/shared-ui (new commits)
git add libs/shared-ui
git commit -m "Update shared-ui to latest"

3. Git Worktrees

3.1 Worktrees κ°œμš”

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Git Worktrees                             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                             β”‚
β”‚  ν•˜λ‚˜μ˜ μ €μž₯μ†Œ, μ—¬λŸ¬ μž‘μ—… 디렉토리                          β”‚
β”‚                                                             β”‚
β”‚  ~/.git/my-project/     ← 메인 μ €μž₯μ†Œ                      β”‚
β”‚  β”œβ”€β”€ .git/                                                  β”‚
β”‚  β”œβ”€β”€ src/                                                   β”‚
β”‚  └── (ν˜„μž¬ 브랜치: main)                                    β”‚
β”‚                                                             β”‚
β”‚  ~/worktrees/feature-a/ ← Worktree 1                       β”‚
β”‚  β”œβ”€β”€ .git (파일, 메인 .git μ°Έμ‘°)                           β”‚
β”‚  β”œβ”€β”€ src/                                                   β”‚
β”‚  └── (ν˜„μž¬ 브랜치: feature/a)                               β”‚
β”‚                                                             β”‚
β”‚  ~/worktrees/hotfix/    ← Worktree 2                       β”‚
β”‚  β”œβ”€β”€ .git (파일, 메인 .git μ°Έμ‘°)                           β”‚
β”‚  β”œβ”€β”€ src/                                                   β”‚
β”‚  └── (ν˜„μž¬ 브랜치: hotfix/urgent)                           β”‚
β”‚                                                             β”‚
β”‚  μž₯점:                                                      β”‚
β”‚  β€’ stash 없이 브랜치 μ „ν™˜                                   β”‚
β”‚  β€’ μ—¬λŸ¬ 브랜치 λ™μ‹œ μž‘μ—…                                    β”‚
β”‚  β€’ κΈ΄ λΉŒλ“œ 쀑 λ‹€λ₯Έ μž‘μ—… κ°€λŠ₯                                β”‚
β”‚  β€’ CIμ—μ„œ μ—¬λŸ¬ 브랜치 병렬 λΉŒλ“œ                             β”‚
β”‚                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

3.2 Worktree λͺ…λ Ήμ–΄

# Worktree λͺ©λ‘ 확인
git worktree list
# /home/user/my-project        abc1234 [main]

# μƒˆ Worktree μΆ”κ°€ (κΈ°μ‘΄ 브랜치)
git worktree add ../feature-a feature/a
# Preparing worktree (checking out 'feature/a')

# μƒˆ Worktree μΆ”κ°€ (μƒˆ 브랜치 생성)
git worktree add -b feature/b ../feature-b main

# νŠΉμ • κ²½λ‘œμ— μΆ”κ°€
git worktree add ~/worktrees/hotfix hotfix/urgent

# Worktree λͺ©λ‘ 확인
git worktree list
# /home/user/my-project        abc1234 [main]
# /home/user/feature-a         def5678 [feature/a]
# /home/user/worktrees/hotfix  ghi9012 [hotfix/urgent]

# Worktreeμ—μ„œ μž‘μ—…
cd ../feature-a
# 일반적인 Git μž‘μ—… μˆ˜ν–‰
git add .
git commit -m "Work on feature A"
git push

# Worktree 제거
git worktree remove ../feature-a

# λ˜λŠ” 디렉토리 μ‚­μ œ ν›„ 정리
rm -rf ../feature-a
git worktree prune  # μœ νš¨ν•˜μ§€ μ•Šμ€ worktree 정리

# 잠금/잠금 ν•΄μ œ (μ‹€μˆ˜λ‘œ μ‚­μ œ λ°©μ§€)
git worktree lock ../feature-a
git worktree unlock ../feature-a

3.3 Worktree ν™œμš© 사둀

# 사둀 1: κΈ΄κΈ‰ 버그 μˆ˜μ •
# ν˜„μž¬ feature μž‘μ—… 쀑인데 κΈ΄κΈ‰ 버그 λ°œμƒ
git worktree add ../hotfix main
cd ../hotfix
git checkout -b hotfix/critical-bug
# 버그 μˆ˜μ •
git add . && git commit -m "Fix critical bug"
git push -u origin hotfix/critical-bug
# PR 생성 ν›„ 병합
cd ../my-project
git worktree remove ../hotfix

# 사둀 2: μ½”λ“œ 리뷰
# PR μ½”λ“œλ₯Ό λ‘œμ»¬μ—μ„œ 확인
git fetch origin
git worktree add ../pr-123 origin/feature/new-feature
cd ../pr-123
npm install && npm test
# 리뷰 ν›„ 제거
git worktree remove ../pr-123

# 사둀 3: 병렬 λΉŒλ“œ (CI)
git worktree add ../build-debug main
git worktree add ../build-release main
cd ../build-debug && npm run build:debug &
cd ../build-release && npm run build:release &
wait

# 사둀 4: 버전 비ꡐ
git worktree add ../v1.0 v1.0.0
git worktree add ../v2.0 v2.0.0
diff -r ../v1.0/src ../v2.0/src

4. κ³ κΈ‰ λͺ…λ Ήμ–΄

4.1 Git Bisect (이진 검색)

# 버그가 λ°œμƒν•œ 컀밋 μ°ΎκΈ°
git bisect start

# ν˜„μž¬ μƒνƒœ (버그 있음)
git bisect bad

# μ •μƒμ΄μ—ˆλ˜ 컀밋
git bisect good abc1234

# Git이 쀑간 μ»€λ°‹μœΌλ‘œ 체크아웃
# ν…ŒμŠ€νŠΈ ν›„ κ²°κ³Ό ν‘œμ‹œ
git bisect good  # λ˜λŠ” git bisect bad

# 반볡...
# κ²°κ³Ό:
# abc1234 is the first bad commit

# μ’…λ£Œ
git bisect reset

# μžλ™ν™”λœ bisect
git bisect start HEAD abc1234
git bisect run npm test
# μžλ™μœΌλ‘œ good/bad νŒλ‹¨ν•˜μ—¬ 찾음

4.2 Git Reflog

# λͺ¨λ“  HEAD 이동 기둝
git reflog
# abc1234 HEAD@{0}: commit: Add feature
# def5678 HEAD@{1}: checkout: moving from main to feature
# ghi9012 HEAD@{2}: reset: moving to HEAD~1
# ...

# νŠΉμ • 브랜치의 reflog
git reflog show main

# μ‚­μ œλœ 컀밋 볡ꡬ
git reflog
# abc1234 HEAD@{5}: commit: Important work  ← 이 컀밋 볡ꡬ
git checkout abc1234
git checkout -b recovered-branch

# 잘λͺ»λœ reset μ·¨μ†Œ
git reset --hard HEAD@{2}

# reflog 만료 κΈ°κ°„ (κΈ°λ³Έ 90일)
git config gc.reflogExpire 180.days

4.3 Git Stash κ³ κΈ‰

# κΈ°λ³Έ stash
git stash
git stash push -m "Work in progress on feature X"

# νŠΉμ • 파일만 stash
git stash push -m "Partial work" -- src/file1.js src/file2.js

# Untracked 파일 포함
git stash push -u -m "Include untracked"

# λͺ¨λ“  파일 포함 (ignored 포함)
git stash push -a -m "Include all"

# Stash λͺ©λ‘
git stash list
# stash@{0}: On feature: Work in progress
# stash@{1}: On main: Bug fix attempt

# νŠΉμ • stash 적용 (μ‚­μ œ μ•ˆ 함)
git stash apply stash@{1}

# νŠΉμ • stash 적용 ν›„ μ‚­μ œ
git stash pop stash@{1}

# Stash λ‚΄μš© 확인
git stash show -p stash@{0}

# Stashλ₯Ό 브랜치둜 λ³€ν™˜
git stash branch new-feature stash@{0}

# Stash μ‚­μ œ
git stash drop stash@{0}
git stash clear  # λͺ¨λ‘ μ‚­μ œ

4.4 Git Cherry-pick κ³ κΈ‰

# κΈ°λ³Έ cherry-pick
git cherry-pick abc1234

# μ—¬λŸ¬ 컀밋
git cherry-pick abc1234 def5678 ghi9012

# λ²”μœ„ cherry-pick
git cherry-pick abc1234..ghi9012  # abc1234 μ œμ™Έ
git cherry-pick abc1234^..ghi9012  # abc1234 포함

# μ»€λ°‹ν•˜μ§€ μ•Šκ³  λ³€κ²½λ§Œ 적용
git cherry-pick -n abc1234

# 좩돌 ν•΄κ²° ν›„ 계속
git cherry-pick --continue

# 쀑단
git cherry-pick --abort

# Merge 컀밋 cherry-pick (-m μ˜΅μ…˜ ν•„μš”)
git cherry-pick -m 1 abc1234
# -m 1: 첫 번째 λΆ€λͺ¨ κΈ°μ€€ (보톡 main)
# -m 2: 두 번째 λΆ€λͺ¨ κΈ°μ€€ (λ³‘ν•©λœ 브랜치)

4.5 Git Rebase κ³ κΈ‰

# λŒ€ν™”ν˜• rebase
git rebase -i HEAD~5
# pick, reword, edit, squash, fixup, drop

# νŠΉμ • 컀밋뢀터 rebase
git rebase -i abc1234

# Autosquash (fixup! 접두사 μžλ™ 처리)
git commit --fixup abc1234
git rebase -i --autosquash abc1234^

# Rebase 쀑 좩돌
git rebase --continue
git rebase --skip
git rebase --abort

# onto μ˜΅μ…˜ (브랜치 이동)
git rebase --onto main feature-base feature
# feature-base와 feature μ‚¬μ΄μ˜ 컀밋을 main μœ„λ‘œ 이동

# preserve-merges (병합 컀밋 μœ μ§€) - deprecated
git rebase --rebase-merges main

5. Git λ‚΄λΆ€ ꡬ쑰

5.1 Git 객체

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Git 객체 μœ ν˜•                             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                             β”‚
β”‚  Blob (파일 λ‚΄μš©)                                           β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  SHA-1: abc123...                                   β”‚   β”‚
β”‚  β”‚  λ‚΄μš©: (파일의 λ°”μ΄λ„ˆλ¦¬ 데이터)                     β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                             β”‚
β”‚  Tree (디렉토리)                                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  SHA-1: def456...                                   β”‚   β”‚
β”‚  β”‚  100644 blob abc123... README.md                    β”‚   β”‚
β”‚  β”‚  100644 blob bcd234... main.js                      β”‚   β”‚
β”‚  β”‚  040000 tree cde345... src                          β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                             β”‚
β”‚  Commit (컀밋)                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  SHA-1: ghi789...                                   β”‚   β”‚
β”‚  β”‚  tree def456...                                     β”‚   β”‚
β”‚  β”‚  parent efg567...                                   β”‚   β”‚
β”‚  β”‚  author John <john@example.com> 1234567890 +0900   β”‚   β”‚
β”‚  β”‚  committer John <john@example.com> 1234567890 +0900β”‚   β”‚
β”‚  β”‚                                                      β”‚   β”‚
β”‚  β”‚  Commit message                                      β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                             β”‚
β”‚  Tag (νƒœκ·Έ - annotated)                                     β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  SHA-1: jkl012...                                   β”‚   β”‚
β”‚  β”‚  object ghi789... (컀밋)                            β”‚   β”‚
β”‚  β”‚  type commit                                        β”‚   β”‚
β”‚  β”‚  tag v1.0.0                                         β”‚   β”‚
β”‚  β”‚  tagger John <john@example.com> 1234567890 +0900   β”‚   β”‚
β”‚  β”‚                                                      β”‚   β”‚
β”‚  β”‚  Release version 1.0.0                               β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

5.2 μ €μˆ˜μ€€ λͺ…λ Ήμ–΄ (Plumbing)

# 객체 νƒ€μž… 확인
git cat-file -t abc1234
# commit

# 객체 λ‚΄μš© 확인
git cat-file -p abc1234
# tree def456789...
# parent ...
# author ...

# ν˜„μž¬ μ»€λ°‹μ˜ tree 확인
git cat-file -p HEAD^{tree}

# Blob λ‚΄μš© 확인
git cat-file -p abc1234:README.md

# 객체 ν•΄μ‹œ 계산
echo "Hello" | git hash-object --stdin
# λ˜λŠ” 파일둜
git hash-object README.md

# 객체 μ €μž₯
echo "Hello" | git hash-object -w --stdin

# Tree 생성
git write-tree

# 컀밋 생성
echo "Commit message" | git commit-tree <tree-sha> -p <parent-sha>

# 레퍼런슀 μ—…λ°μ΄νŠΈ
git update-ref refs/heads/new-branch abc1234

5.3 Git 디렉토리 ꡬ쑰

.git/
β”œβ”€β”€ HEAD              # ν˜„μž¬ 브랜치 μ°Έμ‘°
β”œβ”€β”€ config            # μ €μž₯μ†Œ μ„€μ •
β”œβ”€β”€ description       # GitWeb μ„€λͺ…
β”œβ”€β”€ hooks/            # Git hooks
β”œβ”€β”€ info/
β”‚   └── exclude       # 둜컬 .gitignore
β”œβ”€β”€ objects/          # λͺ¨λ“  객체 μ €μž₯
β”‚   β”œβ”€β”€ pack/         # μ••μΆ•λœ 객체
β”‚   β”œβ”€β”€ info/
β”‚   └── ab/
β”‚       └── c123...   # 객체 파일 (처음 2μžκ°€ 디렉토리)
β”œβ”€β”€ refs/
β”‚   β”œβ”€β”€ heads/        # 둜컬 브랜치
β”‚   β”‚   └── main
β”‚   β”œβ”€β”€ remotes/      # 원격 브랜치
β”‚   β”‚   └── origin/
β”‚   β”‚       └── main
β”‚   └── tags/         # νƒœκ·Έ
β”‚       └── v1.0.0
β”œβ”€β”€ logs/             # reflog μ €μž₯
β”‚   β”œβ”€β”€ HEAD
β”‚   └── refs/
β”œβ”€β”€ index             # μŠ€ν…Œμ΄μ§• μ˜μ—­
└── COMMIT_EDITMSG    # λ§ˆμ§€λ§‰ 컀밋 λ©”μ‹œμ§€

6. νŠΈλŸ¬λΈ”μŠˆνŒ…

6.1 일반적인 문제 ν•΄κ²°

# λ§ˆμ§€λ§‰ 컀밋 μˆ˜μ • (push μ „)
git commit --amend -m "New message"
git commit --amend --no-edit  # λ©”μ‹œμ§€ μœ μ§€

# Push된 컀밋 μˆ˜μ • (μœ„ν—˜!)
git commit --amend
git push --force-with-lease  # μ•ˆμ „ν•œ force push

# 잘λͺ»λœ λΈŒλžœμΉ˜μ— 컀밋 (push μ „)
git branch correct-branch    # ν˜„μž¬ μ»€λ°‹μœΌλ‘œ μƒˆ 브랜치
git reset --hard HEAD~1      # ν˜„μž¬ 브랜치 되돌리기
git checkout correct-branch  # μ˜¬λ°”λ₯Έ 브랜치둜 이동

# μ»€λ°‹μ—μ„œ 파일 제거
git reset HEAD~ -- file.txt
git commit --amend

# 민감 정보 제거 (λͺ¨λ“  νžˆμŠ€ν† λ¦¬μ—μ„œ)
git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch secrets.txt" \
  --prune-empty --tag-name-filter cat -- --all

# λ˜λŠ” BFG Repo-Cleaner μ‚¬μš© (더 빠름)
bfg --delete-files secrets.txt
bfg --replace-text passwords.txt

6.2 좩돌 ν•΄κ²°

# Merge 좩돌 확인
git status
git diff --name-only --diff-filter=U

# 좩돌 마컀
# <<<<<<< HEAD
# ν˜„μž¬ 브랜치 λ‚΄μš©
# =======
# λ³‘ν•©ν•˜λ €λŠ” 브랜치 λ‚΄μš©
# >>>>>>> feature

# νŒŒμΌλ³„λ‘œ 선택
git checkout --ours file.txt    # ν˜„μž¬ 브랜치 선택
git checkout --theirs file.txt  # 병합 브랜치 선택

# Merge 도ꡬ μ‚¬μš©
git mergetool

# 좩돌 ν•΄κ²° ν›„
git add file.txt
git commit

# Merge 쀑단
git merge --abort

6.3 λŒ€μš©λŸ‰ μ €μž₯μ†Œ 관리

# 큰 파일 찾기
git rev-list --objects --all | \
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
  sed -n 's/^blob //p' | \
  sort -nk2 | \
  tail -20

# Git LFS μ„€μ •
git lfs install
git lfs track "*.psd"
git lfs track "*.zip"
git add .gitattributes
git add large-file.psd
git commit -m "Add large file with LFS"

# μ €μž₯μ†Œ 크기 쀄이기
git gc --aggressive --prune=now
git repack -a -d --depth=250 --window=250

# Shallow clone
git clone --depth 1 https://github.com/repo.git

# Sparse checkout
git sparse-checkout init
git sparse-checkout set src/ tests/

7. μ—°μŠ΅ 문제

μ—°μŠ΅ 1: Git Hooks μ„€μ •

# μš”κ΅¬μ‚¬ν•­:
# 1. pre-commit: μ½”λ“œ ν¬λ§·νŒ… 검사
# 2. commit-msg: Conventional Commits 검증
# 3. pre-push: ν…ŒμŠ€νŠΈ μ‹€ν–‰
# 4. Husky둜 νŒ€κ³Ό 곡유 κ°€λŠ₯ν•˜κ²Œ μ„€μ •

# Hook 슀크립트 μž‘μ„±:

μ—°μŠ΅ 2: Submodule ν”„λ‘œμ νŠΈ

# μš”κ΅¬μ‚¬ν•­:
# 1. 메인 ν”„λ‘œμ νŠΈ 생성
# 2. 곡유 라이브러리λ₯Ό submodule둜 μΆ”κ°€
# 3. Submodule μ—…λ°μ΄νŠΈ 슀크립트 μž‘μ„±
# 4. CIμ—μ„œ submodule 포함 λΉŒλ“œ

# λͺ…λ Ήμ–΄ 및 슀크립트 μž‘μ„±:

μ—°μŠ΅ 3: Worktree ν™œμš©

# μš”κ΅¬μ‚¬ν•­:
# 1. 메인 μž‘μ—… 쀑 κΈ΄κΈ‰ 버그 μˆ˜μ • μ‹œλ‚˜λ¦¬μ˜€
# 2. Worktree둜 병렬 μž‘μ—…
# 3. μž‘μ—… μ™„λ£Œ ν›„ 정리

# λͺ…λ Ήμ–΄ μž‘μ„±:

μ—°μŠ΅ 4: Bisect둜 버그 μ°ΎκΈ°

# μš”κ΅¬μ‚¬ν•­:
# 1. ν…ŒμŠ€νŠΈ 슀크립트 μž‘μ„±
# 2. git bisect run으둜 μžλ™ν™”
# 3. 버그 컀밋 μ°ΎκΈ°

# λͺ…λ Ήμ–΄ μž‘μ„±:

λ‹€μŒ 단계

참고 자료


← 이전: Git μ›Œν¬ν”Œλ‘œμš° μ „λž΅ | λ‹€μŒ: λͺ¨λ…Έλ ˆν¬ 관리 β†’ | λͺ©μ°¨

to navigate between lessons