← Git 강의 목록으로
🔀
Git·GitHub 심화
add · commit · log · diff · .gitignore · alias

2단원 — 일상 작업

Git을 매일 사용하려면 `add`, `commit`, `log`, `diff` 네 명령을 손에 익혀야 합니다. 여기에 `.gitignore`로 추적에서 제외할 파일을 관리하고, 반복 명령을 `alias`로 단축하며, `HEAD`가 무엇을 가리키는지 이해하면 하루 종일 터미널을 떠나지 않아도 편안하게 작업할 수 있습니다.

addcommitlog.gitignore
소요 시간
1~2시간
난이도
📊 초급
선수 조건
🎯 1단원
결과물
매일 쓰는 add·commit·log·diff 루틴

이 강의에서 배우는 것

  • 1`git add`의 다양한 옵션(`-p`, `-u`, `.`)을 상황에 맞게 사용할 수 있다.
  • 2`git log`의 형식 옵션(`--oneline`, `--graph`, `--since`)을 활용할 수 있다.
  • 3`git diff`로 워킹 디렉토리·스테이징·커밋 간 차이를 비교할 수 있다.
  • 4`.gitignore` 파일을 작성하고 이미 추적 중인 파일을 제외할 수 있다.
  • 5자주 쓰는 명령을 `git alias`로 등록하고 사용할 수 있다.
  • 6`HEAD`, `HEAD~1`, `HEAD^` 표기법을 이해하고 활용할 수 있다.

핵심 개념

1) git add 옵션

bash
git add .           # 현재 디렉토리 이하 모든 변경
git add -u          # 추적 중인 파일의 수정·삭제만 (새 파일 제외)
git add -p          # 변경 덩어리(hunk)를 하나씩 선택해서 스테이징
git add src/        # 특정 디렉토리만

2) git commit 옵션

bash
git commit -m "메시지"         # 한 줄 메시지로 즉시 커밋
git commit                     # 에디터 열어 메시지 작성
git commit --amend             # 마지막 커밋 메시지 수정 (push 전에만)
git commit -am "메시지"        # add -u + commit 한 번에 (새 파일 제외)

3) git log 옵션

bash
git log --oneline              # 한 줄 요약
git log --oneline --graph      # 브랜치 그래프
git log --since="2 weeks ago"  # 기간 필터
git log -n 5                   # 최근 5개만
git log --author="홍길동"      # 작성자 필터
git log -- src/main.py         # 특정 파일 이력

4) git diff

bash
git diff                       # 워킹 디렉토리 vs 스테이징
git diff --staged              # 스테이징 vs 최신 커밋
git diff HEAD~1 HEAD           # 두 커밋 간 비교
git diff main feature          # 두 브랜치 비교

5) .gitignore

text
# 빌드 산출물
*.o
*.class
build/

# 환경 설정
.env
.env.local

# OS 메타파일
.DS_Store
Thumbs.db

# IDE
.idea/
.vscode/

이미 추적 중인 파일을 나중에 무시하려면:

bash
git rm --cached 파일명
echo "파일명" >> .gitignore
git commit -m "chore: ignore 파일명"

6) git alias

bash
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.lg "log --oneline --graph --all"
git config --global alias.aa "add --all"

이후 `git lg` 처럼 단축어로 사용.

7) HEAD 이해

text
HEAD → main → (최신 커밋 해시)
표기의미
`HEAD`현재 체크아웃된 커밋
`HEAD~1`한 단계 이전
`HEAD~2`두 단계 이전
`HEAD^`첫 번째 부모 (`~1`과 동일)

예제로 보기

예제 1 — `ex01_add_commit.sh` : add -p 로 부분 스테이징

bash
#!/usr/bin/env bash
REPO=$(mktemp -d); cd "$REPO"
git init -q
git config user.name "실습용" && git config user.email "demo@example.com"

echo -e "line1\nline2\nline3" > file.txt
git add file.txt && git commit -q -m "init"

# 여러 영역을 동시에 수정
echo "line4" >> file.txt          # 워킹 디렉토리 변경
git add file.txt                   # 스테이징

echo "line5" >> file.txt          # 스테이징 후 추가 변경

echo "=== diff (워킹 vs 스테이징) ==="
git diff

echo ""
echo "=== diff --staged (스테이징 vs HEAD) ==="
git diff --staged

rm -rf "$REPO"

**실행 결과**

text
=== diff (워킹 vs 스테이징) ===
+line5

=== diff --staged (스테이징 vs HEAD) ===
+line4

핵심: `git diff`와 `git diff --staged`는 서로 다른 비교 대상을 가진다.

예제 2 — `ex02_log_diff.sh` : 다양한 log 옵션 비교

bash
#!/usr/bin/env bash
REPO=$(mktemp -d); cd "$REPO"
git init -q
git config user.name "실습용" && git config user.email "demo@example.com"

for i in 1 2 3; do
    echo "v$i" > version.txt
    git add version.txt
    git commit -q -m "chore: version $i"
done

echo "=== --oneline ==="
git log --oneline

echo ""
echo "=== --oneline --stat ==="
git log --oneline --stat

echo ""
echo "=== -n 2 ==="
git log --oneline -n 2

rm -rf "$REPO"

**실행 결과**

text
=== --oneline ===
c3d4e5f chore: version 3
b2c3d4e chore: version 2
a1b2c3d chore: version 1

=== --oneline --stat ===
c3d4e5f chore: version 3
 version.txt | 2 +-
...

핵심: `git log`는 옵션 조합으로 원하는 수준의 정보를 선택적으로 볼 수 있다.

예제 3 — `ex03_gitignore.sh` : .gitignore 작성 및 캐시 제거

bash
#!/usr/bin/env bash
REPO=$(mktemp -d); cd "$REPO"
git init -q
git config user.name "실습용" && git config user.email "demo@example.com"

echo "secret" > .env
echo "code" > main.py
git add . && git commit -q -m "init: add files"

# 이제 .env 를 무시하고 싶을 때
echo ".env" > .gitignore
git rm --cached .env
git add .gitignore
git commit -m "chore: ignore .env"

echo "=== 추적 파일 목록 ==="
git ls-files

echo ""
echo "=== status ==="
git status

rm -rf "$REPO"

**실행 결과**

text
=== 추적 파일 목록 ===
.gitignore
main.py

=== status ===
On branch main
nothing to commit, working tree clean

핵심: `git rm --cached`는 파일을 디스크에서 삭제하지 않고 추적에서만 제외한다.

예제 4 — `ex04_alias.sh` : alias 등록 및 사용

bash
#!/usr/bin/env bash
REPO=$(mktemp -d); cd "$REPO"
git init -q
git config user.name "실습용" && git config user.email "demo@example.com"

# 로컬 저장소에만 alias 등록 (전역 설정 오염 방지)
git config alias.lg "log --oneline --graph --all"
git config alias.st "status --short"

echo "첫 파일" > a.txt
git add a.txt && git commit -q -m "first"

echo "=== git st (alias) ==="
git st

echo ""
echo "=== git lg (alias) ==="
git lg

rm -rf "$REPO"

**실행 결과**

text
=== git st (alias) ===
   (nothing)

=== git lg (alias) ===
* a1b2c3d first

핵심: `git config alias.<이름>`으로 복잡한 명령을 단축어로 만들 수 있다.

다른 시각으로 보기

Git 명령일상 비유
`git add -p`장바구니에 상품 하나씩 담기
`git commit`결제 완료
`git log`영수증 목록 보기
`git diff`주문 전후 가격 비교
`.gitignore`쇼핑 금지 목록

자주 하는 실수

  1. **`git add .` 으로 불필요한 파일까지 추가** — `.gitignore`를 먼저 설정하거나 `git add -p`를 사용한다.
  2. **커밋 메시지 없이 `-m ""`** — 빈 메시지로 커밋하면 히스토리 파악이 어렵다.
  3. **`--amend`로 push된 커밋 수정** — 강제 push가 필요해 팀원의 이력이 꼬인다.
  4. **`.env` 파일을 실수로 커밋** — 민감 정보가 공개 저장소에 노출된다; 즉시 `git rm --cached`.
  5. **`HEAD~1`과 `HEAD^1` 혼동** — 병합 커밋에서는 `^2`가 두 번째 부모를 가리킨다.

정리

  • `git add -p`로 변경 덩어리를 선택해 커밋을 작게 유지한다.
  • `git diff`는 워킹·스테이징 비교, `git diff --staged`는 스테이징·커밋 비교다.
  • `.gitignore`에 패턴을 추가하고, 이미 추적된 파일은 `git rm --cached`로 제거한다.
  • `alias`로 자주 쓰는 명령을 단축해 생산성을 높인다.
  • `HEAD~N`으로 N단계 이전 커밋을 참조한다.

직접 해 보기

bash
cd 02_일상_작업/src
chmod +x *.sh
./ex01_add_commit.sh
./ex02_log_diff.sh
./ex03_gitignore.sh
./ex04_alias.sh

응용:

  1. `git log --since="1 week ago" --author="$(git config user.name)"` 로 내 최근 커밋을 필터링해 보세요.
  2. `git config --global alias.undo "reset HEAD~1 --mixed"` 로 마지막 커밋을 되돌리는 alias를 만들어 보세요.

💻 예제 (examples)

실제로 실행해 결과를 확인할 수 있는 예제입니다.

ex01_add_commit.shadd -p 로 부분 스테이징
CODE
#!/usr/bin/env bash
set -euo pipefail

REPO=$(mktemp -d)
cd "$REPO"
git init -q -b main
git config user.name "실습용" && git config user.email "demo@example.com"
    git config commit.gpgsign false

printf 'line1\nline2\nline3\n' > file.txt
git add file.txt && git commit -q -m "init"

echo "line4" >> file.txt
git add file.txt

echo "line5" >> file.txt

echo "=== diff: 워킹 vs 스테이징 ==="
git diff

echo ""
echo "=== diff --staged: 스테이징 vs HEAD ==="
git diff --staged

git commit -q -m "add line4 (staged only)"
echo ""
echo "=== 커밋 후 남은 변경 ==="
git diff

rm -rf "$REPO"
▶ 실행 결과
=== diff (워킹 vs 스테이징) ===
+line5

=== diff --staged (스테이징 vs HEAD) ===
+line4
ex02_log_diff.sh다양한 log 옵션 비교
CODE
#!/usr/bin/env bash
set -euo pipefail

REPO=$(mktemp -d)
cd "$REPO"
git init -q -b main
git config user.name "실습용" && git config user.email "demo@example.com"
    git config commit.gpgsign false

for i in 1 2 3; do
    echo "v$i" > version.txt
    git add version.txt
    git commit -q -m "chore: version $i"
done

echo "=== git log --oneline ==="
git log --oneline

echo ""
echo "=== git log --oneline -n 2 ==="
git log --oneline -n 2

echo ""
echo "=== git log --oneline --stat ==="
git log --oneline --stat

echo ""
echo "=== git diff HEAD~2 HEAD (두 커밋 전 vs 현재) ==="
git diff HEAD~2 HEAD

rm -rf "$REPO"
▶ 실행 결과
=== --oneline ===
c3d4e5f chore: version 3
b2c3d4e chore: version 2
a1b2c3d chore: version 1

=== --oneline --stat ===
c3d4e5f chore: version 3
 version.txt | 2 +-
...
ex03_gitignore.sh.gitignore 작성 및 캐시 제거
CODE
#!/usr/bin/env bash
set -euo pipefail

REPO=$(mktemp -d)
cd "$REPO"
git init -q -b main
git config user.name "실습용" && git config user.email "demo@example.com"
    git config commit.gpgsign false

echo "secret_password" > .env
echo "print('hello')" > main.py
git add .
git commit -q -m "init: add files (including .env by mistake)"

echo "=== 실수로 커밋된 파일 목록 ==="
git ls-files

# 이제 .env 를 추적에서 제외
echo ".env" > .gitignore
git rm --cached .env
git add .gitignore
git commit -m "chore: stop tracking .env"

echo ""
echo "=== 수정 후 추적 파일 목록 ==="
git ls-files

echo ""
echo "=== .env 파일은 디스크에 그대로 존재 ==="
ls -la .env

rm -rf "$REPO"
▶ 실행 결과
=== 추적 파일 목록 ===
.gitignore
main.py

=== status ===
On branch main
nothing to commit, working tree clean
ex04_alias.shalias 등록 및 사용
CODE
#!/usr/bin/env bash
set -euo pipefail

REPO=$(mktemp -d)
cd "$REPO"
git init -q -b main
git config user.name "실습용" && git config user.email "demo@example.com"
    git config commit.gpgsign false

# 로컬 저장소 alias 등록
git config alias.lg "log --oneline --graph --all --decorate"
git config alias.st "status --short --branch"
git config alias.aa "add --all"
git config alias.cm "commit -m"

for i in 1 2; do
    echo "file$i" > "file$i.txt"
    git aa
    git cm "chore: add file$i"
done

echo "=== git st (status --short --branch) ==="
git st

echo ""
echo "=== git lg (log --oneline --graph --all) ==="
git lg

echo ""
echo "=== 등록된 alias 목록 ==="
git config --list | grep "^alias\."

rm -rf "$REPO"
▶ 실행 결과
=== git st (alias) ===
   (nothing)

=== git lg (alias) ===
* a1b2c3d first

📝 과제 (exercises)

직접 풀어보고, 막힐 때 정답을 펼쳐 비교해보세요.

과제 1

문제 1 (hw01.sh)

목표: 아래 시나리오를 재현하는 스크립트를 작성하세요.

요구사항
  • 파일명: hw01.sh
입출력 예시
# 예상 출력 (핵심 부분)
=== diff (워킹 vs 스테이징) ===
+할 일 5

=== diff --staged (스테이징 vs HEAD) ===
+할 일 4
정답 코드 펼치기 / 접기
SOLUTION
#!/usr/bin/env bash
set -euo pipefail

REPO=$(mktemp -d)
cd "$REPO"
git init -q -b main
git config user.name "실습용" && git config user.email "demo@example.com"
    git config commit.gpgsign false

printf '할 일 1\n할 일 2\n할 일 3\n' > notes.txt
git add notes.txt && git commit -q -m "init: add notes"

echo "할 일 4" >> notes.txt
git add notes.txt

echo "할 일 5" >> notes.txt

echo "=== diff: 워킹 vs 스테이징 ==="
git diff

echo ""
echo "=== diff --staged: 스테이징 vs HEAD ==="
git diff --staged

rm -rf "$REPO"
▶ 실행 결과
# 예상 출력 (핵심 부분)
=== diff (워킹 vs 스테이징) ===
+할 일 5

=== diff --staged (스테이징 vs HEAD) ===
+할 일 4
과제 2

문제 2 (hw02.sh)

목표: 다음 조건을 모두 만족하는 스크립트를 작성하세요.

요구사항
  • 파일명: hw02.sh
정답 코드 펼치기 / 접기
SOLUTION
#!/usr/bin/env bash
set -euo pipefail

REPO=$(mktemp -d)
cd "$REPO"
git init -q -b main
git config user.name "실습용" && git config user.email "demo@example.com"
    git config commit.gpgsign false

for i in 1 2 3 4 5; do
    echo "commit $i" > "file$i.txt"
    git add "file$i.txt"
    git commit -q -m "chore: add file$i"
done

echo "*.log" > .gitignore
git add .gitignore
git commit -q -m "chore: ignore *.log files"

echo "some debug info" > debug.log

echo "=== git status (.log 파일이 보이지 않아야 함) ==="
git status

echo ""
echo "=== git log --oneline (전체) ==="
git log --oneline

echo ""
echo "=== git log --oneline -n 3 (최근 3개) ==="
git log --oneline -n 3

rm -rf "$REPO"
예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗