Git에서의 되돌리기 옵션

Git은 변경 사항을 되돌리는 옵션을 제공합니다. 변경 사항을 되돌리는 방법은 변경 사항이 저장되지 않았는지, 스테이징되었는지, 커밋되었는지, 또는 푸시되었는지에 따라 다릅니다.

변경 사항을 되돌릴 수 있는 시점

일반적인 Git 워크플로우에서:

  1. 파일을 생성하거나 편집합니다. 이 파일은 저장되지 않은 상태로 시작됩니다. 새로운 경우에는 아직 Git에서 추적되지 않습니다.
  2. 파일을 로컬 리포지터리에 추가합니다 (git add), 이로써 파일은 스테이징 상태로 이동합니다.
  3. 파일을 로컬 리포지터리에 커밋합니다 (git commit).
  4. 그런 다음, 파일을 다른 개발자와 공유할 수 있도록, 원격 리포지터리에 커밋함으로써(git push)

이와 같은 워크플로우의 어느 시점에서든 변경 사항을 되돌릴 수 있습니다:

로컬 변경 사항 되돌리기

원격 리포지터리에 변경 사항을 푸시하기 전에 Git에서 만든 변경 사항은 로컬 개발 환경에만 있습니다.

저장되지 않은 로컬 변경 사항 되돌리기

변경을 만들었지만 아직 스테이징하지 않은 경우, 작업을 되돌릴 수 있습니다.

  1. 파일이 스테이징되지 않았는지 (git add <file>을 사용하지 않았는지) git status를 실행하여 확인합니다.

    $ git status
    On branch main
    Your branch is up-to-date with 'origin/main'.
    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)
           
        modified:   <file>
    no changes added to commit (use "git add" and/or "git commit -a")
    
  2. 옵션을 선택하고 변경 사항을 되돌립니다.

    • 로컬 변경 사항 덮어쓰기:

      git checkout -- <file>
      
    • 나중에 재사용할 수 있도록 로컬 변경 사항 저장:

      git stash
      
    • 모든 파일의 로컬 변경 사항 영구적으로 삭제:

      git reset --hard
      

스테이징된 로컬 변경 사항 되돌리기

파일을 스테이징했다면, 이를 되돌릴 수 있습니다.

  1. 파일이 스테이징되었는지(git add <file>을 사용했는지) git status를 실행하여 확인합니다.

    $ git status
    On branch main
    Your branch is up-to-date with 'origin/main'.
    Changes to be committed:
      (use "git restore --staged <file>..." to unstage)
         
      new file:   <file>
    
  2. 옵션을 선택하고 변경 사항을 되돌립니다.

    • 파일을 스테이징 해제하지만 변경 사항을 유지하려면:

      git restore --staged <file>
      
    • 모든 것을 스테이징 해제하지만 변경 사항 유지:

      git reset
      
    • 파일을 현재 커밋 (HEAD)의 상태로 스테이징 해제:

      git reset HEAD <file>
      
    • 모든 로컬 변경 사항을 삭제하지만 나중에 저장:

      git stash
      
    • 모든 것을 영구적으로 삭제:

      git reset --hard
      

빠르게 로컬 변경 사항 저장

다른 브랜치로 변경하려면 git stash를 사용할 수 있습니다.

  1. 작업을 저장하려는 브랜치에서 git stash를 입력합니다.
  2. 다른 브랜치로 전환합니다 (git checkout <branchname>).
  3. 커밋하고 푸시하고 테스트합니다.
  4. 변경을 계속할 원하는 브랜치로 돌아갑니다.
  5. git stash list를 사용하여 이전에 저장된 커밋을 나열합니다.

    stash@{0}: WIP on submit: 6ebd0e2... Update git-stash documentation
    stash@{1}: On master: 9cc0589... Add git-stash
    
  6. git stash의 버전을 실행합니다:

    • 이전에 저장된 변경을 되돌리고 해당 변경을 저장디렉터리에서 제거하려면 git stash pop을 사용합니다.
    • 이전에 저장된 변경을 되돌리지만 그대로 유지하려면 git stash apply를 사용합니다.

커밋된 로컬 변경 사항 되돌리기

로컬 리포지터리에 커밋을 하면 (git commit), Git은 변경 사항을 기록합니다. 아직 원격 리포지터리에 푸시하지 않았기 때문에 변경 사항은 공개되지 않았습니다 (또는 다른 개발자들과 공유되지 않았습니다). 이 시점에서 변경 사항을 되돌릴 수 있습니다.

히스토리 변경 없이 스테이징된 로컬 변경 사항 되돌리기

커밋 히스토리를 유지한 채로 커밋을 되돌릴 수 있습니다.

이 예시에서는 A,B,C,D,E 다섯 개의 커밋을 A-B-C-D-E 순서대로 커밋했다고 가정합니다. 되돌리고자 하는 커밋은 B입니다.

  1. 되돌릴 커밋의 커밋 SHA를 찾습니다. 커밋 로그를 확인하려면 git log를 입력합니다.
  2. 옵션을 선택하고 변경 사항을 되돌립니다.

    • 커밋 B에서 도입된 추가 및 삭제 변경 사항 교체:

      git revert <commit-B-SHA>
      
    • 커밋 B에서 특정 파일 또는 디렉터리의 변경 사항을 되돌리지만, 스테이징된 상태로 유지:

      git checkout <commit-B-SHA> <file>
      
    • 커밋 B에서 특정 파일 또는 디렉터리의 변경 사항을 되돌리지만, 스테이징되지 않은 상태로 유지:

      git reset <commit-B-SHA> <file>
      

여러 커밋된 변경 사항 되돌리기

여러 커밋에서 복구할 수 있습니다. 예를 들어, 기능 브랜치에 커밋 A-B-C-D를 했고, 그 후 CD를 되돌리고 싶다고 가정합니다.

여러 부정확한 커밋에서 복구하는 방법:

  1. 마지막 올바른 커밋을 체크아웃합니다. 이 예시에서는 B입니다.

    git checkout <commit-B-SHA>
    
  2. 새로운 브랜치를 생성합니다.

    git checkout -b new-path-of-feature
    
  3. 변경을 추가하고 푸시하고 커밋합니다.

이제 커밋은 A-B-C-D-E입니다.

또 다른 방법으로 GitLab에서는 해당 커밋을 새로운 Merge Request으로 cherry-pick할 수 있습니다.

note
B로 재설정하고 E를 커밋하는 것도 하나의 해결책이 될 수 있지만, 다른 개발자들이 로컬환경에 갖고 있는 것과 충돌하기 때문에 이 방법은 권장되지 않습니다.

로컬에서 스테이징된 변경 내용을 히스토리 수정으로 되돌리기

다음 작업은 Git 히스토리를 다시 작성합니다.

특정 커밋 삭제

특정 커밋을 삭제할 수 있습니다. 예를 들어, A-B-C-D 커밋이 있고 B 커밋을 삭제하려는 경우입니다.

  1. 현재 커밋 D에서 B까지 범위를 재베이스합니다:

    git rebase -i A
    

    커밋 디렉터리이 편집기에 표시됩니다.

  2. 커밋 B 앞에 pickdrop으로 변경합니다.
  3. 다른 모든 커밋에 대해 기본값인 pick을 유지합니다.
  4. 편집기를 저장하고 종료합니다.

특정 커밋 수정

특정 커밋을 수정할 수 있습니다. 예를 들어, A-B-C-D 커밋이 있고 B 커밋에서 도입된 내용을 수정하려는 경우입니다.

  1. 현재 커밋 D에서 B까지 범위를 재베이스합니다:

    git rebase -i A
    

    커밋 디렉터리이 편집기에 표시됩니다.

  2. 커밋 B 앞에 pickedit으로 변경합니다.
  3. 다른 모든 커밋에 대해 기본값인 pick을 유지합니다.
  4. 편집기를 저장하고 종료합니다.
  5. 편집기에서 파일을 열고 편집한 후 변경 사항을 커밋합니다:

    git commit -a
    

되돌리기 취소

이전 로컬 커밋을 다시 불러올 수 있습니다. 그러나 이전 커밋들이 모두 사용 가능한 것은 아니며, Git은 정기적으로 브랜치나 태그에 의해 도달할 수 없는 커밋을 정리합니다.

리포지터리 히스토리를 확인하고 이전 커밋을 추적하려면 git reflog show를 실행하세요. 예시:

$ git reflog show

# 예시 출력:
b673187 HEAD@{4}: merge 6e43d5987921bde189640cc1e37661f7f75c9c0b: Merge made by the 'recursive' strategy.
eb37e74 HEAD@{5}: rebase -i (finish): returning to refs/heads/master
eb37e74 HEAD@{6}: rebase -i (pick): Commit C
97436c6 HEAD@{7}: rebase -i (start): checkout 97436c6eec6396c63856c19b6a96372705b08b1b
...
88f1867 HEAD@{12}: commit: Commit D
97436c6 HEAD@{13}: checkout: moving from 97436c6eec6396c63856c19b6a96372705b08b1b to test
97436c6 HEAD@{14}: checkout: moving from master to 97436c6
05cc326 HEAD@{15}: commit: Commit C
6e43d59 HEAD@{16}: commit: Commit B

이 출력은 리포지터리의 히스토리를 보여주며 다음을 포함합니다:

  • 커밋 SHA
  • 몇 번째로 HEAD가 변경된 액션을 수행했는지 (HEAD@{12}가 12번째 HEAD 변경 액션 전에 만들어진 것임)
  • 수행된 액션, 예: 커밋, 리베이스, 머지
  • HEAD를 변경한 액션에 대한 설명

… (이하 생략)