강의/Git & Github 실무 활용 완벽 가이드

Git 변경 사항 복구 완벽 정리: Checkout, Reset, Revert, Restore

studylida 2025. 2. 11. 20:04

이전 커밋 확인하기: Git의 시간 여행

Git에서는 과거의 특정 상태를 확인하고 싶을 때, 해당 커밋을 직접 체크아웃할 수 있습니다. 이를 위해 git log --oneline 명령어를 사용하여 원하는 커밋의 해시를 찾고, git checkout <커밋 해시>를 실행하면 됩니다.

과거 커밋 체크아웃하기

다음과 같은 브랜치 구조가 있다고 가정해봅시다.

*  (HEAD -> main)  c5d3a1d  최신 커밋
*                  a8f1b2c  새로운 기능 추가
*                  4f1b3e9  버그 수정
*                  7a3c2d1  개츠비 → 캐츠비 이름 변경
*                  3d2e1f7  1장과 2장으로 분리
*                  9c8b7a6  소설 개요 작성 (최초 커밋)

이제 과거 상태를 확인하기 위해 7a3c2d1 커밋을 체크아웃해봅시다.

git checkout 7a3c2d1

그러면 현재 HEAD는 해당 커밋을 직접 가리키게 되며, "분리된 HEAD(detached HEAD)" 상태가 됩니다.

*                  c5d3a1d  최신 커밋
*                  a8f1b2c  새로운 기능 추가
*                  4f1b3e9  버그 수정
*  (HEAD)          7a3c2d1  개츠비 → 캐츠비 이름 변경
*                  3d2e1f7  1장과 2장으로 분리
*                  9c8b7a6  소설 개요 작성 (최초 커밋)

이 상태에서는 과거 코드의 상태를 확인하고, 임시로 변경을 가할 수도 있습니다. 하지만 새로 커밋을 하면 기존 브랜치(main)과 연결되지 않으므로 주의해야 합니다.

기존 브랜치로 돌아가기

분리된 HEAD 상태에서 원래 브랜치로 돌아가고 싶다면, 다음을 실행하면 됩니다.

git switch main

또는 기존 checkout 명령어를 사용할 수도 있습니다.

git checkout main

그러면 다시 원래 브랜치로 돌아오게 됩니다.

*  (HEAD -> main)  c5d3a1d  최신 커밋
*                  a8f1b2c  새로운 기능 추가
*                  4f1b3e9  버그 수정
*                  7a3c2d1  개츠비 → 캐츠비 이름 변경
*                  3d2e1f7  1장과 2장으로 분리
*                  9c8b7a6  소설 개요 작성 (최초 커밋)

분리된 HEAD에서 다시 연결하기

분리된 HEAD 상태에서 우리는 과거의 특정 커밋을 탐색하고, 변경 사항을 확인할 수 있습니다. 그러나 이 상태에서는 새로운 커밋을 하더라도 특정 브랜치에 연결되지 않기 때문에 주의가 필요합니다.

이제 분리된 HEAD 상태에서 다시 브랜치에 연결하는 방법을 살펴보겠습니다.

1. 기존 브랜치로 돌아가기

분리된 HEAD 상태에서 다시 기존 브랜치로 돌아가려면 간단히 브랜치를 스위치하면 됩니다.

git switch main

또는

git checkout main

그러면 HEAD가 다시 브랜치 참조를 가리키게 되며, 정상적인 Git 작업을 할 수 있습니다.

*  (HEAD -> main)  c5d3a1d  최신 커밋
*                  a8f1b2c  새로운 기능 추가
*                  4f1b3e9  버그 수정
*                  7a3c2d1  개츠비 → 캐츠비 이름 변경
*                  3d2e1f7  1장과 2장으로 분리
*                  9c8b7a6  소설 개요 작성 (최초 커밋)

2. 과거 커밋을 기반으로 새로운 브랜치 만들기

때때로 과거의 특정 시점으로 돌아가 해당 상태를 바탕으로 새로운 작업을 시작하고 싶을 수 있습니다. 이런 경우, 분리된 HEAD 상태에서 새로운 브랜치를 만들면 됩니다.

먼저 특정 커밋을 체크아웃합니다.

git checkout 4f1b3e9

이제 HEAD가 해당 커밋을 가리키고 있으며, 분리된 HEAD 상태가 됩니다.

*                  c5d3a1d  최신 커밋
*                  a8f1b2c  새로운 기능 추가
*                  4f1b3e9  버그 수정  <-- (HEAD)
*                  7a3c2d1  개츠비 → 캐츠비 이름 변경
*                  3d2e1f7  1장과 2장으로 분리
*                  9c8b7a6  소설 개요 작성 (최초 커밋)

이 상태에서 새로운 브랜치를 만들고 스위치하면 됩니다.

git switch -c new-feature

또는

git branch new-feature
git switch new-feature

이제 HEAD가 new-feature 브랜치를 가리키며, 우리는 과거 커밋을 기반으로 새롭게 작업을 시작할 수 있습니다.

*  (HEAD -> new-feature)  4f1b3e9  버그 수정
*                  7a3c2d1  개츠비 → 캐츠비 이름 변경
*                  3d2e1f7  1장과 2장으로 분리
*                  9c8b7a6  소설 개요 작성 (최초 커밋)

이제 new-feature 브랜치에서 새로운 변경 사항을 추가하고 커밋하면, 기존 main 브랜치와는 별개로 새로운 작업 흐름을 만들 수 있습니다.


HEAD를 이용한 커밋 참조

Git에서는 특정 커밋 해시를 사용하여 과거의 커밋을 체크아웃할 수 있지만, HEAD를 활용하면 더 직관적으로 커밋을 참조할 수 있습니다.

1. HEAD~n을 이용한 커밋 탐색

HEAD는 현재 우리가 위치한 브랜치의 최신 커밋을 가리킵니다. HEAD에서 과거 커밋을 참조하려면 HEAD~n 형식을 사용하면 됩니다.

HEAD    → 현재 커밋
HEAD~1  → 한 커밋 전 (부모 커밋)
HEAD~2  → 두 커밋 전 (조부모 커밋)
HEAD~3  → 세 커밋 전

예를 들어, 다음과 같은 브랜치가 있다고 가정해봅시다.

*  (HEAD -> main)  c5d3a1d  최신 커밋
*                  a8f1b2c  새로운 기능 추가
*                  4f1b3e9  버그 수정
*                  7a3c2d1  개츠비 → 캐츠비 이름 변경
*                  3d2e1f7  1장과 2장으로 분리
*                  9c8b7a6  소설 개요 작성 (최초 커밋)

현재 HEADmain 브랜치의 최신 커밋 c5d3a1d를 가리키고 있습니다.
이제 한 커밋 전으로 돌아가려면 다음 명령어를 실행하면 됩니다.

git checkout HEAD~1

그러면 HEAD가 한 커밋 전의 a8f1b2c을 가리키게 됩니다.

*  (HEAD)          a8f1b2c  새로운 기능 추가
*                  4f1b3e9  버그 수정
*                  7a3c2d1  개츠비 → 캐츠비 이름 변경
*                  3d2e1f7  1장과 2장으로 분리
*                  9c8b7a6  소설 개요 작성 (최초 커밋)

이 상태는 분리된 HEAD(detached HEAD) 상태이므로, 새 커밋을 하더라도 브랜치에 속하지 않습니다.

2. HEAD를 사용한 연속 탐색

이전 커밋을 계속해서 탐색하려면 다음과 같이 연속적으로 사용할 수도 있습니다.

git checkout HEAD~2

그러면 HEAD는 두 커밋 전인 4f1b3e9을 가리키게 됩니다.

*  (HEAD)          4f1b3e9  버그 수정
*                  7a3c2d1  개츠비 → 캐츠비 이름 변경
*                  3d2e1f7  1장과 2장으로 분리
*                  9c8b7a6  소설 개요 작성 (최초 커밋)

3. HEAD를 사용한 원래 브랜치로 돌아가기

과거 커밋을 확인한 후 원래 브랜치로 돌아가고 싶다면 간단히 다음 명령어를 실행하면 됩니다.

git switch main

또는 마지막으로 있던 브랜치로 돌아가고 싶다면, - 옵션을 사용할 수 있습니다.

git switch -

이렇게 하면 가장 최근에 있던 브랜치로 복귀할 수 있습니다.


Git Checkout으로 변경 사항 폐기하기

작업 도중 실수로 원하지 않는 변경을 했다면, git checkout을 사용하여 파일을 마지막 커밋된 상태로 되돌릴 수 있습니다. 이는 커밋되지 않은 변경 사항을 쉽게 취소하는 방법입니다.

1. 특정 파일의 변경 사항 취소

예를 들어, 아래와 같은 Git 상태에서 파일을 수정했다고 가정해봅시다.

git status

출력:

수정됨: cat.txt
수정됨: dog.txt

만약 dog.txt 파일을 원래 상태로 되돌리고 싶다면, 다음 명령어를 실행합니다.

git checkout HEAD -- dog.txt

이 명령어를 실행하면 dog.txt는 마지막 커밋된 상태로 복구됩니다.

*  (HEAD -> main)  3f2e1b6  '세 번째 커밋'
*                  2d1b7a9  '두 번째 커밋'
*                  1a8c3d2  '첫 번째 커밋'

2. 여러 파일의 변경 사항 취소

여러 파일을 되돌리려면 파일명을 나열하면 됩니다.

git checkout HEAD -- cat.txt dog.txt

만약 작업한 모든 파일을 원래대로 되돌리고 싶다면, -- . 옵션을 사용할 수도 있습니다.

git checkout HEAD -- .

이 명령어를 실행하면 변경한 모든 파일이 마지막 커밋된 상태로 돌아갑니다.

3. 더 짧은 명령어 사용

HEAD를 생략하고 --만 사용해도 동일한 결과를 얻을 수 있습니다.

git checkout -- cat.txt

이 방식은 보다 간결하며, 동일하게 해당 파일을 마지막 커밋 상태로 복구합니다.


Git Restore로 수정 사항 취소하기

Git의 restore 명령어는 checkout이 담당하던 여러 기능 중 "파일 변경 사항 되돌리기"에 집중하여 분리된 명령어입니다. 이를 사용하면 작업 중인 파일을 간단히 이전 커밋된 상태로 복원할 수 있습니다.

1. 변경 사항을 마지막 커밋 상태로 복원하기

작업 도중 파일을 실수로 수정했지만 아직 git add로 스테이징하지 않았다면, 다음 명령어로 변경 사항을 취소할 수 있습니다.

git restore filename

예를 들어 dog.txt를 원래 상태로 복원하려면 다음을 실행하면 됩니다.

git restore dog.txt

이는 기존의 git checkout HEAD -- dog.txt와 동일한 역할을 합니다.

2. 특정 커밋 상태로 파일 복원하기

restore 명령어를 사용하면 특정 커밋에 있던 파일 상태로 되돌릴 수도 있습니다. 이때 --source 옵션을 사용합니다.

git restore --source HEAD~2 dog.txt

위 명령어는 dog.txt를 현재 커밋에서 두 단계 이전(HEAD~2)의 상태로 복원합니다.

브랜치 내의 현재 위치는 유지되며, 커밋되지 않은 변경 사항이 사라지므로 주의해야 합니다.

3. 여러 파일의 변경 사항 복원하기

모든 변경 사항을 되돌리고 싶다면 다음과 같이 실행할 수 있습니다.

git restore .

이 명령어는 현재 변경된 모든 파일을 복원합니다.


Git Restore로 변경 사항 스테이징 취소하기

Git에서 실수로 파일을 git add로 스테이징했지만, 다시 커밋에 포함하지 않으려면 git restore --staged 명령어를 사용할 수 있습니다.

1. 스테이징된 파일 언스테이징하기

예를 들어, 다음과 같은 변경 사항이 있다고 가정해봅시다.

git status

출력:

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   cat.txt
        modified:   dog.txt
        new file:   secrets.txt

여기서 secrets.txt 파일은 실수로 스테이지되었으며, 커밋에 포함하고 싶지 않습니다. 이를 언스테이징하려면 다음 명령어를 실행합니다.

git restore --staged secrets.txt

이제 secrets.txt는 스테이지에서 제외되었고, 커밋에 포함되지 않습니다. 하지만 파일 자체는 여전히 존재하며, 수정된 상태도 유지됩니다.


2. 여러 개의 파일 언스테이징하기

만약 여러 파일을 한 번에 언스테이징하고 싶다면 다음과 같이 실행할 수 있습니다.

git restore --staged cat.txt dog.txt

또는 모든 스테이징된 파일을 언스테이징하려면:

git restore --staged .

3. git status를 활용하기

Git은 git status를 실행하면 언스테이징하는 방법을 알려줍니다. 예를 들어, git status를 실행하면 다음과 같은 메시지가 나타납니다.

(use "git restore --staged <file>..." to unstage)

따라서 명령어를 기억하지 못하더라도, git status를 참고하면 적절한 명령어를 쉽게 확인할 수 있습니다.


Git Reset으로 커밋 취소하기

git reset은 특정 커밋으로 되돌아가고, 이후의 커밋을 삭제하는 명령어입니다. 하지만 변경 사항을 어떻게 처리할지에 따라 두 가지 방식으로 사용할 수 있습니다.


1. 일반적인 git reset (soft reset)

기본적인 git reset은 지정한 커밋 이후의 커밋을 제거하지만, 변경 사항은 워킹 디렉토리에 그대로 남겨둡니다.

예를 들어, 다음과 같은 브랜치가 있다고 가정합시다.

* (HEAD -> main)  9f8e1c2  '이 커밋을 취소하세요'
*                 4a3b7d9  '실수로 만든 커밋'
*                 3c2d1a5  '네 번째 커밋'
*                 2b1e7f3  '세 번째 커밋'
*                 1a9c8d4  '두 번째 커밋'
*                 0f7b6a2  '첫 번째 커밋'

만약 실수로 만든 커밋이 커밋을 취소하세요를 제거하고 싶다면, 아래 명령어를 실행합니다.

git reset 3c2d1a5

이렇게 하면 Git은 해당 커밋까지만 유지하고, 이후의 커밋들은 삭제됩니다. 하지만 변경 사항은 워킹 디렉토리에 남아있습니다.

* (HEAD -> main)  3c2d1a5  '네 번째 커밋'
*                 2b1e7f3  '세 번째 커밋'
*                 1a9c8d4  '두 번째 커밋'
*                 0f7b6a2  '첫 번째 커밋'

즉, 커밋을 취소하되 변경 사항을 유지하고 싶은 경우 git reset을 사용할 수 있습니다.

이제 원치 않는 커밋을 삭제했지만, 변경 사항은 여전히 남아 있습니다. 만약 해당 변경 사항을 다른 브랜치에서 살리고 싶다면, 새로운 브랜치를 만들어 이동하면 됩니다.

git switch -c new-branch
git add .
git commit -m "이전 커밋에서 변경 사항 복구"

이제 main 브랜치는 원래 상태로 돌아가고, 삭제된 커밋에서 작업했던 내용은 new-branch에서 이어갈 수 있습니다.


2. git reset --hard (하드 리셋)

하드 리셋은 지정한 커밋 이후의 커밋을 삭제하는 것뿐만 아니라 모든 변경 사항도 삭제합니다.

git reset --hard 3c2d1a5

이렇게 하면 이후의 커밋이 모두 제거되며, 변경 사항도 완전히 사라집니다.

* (HEAD -> main)  3c2d1a5  '네 번째 커밋'
*                 2b1e7f3  '세 번째 커밋'
*                 1a9c8d4  '두 번째 커밋'
*                 0f7b6a2  '첫 번째 커밋'

주의: git reset --hard를 실행하면 워킹 디렉토리의 변경 사항도 삭제되므로, 반드시 신중하게 사용해야 합니다.


Git Revert로 커밋 원래대로 복구하기

git revert는 이전 커밋의 변경 사항을 취소하는 새로운 커밋을 생성하는 명령어입니다. 즉, 기존 커밋을 제거하는 것이 아니라 "이전 커밋을 무효화하는 새로운 커밋"을 만드는 방식으로 작업 이력을 유지하면서 변경 사항을 되돌릴 수 있습니다.


1. git resetgit revert의 차이

명령어 동작 방식 기존 커밋 기록 유지 여부 협업 시 안전 여부
git reset 브랜치 포인터를 특정 커밋으로 되돌림 ❌ 삭제됨 ⚠️ 협업 시 주의
git revert 지정한 커밋의 변경 사항을 취소하는 새로운 커밋 생성 ✅ 유지됨 ✅ 안전

2. git revert 사용 방법

예를 들어, 다음과 같은 커밋 기록이 있다고 가정해봅시다.

* (HEAD -> main)  8f2e1b6  '나쁜 커밋 만들었음'
*                 4a3b7d9  '기능 추가'
*                 3c2d1a5  '버그 수정'
*                 2b1e7f3  '초기 설정'

'나쁜 커밋 만들었음'을 취소하려면 다음 명령어를 실행합니다.

git revert HEAD

이제 새로운 커밋이 생성됩니다.

* (HEAD -> main)  a9c8d7f  'Revert "나쁜 커밋 만들었음"'
*                 8f2e1b6  '나쁜 커밋 만들었음'
*                 4a3b7d9  '기능 추가'
*                 3c2d1a5  '버그 수정'
*                 2b1e7f3  '초기 설정'

이처럼 기존 커밋(8f2e1b6)은 남아있지만, 새로운 커밋(a9c8d7f)이 생성되어 해당 변경 사항을 무효화합니다.


3. 특정 커밋 되돌리기

HEAD~1이 아니라 더 이전의 특정 커밋을 되돌릴 수도 있습니다.

git revert 4a3b7d9

이렇게 하면 '기능 추가' 커밋의 변경 사항을 취소하는 새로운 커밋이 생성됩니다.


4. 협업에서 git revert가 안전한 이유

만약 여러 개발자가 공유하는 저장소에서 git reset을 사용하면, 다른 사람들의 작업과 충돌이 발생할 수 있습니다. 하지만 git revert기존 기록을 유지하면서 변경 사항만 되돌리므로 충돌 없이 안전하게 사용할 수 있습니다.

즉, 혼자 작업할 때는 git reset, 팀과 협업할 때는 git revert가 더 적합합니다.


실습링크

1. 프로젝트 준비

우선 제공된 연습용 Git 저장소를 클론해야 합니다.

git clone <저장소 URL>
cd yesterday-exercise

이제 lyrics.txt 파일이 포함된 프로젝트 폴더가 생성됩니다.
git log --oneline을 실행하면 스크램블 에그(원래 가사 버전)와 예스터데이(수정된 가사 버전)에 관련된 여러 커밋이 보일 것입니다.


2. 특정 커밋 체크아웃 및 브랜치 생성

1️⃣ 초기 가사 버전(스크램블 에그)으로 이동
먼저 git log --oneline을 실행해 "원래 가사 끝" 커밋을 찾고, 해당 커밋으로 체크아웃합니다.

git checkout <스크램블 에그 커밋 해시>

이제 HEAD가 해당 커밋을 가리키고 있으며, 분리된 HEAD(detached HEAD) 상태가 됩니다.

2️⃣ 스크램블 에그 브랜치 생성
이 상태에서 새 브랜치를 만들어 현재 커밋을 기준으로 작업할 수 있도록 합니다.

git switch -c scrambled-egg

이제 scrambled-egg 브랜치에서 과거 가사 버전을 유지할 수 있습니다.


3. 변경 사항 되돌리기

1️⃣ 마스터 브랜치로 돌아가기

git switch master

이제 lyrics.txt의 최신 가사가 다시 나타납니다.

2️⃣ 가사를 삭제하고 되돌리기
파일의 모든 내용을 삭제한 후 Git 명령어를 사용하여 원래 상태로 복구해야 합니다.

git restore lyrics.txt

또는 기존 git checkout을 활용할 수도 있습니다.

git checkout HEAD -- lyrics.txt

이제 lyrics.txt 파일이 원래대로 복구됩니다.


4. 패러디 가사 추가 후 되돌리기

1️⃣ 패러디 가사 추가 및 커밋
lyrics.txt 파일에 새로운 패러디 가사를 추가하고, 이를 커밋합니다.

git add lyrics.txt
git commit -m "404 가사 시작"

다시 한 번 패러디 가사를 추가하고, 커밋합니다.

git add lyrics.txt
git commit -m "404 가사 끝"

2️⃣ 커밋 취소 및 새로운 브랜치로 이동
마스터 브랜치에서 패러디 가사 커밋을 제거하고 싶다면 리셋(reset) 명령어를 사용합니다.

git reset <패러디 가사 추가 전 커밋 해시>

⚠️ 주의: --hard 옵션을 사용하면 변경 사항도 삭제되므로, 여기서는 사용하지 않습니다.
이제 커밋은 삭제되었지만, 파일의 변경 사항은 그대로 남아 있습니다.

3️⃣ 새 브랜치 404 생성 및 커밋
이제 변경 사항을 새로운 브랜치에 저장하고 싶다면 다음과 같이 실행합니다.

git switch -c 404
git add lyrics.txt
git commit -m "404 패러디 가사 추가"

이제 404 브랜치에서는 패러디 가사가 유지되고, 마스터 브랜치로 돌아가면 원래의 예스터데이 가사가 남아있게 됩니다.

git switch master

📝 정리

Git 명령어 용도
git restore <파일> 커밋되지 않은 변경 사항 되돌리기
git reset <커밋 해시> 커밋 삭제(변경 사항 유지)
git checkout <커밋 해시> 특정 커밋 체크아웃(분리된 HEAD)
git switch -c <브랜치> 새로운 브랜치 생성 및 전환