변경 사항 되돌리기

Git은 변경 사항을 되돌리는 옵션을 제공합니다. Git workflow의 어떤 지점에서든 변경 사항을 되돌릴 수 있습니다.

변경 사항을 되돌리는 방법은 다음과 같이 달라집니다.

  • 로컬 컴퓨터에만 있는 경우
  • GitLab.com과 같은 Git 서버에 저장된 경우

로컬 변경 사항 되돌리기

원격 저장소에 변경 사항을 푸시하지 않은 경우, 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 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 reset --hard
      

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

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

커밋된 로컬 변경 사항 스테이징되지 않은 상태로 되돌리기

커밋 히스토리를 유지하면서 커밋을 되돌릴 수 있습니다.

이 예시에서는 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을 사용하여 새 머지 요청으로 해당 커밋을 cherry-pick할 수 있습니다.

참고: 다른 해결책은 B로 되돌리고 E로 커밋하는 것입니다. 그러나 이러한 해결책은 로컬에서 다른 개발자가 가지고 있는 것과 충돌하는 A-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를 변경한 작업에 대한 설명.

원격 변경 내용을 변경 내역을 변경하지 않고 취소

원격 저장소에서 변경 내용을 되돌리려면 변경을 원하는 새 커밋을 만들면 됩니다. 이 프로세스를 따라야 하며 변경 내역을 보존하고 명확한 타임라인과 개발 구조를 제공합니다. 그러나 여러 다른 개발자가 작업하는 기반 브랜치로 병합된 경우에만 해당 프로시저가 필요합니다.

revert를 사용하여 브랜치 유지

특정 커밋 B에서 도입된 변경을 되돌리려면:

git revert B

원격 변경을 변경 내역을 변경하면서 취소

원격 변경을 취소하고 변경 내역을 변경할 수 있습니다.

업데이트된 변경 내역에서도 이전 커밋에는 여전히 커밋 SHA로 액세스할 수 있습니다. 이는 자동으로 분리된 커밋의 모든 정리가 수행되거나 수동으로 정리가 실행될 때까지의 경우입니다. 심지어 정리가 이루어져도 여전히 그들을 가리키는 참조가 있으면 이전 커밋이 제거되지 않을 수 있습니다.

원격 브랜치에 히스토리를 수정하면 문제가 발생합니다

히스토리 변경이 허용되는 경우

공용 브랜치나 다른 개발자가 사용할 수 있는 브랜치에서 작업할 때 히스토리를 변경해서는 안됩니다.

GitLab와 같이 큰 오픈 소스 저장소에 기여할 때 나중에 리뷰 후에 커밋을 하나로 압축할 수 있습니다.

대상 브랜치에서 당신의 브랜치의 커밋을 하나로 압축하기 위해 병합할 때 git merge --squash를 사용할 수 있습니다.

참고: 기본 브랜치나 공유 브랜치의 커밋 히스토리를 변경하지 마세요.

히스토리 변경 방법

병합 요청의 브랜치는 공개 브랜치이며 다른 개발자가 사용할 수 있습니다. 그러나 프로젝트 규칙이 리뷰가 완료된 후 대상 브랜치에서 표시된 커밋의 수를 줄이기 위해 git rebase를 사용하도록 요구할 수 있습니다.

git rebase -i를 사용하여 히스토리를 수정, 압축 및 커밋을 삭제할 수 있습니다.

#
# 명령:
# p, pick = 커밋 사용
# r, reword = 커밋 사용, 그러나 커밋 메시지 편집
# e, edit = 커밋 사용, 그러나 수정을 중단
# s, squash = 커밋 사용, 그러나 직전 커밋과 병합
# f, fixup = "squash"와 유사하나, 이 커밋의 로그 메시지는 폐기
# x, exec = 명령 실행 (라인의 나머지) 셸을 사용하여
# d, drop = 커밋 제거
#
# 이러한 라인은 재정렬될 수 있으며 위에서 아래로 실행됩니다.
#
# 라인을 제거하면 해당 커밋이 손실됩니다.
#
# 그러나 모든 것을 제거하면 리베이스가 중단됩니다.
#
# 빈 커밋은 주석 처리됩니다.

참고: 리베이스를 중단하려면 편집기를 닫지 마세요. 대신 모든 주석이 달린 줄을 제거하고 저장하세요.

공용 및 원격 브랜치에서 git rebase를 신중하게 사용하세요. 원격 저장소에 밀기 전에 로컬에서 실험을 해보세요.

# commit-id에서 HEAD(현재 커밋)까지 히스토리 수정
git rebase -i commit-id

텍스트 삭제

실수로 커밋된 민감하거나 비밀스런 정보를 영구적으로 삭제하여 저장소의 히스토리에서 더 이상 액세스할 수 없게 합니다. 문자열 목록을 ***REMOVED***로 대체합니다.

또한, 특정 파일을 저장소에서 완전히 삭제하려면 블롭 제거를 참조하세요.

필수 조건:

  • 프로젝트에 대한 소유자 역할이 있어야 합니다.

저장소에서 텍스트를 삭제하는 방법:

  1. 왼쪽 사이드바에서 검색 또는 이동을 선택하고 프로젝트를 찾습니다.
  2. 설정 > 저장소를 선택합니다.
  3. 저장소 유지관리를 확장합니다.
  4. 텍스트 삭제를 선택합니다.
  5. 창에서 텍스트를 입력합니다. 정규식과 glob 패턴을 사용할 수 있습니다.
  6. 일치하는 문자열 삭제를 선택합니다.
  7. 확인 대화상자에서 프로젝트 경로를 입력합니다.
  8. 예, 일치하는 문자열을 삭제를 선택합니다.
  9. 왼쪽 사이드바에서 설정 > 일반을 선택합니다.
  10. 고급을 확장합니다.
  11. 하우스키퍼 실행을 선택합니다.

커밋을 제거하여 커밋 되돌리기

  • 마지막 커밋을 되돌린 후 모든 것을 스테이징 영역으로 되돌립니다:

    git reset --soft HEAD^
    
  • 파일을 추가하고 커밋 메시지를 변경합니다:

    git commit --amend -m "새 메시지"
    
  • 마지막 변경 사항을 되돌리고 다른 변경 사항을 모두 제거하려면, 아직 푸쉬하지 않은 경우:

    git reset --hard HEAD^
    
  • 마지막 변경 사항을 되돌리고 마지막 2개의 커밋을 제거하려면, 아직 푸쉬하지 않은 경우:

    git reset --hard HEAD^^
    

Git reset 샘플 워크플로우

다음은 일반적인 Git reset 워크플로우입니다:

  1. 파일을 편집합니다.
  2. 브랜치의 상태를 확인합니다:

    git status
    
  3. 잘못된 커밋 메시지로 변경 사항을 브랜치에 커밋합니다:

    git commit -am "kjkfjkg"
    
  4. Git 로그를 확인합니다:

    git log
    
  5. 올바른 커밋 메시지로 커밋을 수정합니다:

    git commit --amend -m "새 댓글 추가됨"
    
  6. 다시 Git 로그를 확인합니다:

    git log
    
  7. 브랜치를 소프트 리셋합니다:

    git reset --soft HEAD^
    
  8. 다시 Git 로그를 확인합니다:

    git log
    
  9. 원격 브랜치에서 브랜치의 업데이트를 가져옵니다:

    git pull origin <브랜치>
    
  10. 브랜치의 변경 사항을 원격으로 푸시합니다:

    git push origin <브랜치>
    

새로운 교체 커밋으로 커밋 되돌리기

git revert <commit-sha>

git revertgit reset의 차이점

  • git reset 명령어는 커밋을 제거합니다. git revert 명령어는 변경 사항을 제거하지만 커밋은 남겨둡니다.
  • git revert 명령어는 안전합니다. 왜냐하면 되돌릴 수 있기 때문입니다.
# 변경된 파일
git commit -am "버그 도입"
git revert HEAD
# 변경 내용을 되돌리는 새로운 커밋 생성
# 이제 되돌린 커밋을 다시 적용하려고 합니다
git log # 되돌린 커밋의 해시 가져오기
git revert <revert 커밋 해시>
# 되돌린 커밋이 다시 생겨납니다 (다시 새로운 커밋이 생성됨)

변경 내용 스테이징 해제

Git에서 파일을 _스테이징_하면 커밋을 준비하기 위해 파일의 변경 사항을 추적하도록 지시합니다. 파일의 변경 사항을 무시하고 다음 커밋에 포함시키고 싶지 않다면 파일을 _스테이징 해제_하세요.

파일 스테이징 해제

  • 스테이징에서 파일을 제거하지만 변경 내용은 유지:

    git reset HEAD <file>
    
  • 마지막 세 개의 커밋을 스테이징에서 제거:

    git reset HEAD^3
    
  • HEAD로부터 특정 파일의 변경 내용을 스테이징에서 제거:

    git reset <filename>
    

파일을 스테이징에서 제거한 후, 파일을 변경 내용 이전의 상태로 되돌리려면:

git checkout -- <file>

파일 제거

  • 디스크와 저장소에서 파일을 제거하려면 git rm을 사용하세요. 디렉토리를 제거하려면 -r 플래그를 사용하세요:

    git rm '*.txt'
    git rm -r <dirname>
    
  • 디스크에 파일을 유지하고 저장소에서만 삭제하려면(예를 들어 .gitignore에 추가하려는 파일) --cache 플래그를 사용하여 rm 명령어를 사용하세요:

    git rm <filename> --cache
    

이러한 명령은 파일을 현재 브랜치에서 제거하지만 저장소의 이력에서는 삭제하지 않습니다. 파일의 모든 흔적을 완전히 삭제하려면 과거와 현재 모두 저장소에서 완전히 제거하려면 블랍 제거를 참조하세요.

관련 주제