> ## Documentation Index
> Fetch the complete documentation index at: https://blog.nvim.me/llms.txt
> Use this file to discover all available pages before exploring further.

# 커밋 이력과 변경 내용은 어떻게 읽을까요?

> index.js의 코드가 바뀐 기록을 log로 찾고, show와 두 커밋 사이의 diff로 확인해요. 한 커밋의 변경과 두 시점의 차이를 구분해봐요.

`index.js`를 열어보니 `console.log("hello world");`가 있어요. 처음에는 `"hello"`였는데 어느 커밋에서 바뀌었을까요? 중간에 다른 문자열도 있었을까요?

[작업 트리·스테이징 영역·커밋 실습](https://blog.nvim.me/git/learn/working-tree-index-and-commit.md)에서는 같은 줄을 두 번 바꿔 기록했어요. 이번에는 **이미 남긴 기록을 찾아 실제로 바뀐 줄과 당시 파일을 확인**해봐요.

## 목록에서 찾고, 한 커밋을 읽고, 두 시점을 비교해요

| Git 명령 | 답하는 질문 |
| - | - |
| `git log` | 어떤 커밋이 남아 있을까요? |
| `git show` | 이 커밋에는 어떤 메시지와 변경이 있을까요? |
| `git diff 이전커밋 이후커밋` | 두 시점의 파일 내용은 어떻게 다를까요? |

커밋은 파일의 스냅샷과 메시지, 작성자, 부모 커밋 등의 정보를 연결해요. Git은 스냅샷을 비교해서 차이를 보여줘요. 이 글은 병합 없이 일렬로 이어진 세 커밋을 기준으로 해요.

## 시작 상태를 맞춰요

명령은 **Bash** 기준이에요. 코드 실행이나 Node.js 설치는 필요하지 않아요. 앞편을 끝낸 연습 저장소에서 이어가요. 준비를 마친 뒤에는 조회 명령만 실행하므로 브랜치, 작업 파일, 인덱스, 커밋 이력은 바뀌지 않아요.

| 항목 | 시작할 때와 끝낼 때의 상태 |
| - | - |
| 브랜치 | `main` |
| 이력 | 초기 기록 → hello git 변경 → hello world 변경, 총 세 커밋 |
| `index.js` | `console.log("hello world");` 한 줄 |
| `README.md` | `# Git practice` 한 줄 |
| 미커밋 변경과 원격 | 둘 다 없음 |

다른 작업이 섞여 있다면 되돌리지 말고 새 연습 저장소를 준비해요.

> [accordion] 이 글부터 시작한다면: 같은 세 커밋 준비하기
>
> Git 설치는 [첫 커밋 실습](https://blog.nvim.me/git/learn/setup-and-first-commit.md)을 참고해요. `git init -b main`에는 Git 2.28 이상이 필요해요. 별도 hook이나 커밋 서명 설정이 없는 연습 환경을 기준으로 해요.
>
> 기존 Git 저장소 밖의 폴더에서 실행해요. 같은 이름의 폴더가 있어 `mkdir`가 실패하면 멈추고 새 이름을 골라요.
>
> ```bash theme={null}
> mkdir git-practice-history && cd git-practice-history && git init -b main
> ```
>
> 아래 `>`는 파일을 덮어써요. 방금 만든 빈 연습 폴더에서만 실행해요. 작성자 설정은 이 저장소에만 적용하며, 원격 저장소는 연결하지 않아요.
>
> ```bash theme={null}
> git config --local user.name "Git Learner"
> git config --local user.email "learner@example.com"
> printf 'console.log("hello");\n' > index.js
> printf '# Git practice\n' > README.md
> git add index.js README.md
> git commit -m "Add initial script and README"
>
> printf 'console.log("hello git");\n' > index.js
> git add index.js
> git commit -m "Change greeting to hello git"
>
> printf 'console.log("hello world");\n' > index.js
> git add index.js
> git commit -m "Change greeting to hello world"
> ```
>
> 앞편에서 관찰한 스테이징 중간 상태를 반복하지 않고, 이번 조회에 필요한 세 스냅샷과 메시지를 준비한 거예요. 작성 시각 등이 다르므로 앞편과 커밋 ID까지 같을 필요는 없어요.

같은 터미널에서 연습 저장소의 상태를 확인해요.

```bash theme={null}
git status
git log --oneline
cat index.js README.md
git remote -v
```

`status`에는 `main`과 `working tree clean`이 보여야 해요. `log`에는 표에 적힌 세 커밋이 있어야 해요. 파일 내용은 차례로 `console.log("hello world");`, `# Git practice`이고, 원격 출력은 없어야 해요. 다르면 준비 상태부터 맞춰요.

출력이 별도 보기 화면으로 열리면 `q`로 나올 수 있어요. 아래 명령은 모두 저장소 루트에서 실행해요.

## log로 후보를 찾고 show로 확인해요

**1. 기록 목록에서 코드 변경 찾기**

먼저 최근 기록을 짧게 보고, index.js와 관련된 기록으로 범위를 좁혀요.

```bash theme={null}
git log --oneline -3
git log --oneline -- index.js
git log --oneline --grep='hello git'
```

첫 명령에서는 위에서부터 `Change greeting to hello world`, `Change greeting to hello git`, `Add initial script and README`가 보여요. 각 메시지 앞에는 실제 커밋의 축약 해시가 붙어요. `-3`은 최대 세 커밋만 보여달라는 뜻이에요.

`-- index.js`는 파일 경로로 이력을 좁혀요. 이 예제에서는 세 커밋 모두 index.js를 변경했으므로 목록이 같아요. `--`는 옵션·커밋 이름과 파일 경로를 구분해요.

`--grep='hello git'`은 **커밋 메시지**를 검색해요. 파일 안의 `hello git`을 찾는 명령은 아니에요. 여기서는 `Change greeting to hello git` 하나가 나와요. 목록과 검색 옵션은 [git log 공식 문서](https://git-scm.com/docs/git-log)를 참고해요.

**2. 찾은 커밋의 메시지와 변경 읽기**

이번 이력에서 `HEAD`는 마지막 hello world 커밋을 가리켜요. `HEAD~1`은 그 부모인 hello git 커밋, `HEAD~2`는 부모를 두 번 따라간 초기 커밋이에요.

```bash theme={null}
git show HEAD~1 -- index.js
```

출력에서 커밋 ID, 작성자, 시각, 메시지와 코드 변경을 읽어봐요. 이 예제의 일반 커밋은 **부모 커밋과 비교한 차이**를 보여줘요. 변경 부분은 다음과 같아요. 파일명·해시를 나타내는 앞부분은 생략했어요.

```diff theme={null}
--- a/index.js
+++ b/index.js
@@ -1 +1 @@
-console.log("hello");
+console.log("hello git");
```

| 표시 | 읽는 방법 |
| - | - |
| `--- a/index.js`, `+++ b/index.js` | 비교하는 이전 쪽과 이후 쪽 파일이에요. `a`, `b`는 이 출력의 접두사예요 |
| `@@ -1 +1 @@` | 이전 파일과 이후 파일 모두 1번 줄 부근의 변경이에요. 줄 수를 생략하면 한 줄이에요 |
| `-console.log("hello");` | 이전 쪽에 있던 줄이에요 |
| `+console.log("hello git");` | 이후 쪽에 있는 줄이에요 |

앞의 `-`, `+`는 파일에 저장된 문자가 아니에요. 더 긴 파일에서는 공백으로 시작하는, 바뀌지 않은 문맥 줄도 나올 수 있어요. `show`와 패치 형식은 [git show 공식 문서](https://git-scm.com/docs/git-show)에 설명되어 있어요.

`HEAD~1` 대신 방금 `log`에서 얻은 커밋 해시를 사용해도 돼요. `HEAD~1`은 현재 위치가 바뀌면 다른 커밋을 뜻할 수 있으므로, 특정 기록을 전달할 때는 해시를 남겨요. `~`는 첫 번째 부모를 따라가는 표기이며 날짜순으로 몇 번째 기록이라는 뜻은 아니에요. [커밋을 지정하는 공식 규칙](https://git-scm.com/docs/gitrevisions)을 참고해요.

**3. 그때 파일 전체와 기록 시각 확인하기**

바뀐 줄을 보면 문자열이 어떻게 달라졌는지 알 수 있어요. 당시 파일 전체가 필요하면 커밋 이름 뒤에 `:파일경로`를 붙여요.

```bash theme={null}
git show HEAD~1:index.js
cat index.js
git show --no-patch --format=fuller HEAD~1
```

첫 출력은 `console.log("hello git");`, 두 번째는 `console.log("hello world");`이에요. 과거 파일을 **읽었을 뿐**, 현재 작업 파일이 과거 내용으로 바뀌지는 않았어요.

마지막 명령은 패치를 생략하고 커밋 정보를 자세히 보여줘요. `AuthorDate`는 작성 시각, `CommitDate`는 커밋을 기록한 시각이에요. 이 실습에서는 대개 같지만, 변경을 다른 이력에 옮기는 등의 작업에서는 달라질 수 있어요. 커밋 시각을 서비스 배포 시각으로 읽지는 않아요.

## 두 커밋의 diff에는 중간 과정이 나올까요?

이번에는 처음 코드와 마지막 코드를 비교해봐요. 실행 전에 중간 문자열인 `"hello git"`도 출력에 나올지 예상해보세요.

```bash theme={null}
git diff HEAD~2 HEAD -- index.js
```

변경 줄만 발췌하면 아래와 같아요.

```diff theme={null}
-console.log("hello");
+console.log("hello world");
```

**두 커밋의 `diff`는 양 끝의 스냅샷을 비교해요.** 중간에 `"hello git"`이 있었던 과정은 이 결과에 나오지 않아요. 그 과정을 보려면 앞에서처럼 커밋별로 `show`를 읽거나, `log -p`로 각 커밋의 패치를 함께 봐요.

```bash theme={null}
git log -p -2 -- index.js
git diff HEAD HEAD~2 -- index.js
git diff HEAD~2 HEAD -- README.md
```

첫 명령에서는 최근 것부터 `"hello git"` → `"hello world"`, `"hello"` → `"hello git"` 변경을 읽을 수 있어요. 두 번째는 비교 순서를 뒤집었으므로 `-console.log("hello world");`, `+console.log("hello");`이 나와요. 명령이 코드를 되돌린 것은 아니에요. 마지막 명령은 두 시점의 README 내용이 같아서 출력이 없어요.

[git diff 공식 문서](https://git-scm.com/docs/git-diff)의 두 커밋 비교가 이 동작의 기준이에요. `git diff HEAD~2..HEAD`도 이 경우 같은 두 끝을 비교해요. 반면 `git log HEAD~2..HEAD`는 `HEAD~2`에서 도달할 수 있는 커밋을 제외한 이력을 골라요. 이번 직선 이력에서는 코드 변경 두 커밋만 나와요. 같은 `..` 표기를 보고 두 명령을 같은 의미로 읽지 않아요.

## diff가 비어 있어도 질문은 다를 수 있어요

[앞편](https://blog.nvim.me/git/learn/working-tree-index-and-commit.md)에서 본 미커밋 변경 비교와 이번 이력 비교를 나란히 놓아봐요.

| 명령 | 비교하는 두 상태 | 이번 실습의 결과 |
| - | - | - |
| `git diff` | 인덱스 → 작업 트리 | 차이 없음 |
| `git diff --cached` | `HEAD` → 인덱스 | 차이 없음 |
| `git diff HEAD~2 HEAD` | 초기 커밋 → 마지막 커밋 | `"hello"` → `"hello world"` |

앞의 두 결과가 비어 있어도 과거 코드 변경이 없었던 것은 아니에요. **현재 남은 변경이 있는지**와 **두 과거 기록이 다른지**는 서로 다른 질문이에요. 또 두 끝의 파일이 같더라도, 중간에 고쳤다가 원래 내용으로 돌아온 기록은 있을 수 있어요.

## 코드를 왜 바꿨는지도 알 수 있을까요?

지금까지 확인한 사실은 “문자열을 hello git으로 바꾼 뒤 hello world로 바꿨다”예요. 메시지와 패치가 같은 변경을 가리키는지도 확인했어요.

하지만 `Change greeting to hello git`에는 **문자열을 바꾼 이유**가 적혀 있지 않아요. 요구사항이 달라졌는지, 오타를 고친 것인지 Git이 알아서 기록하지는 않아요. 앞편에서 두 번 커밋한 이유는 세 상태를 관찰하기 위해서였어요.

실무에서는 패치로 무엇이 바뀌었는지 확인하고, 커밋 메시지 본문이나 연결된 이슈·리뷰에서 의도를 찾아요. 근거가 없다면 이유를 추측해서 확정하지 않아요. 앞으로 기록을 남길 때 변경 이유와 선택의 제약을 함께 적으면 다음 사람이 같은 질문을 덜 하게 돼요.

## 예상과 다른 결과가 나왔다면

| 상황 | 확인과 대응 |
| - | - |
| `HEAD~2`를 찾을 수 없다고 해요 | `git log --oneline`으로 세 커밋이 있는지 확인해요. 이번 연습에서는 준비 명령이 끝났는지 살펴봐요. 실제 저장소에서는 이력이 짧거나 얕은 복제일 수도 있어요 |
| 경로를 붙인 `log`나 `diff`가 비어 있어요 | 저장소 루트와 파일명부터 확인해요. 해당 경로에 기록이나 차이가 없을 수도 있어요. 파일 이름 변경을 넘나드는 검색은 이 실습 범위에 포함하지 않아요 |
| `show`에서 그 파일을 찾을 수 없다고 해요 | 선택한 커밋과 파일 경로를 확인해요. 그 시점에는 파일이 없었을 수도 있어요 |
| 파일 내용이나 커밋 개수가 달라요 | 다른 실습을 이어 했는지 확인해요. 기존 작업을 지우지 말고 별도 폴더에 준비 절차를 다시 실행해요 |
| 준비 중 작성자·서명·hook 오류가 났어요 | [첫 커밋 실습의 오류 대응](https://blog.nvim.me/git/learn/setup-and-first-commit.md)을 참고해요. 실패한 커밋을 완료한 뒤 다음 단계로 진행해요 |

## 기록을 읽은 뒤 상태를 확인해요

```bash theme={null}
git status
git diff
git diff --cached
git log --oneline
git remote -v
cat index.js README.md
```

* `main`에는 처음과 같은 세 커밋이 있고 `working tree clean`이에요. 두 미커밋 diff는 비어 있어요.

* `show HEAD~1`에서 `"hello"` → `"hello git"` 변경을 찾았어요.

* `show HEAD~1:index.js`는 과거 파일을 읽었고, 현재 파일은 여전히 `console.log("hello world");`예요.

* 초기·마지막 커밋의 `diff`에는 `"hello"` → `"hello world"`만 나와요. 중간 과정은 커밋별 패치에서 확인했어요.

* README는 `# Git practice`이고 원격 저장소는 없어요.

어떤 기록인지 찾을 때는 `log`, 고른 기록의 메시지와 변경을 읽을 때는 `show`, 두 시점의 차이를 볼 때는 `diff`를 사용해요. 다음에 변경의 근거를 전달할 때는 커밋 해시와 확인한 파일·패치를 함께 남겨보세요. 해시 조회 옵션은 [현재 커밋 해시 확인하기](https://blog.nvim.me/git/how-to/check-current-commit-hash.md)에서 찾아볼 수 있어요.

기록을 읽었다면 이제 어떤 파일을 기록에서 제외할지도 살펴봐요. [.gitignore와 파일 추적 실습](https://blog.nvim.me/git/learn/tracked-files-and-gitignore.md)에서 임시 로그와 이미 기록한 메모를 비교해요.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.