Git 작업 되돌리기 및 수정하기

Git을 사용하다 보면 실수로 파일을 잘못 수정하거나, 커밋 메시지에 오타를 내거나, 특정 변경 사항을 이전 상태로 되돌려야 하는 등 다양한 상황에 직면하게 됩니다. Git은 이렇게 이미 수행한 작업을 되돌리거나 수정할 수 있는 여러 강력한 명령어들을 제공합니다.

최신 커밋 수정 (git commit –amend)

가장 최근에 한 커밋을 약간 수정하고 싶을 때 git commit --amend 명령어를 사용하면 매우 편리합니다. 예를 들어, 방금 커밋을 했는데 커밋 메시지에 오타를 발견했거나, 미처 포함시키지 못한 작은 수정사항이 생각났을 때 이 명령어로 이전 커밋을 대체하는 새로운 커밋을 만들 수 있습니다.
만약 마지막 커밋 메시지를 “Feat: Add new user featur”라고 적었다면, 다음과 같이 수정할 수 있습니다.

# 최신 커밋의 메시지만 수정 (새로운 메시지를 입력)
git commit --amend -m "Feat: Add new user feature"

만약 커밋 메시지는 그대로 두고, ‘config_update.py’ 파일을 빠뜨려서 추가하고 싶다면, 먼저 해당 파일을 스테이징한 후 --no-edit 옵션을 사용하거나 메시지 편집기를 통해 기존 메시지를 그대로 사용할 수 있습니다.

# 빠뜨린 파일 스테이징
git add config_update.py

# 메시지 변경 없이 최신 커밋에 스테이징된 변경사항 추가 (기존 메시지 유지)
git commit --amend --no-edit

# 또는, 메시지 편집기를 열어 기존 메시지를 확인/수정하면서 추가
# git commit --amend

⚠️ 주의사항git commit --amend는 이전 커밋을 새로운 커밋으로 대체하여 커밋 이력을 변경하는 명령어입니다. 따라서, 아직 원격 저장소에 푸시(push)하지 않은 로컬 커밋에 대해서만 사용하는 것이 안전합니다. 이미 푸시된 커밋을 수정하면 다른 팀원들의 작업 이력과 충돌을 일으킬 수 있습니다.

특정 시점으로 되돌리기 (git reset)

git reset 명령어는 현재 브랜치가 가리키는 커밋(HEAD)을 과거의 특정 커밋으로 이동시켜 프로젝트의 상태를 이전으로 되돌립니다. 이 명령어는 강력하지만, 어떻게 사용하느냐에 따라 작업 내용이 유실될 수도 있으므로 주의해서 사용해야 합니다. git reset에는 크게 세 가지 옵션(--soft--mixed--hard)이 있으며, 각 옵션에 따라 워킹 디렉토리와 스테이징 영역의 처리 방식이 달라집니다.

git reset –soft <커밋_ID>

HEAD 포인터만 지정한 <커밋_ID>로 이동시킵니다. 스테이징 영역과 워킹 디렉토리의 내용은 변경되지 않습니다. 즉, <커밋_ID> 이후의 모든 변경사항들이 마치 커밋되지 않고 스테이징 영역에만 추가된 것처럼 남게 됩니다.
예를 들어, 마지막 세 개의 커밋(HEAD~3은 현재 HEAD로부터 3번째 이전 커밋을 의미)을 하나의 의미 있는 커밋으로 합치고 싶을 때, git reset --soft HEAD~3를 실행하면 HEAD는 세 커밋 이전으로 돌아가지만, 그 세 커밋 동안의 모든 변경사항은 스테이징 영역에 그대로 남아있게 됩니다. 이후 git commit 명령으로 이 변경사항들을 하나의 새로운 커밋으로 만들 수 있습니다.

# 최근 3개의 커밋을 취소하되, 변경 내용은 스테이징 영역에 유지
git reset --soft HEAD~3
# 이후, 새로운 커밋 메시지와 함께 하나의 커밋으로 합침
git commit -m "Refactor feature X and fix related bugs"

git reset –mixed <커밋_ID> (또는 옵션 없이 git reset <커밋_ID>)

HEAD 포인터를 지정한 <커밋_ID>로 이동시키고, 스테이징 영역의 내용도 해당 커밋 상태로 되돌립니다. 하지만 워킹 디렉토리의 파일 내용은 변경되지 않습니다. 즉, <커밋_ID> 이후의 변경사항들은 워킹 디렉토리에 남아있지만, 스테이징은 해제된 상태(unstaged)가 됩니다.
예를 들어, 마지막 커밋을 했는데, 일부 파일은 커밋에 포함시키고 싶지 않았거나, 변경 내용을 좀 더 수정하고 싶을 때 git reset HEAD~1 (또는 git reset --mixed HEAD~1)을 사용합니다. 이렇게 하면 마지막 커밋은 취소되고, 해당 커밋의 변경사항들은 워킹 디렉토리에 수정된 상태로 남아있게 됩니다. 이후 필요한 파일만 다시 선별하여 스테이징하고 커밋할 수 있습니다.

# 가장 최근 커밋을 취소하고, 변경 내용은 워킹 디렉토리에 남김 (스테이징 해제)
git reset HEAD~1
# 이제 'file_to_keep.txt'만 다시 스테이징하고 커밋
git add file_to_keep.txt
git commit -m "Keep changes only in file_to_keep.txt"

git reset –hard <커밋_ID>

HEAD 포인터를 지정한 <커밋_ID>로 이동시키고, 스테이징 영역과 워킹 디렉토리의 내용 모두 해당 커밋 상태로 완전히 되돌립니다. 즉, <커밋_ID> 이후의 모든 변경사항(커밋된 내용 포함)과 현재 워킹 디렉토리 및 스테이징 영역의 커밋되지 않은 변경사항까지 모두 사라집니다.
예를 들어, 최근 몇 개의 커밋이 완전히 잘못되었고, 해당 변경사항들을 모두 버리고 특정 과거 시점(예: 커밋 ID가 abcdef123인 상태)으로 프로젝트 상태를 완전히 되돌리고 싶을 때 git reset --hard abcdef123를 사용합니다.

# 'abcdef123' 커밋 시점으로 모든 것을 되돌림 (주의: 이후 변경사항 모두 삭제됨)
git reset --hard abcdef123

--hard 옵션은 데이터 유실 위험이 매우 크므로, 실행 전 매우 신중해야 하며, 정말로 해당 변경사항들이 필요 없는지 반드시 확인해야 합니다. 보통 로컬에서만 작업하던 내용이고, 아직 원격 저장소에는 푸시하지 않았을 때 제한적으로 사용합니다.

커밋 내용 되돌리기 (새로운 커밋 생성) (git revert)

이미 원격 저장소에 푸시되어 팀원들과 공유된 커밋의 내용을 되돌리고 싶을 때, 이력을 변경하는 git reset은 사용할 수 없습니다. 이때는 git revert <되돌릴_커밋_ID> 명령어를 사용합니다. git revert는 지정한 커밋에서 이루어졌던 변경사항을 정확히 반대로 수행하는 새로운 커밋을 생성합니다. 이 방법은 기존 커밋 이력을 그대로 유지하면서 특정 변경의 효과만 안전하게 제거할 수 있어 협업 환경에 적합합니다.
예를 들어, abcdef123이라는 커밋에서 버그가 발생시키는 코드가 추가되었다는 것을 발견했고, 이 커밋은 이미 팀원들과 공유된 상태라고 가정해 봅시다. 이때 해당 커밋의 변경사항만을 되돌리는 새로운 커밋을 만들고 싶다면 다음과 같이 실행합니다.

# 'abcdef123' 커밋에서 이루어진 변경사항을 되돌리는 새로운 커밋 생성
git revert abcdef123

이 명령을 실행하면 Git은 abcdef123 커밋에서 추가된 내용은 삭제하고, 삭제된 내용은 다시 추가하는 등의 반대 작업을 수행하는 새로운 커밋을 만들 준비를 합니다. 커밋 메시지 편집기가 나타나면 revert 커밋에 대한 설명을 작성하고 저장하여 새로운 revert 커밋을 완료합니다.

워킹 디렉토리의 변경 사항 되돌리기

아직 커밋하지 않은, 즉 워킹 디렉토리에만 있는 파일의 변경 사항을 마지막 커밋 상태로 되돌리고 싶을 때가 있습니다. 예를 들어, index.html 파일을 마구 수정하다가 내용이 엉망이 되어, 마지막 커밋 상태로 이 파일만 깔끔하게 되돌리고 싶다면 최신 Git 버전에서는 git restore <파일명> 명령을, 구버전 Git에서는 git checkout -- <파일명> 명령을 사용할 수 있습니다.

# 'README.md' 파일의 워킹 디렉토리 변경사항을 마지막 커밋 상태로 되돌림 (최신 Git)
git restore README.md

# 'config.js' 파일의 워킹 디렉토리 변경사항을 마지막 커밋 상태로 되돌림 (구버전 Git 호환)
git checkout -- config.js

이 명령어들은 해당 파일의 워킹 디렉토리 변경사항을 버리므로, 필요한 내용이 있다면 실행 전에 백업해야 합니다.

스테이징된 변경 사항 내리기 (Unstaging)

때로는 파일을 스테이징 영역(git add를 실행한 후)에 올렸지만, 커밋하기 전에 다시 워킹 디렉토리로 내리고 싶을 수 있습니다. 예를 들어, git add . 명령으로 모든 변경사항을 스테이징했는데, ‘temp_notes.txt’ 파일은 이번 커밋에 포함시키고 싶지 않다는 것을 깨달았을 때, 해당 파일만 스테이징 영역에서 내릴 수 있습니다. 이때 최신 Git 버전에서는 git restore --staged <파일명> 명령을, 구버전 Git에서는 git reset HEAD <파일명> 명령을 사용합니다.

# 'important_data.txt' 파일을 스테이징 영역에서 내림 (최신 Git)
git restore --staged important_data.txt

# 'specific_file.py' 파일을 스테이징 영역에서 내림 (구버전 Git 호환)
git reset HEAD specific_file.py

이 명령어들은 스테이징만 취소할 뿐, 파일의 변경 내용은 워킹 디렉토리에 그대로 남아 있습니다.

임시 저장 공간 활용 (git stash)

현재 브랜치에서 아직 커밋하지 않은 작업(스테이징된 변경사항 및 워킹 디렉토리의 수정사항 모두)을 잠시 안전하게 치워두고 다른 작업을 해야 할 때 git stash 명령어가 매우 유용합니다. 예를 들어, feature-A 브랜치에서 로그인 폼 UI 작업을 한창 진행 중인데, 갑자기 main 브랜치에서 긴급한 버그를 수정해달라는 요청을 받았습니다. 아직 로그인 폼 작업이 완료되지 않아 커밋하기는 애매한 상황일 때, git stash (또는 메시지와 함께 git stash push -m "메시지") 명령으로 현재까지의 변경사항들을 임시 저장 공간에 숨겨두고 작업 디렉토리를 깨끗한 상태(마지막 커밋 상태)로 만들 수 있습니다.

# 현재 워킹 디렉토리 및 스테이징 영역의 변경사항을 스태시에 저장 (기본 메시지 사용)
git stash

# 메시지와 함께 스태시에 저장
git stash push -m "Working on user login form UI components"

# 저장된 스태시 목록 확인
git stash list

# 출력:
# stash@{0}: On feature-A: Working on user login form UI components
# stash@{1}: WIP on feature-B: ...

# 가장 최근 스태시(stash@{0})를 현재 브랜치에 다시 적용하고 스태시 목록에서 제거
git stash pop

# 특정 스태시(예: stash@{1})를 적용하되, 스태시 목록에는 그대로 남겨둠
git stash apply stash@{1}

# 특정 스태시(예: stash@{2})를 스태시 목록에서 삭제
git stash drop stash@{2}

# 모든 스태시를 목록에서 삭제 (주의!)
git stash clear

긴급 버그 수정 요청을 받은 시나리오를 이어가면, git stash로 작업을 숨긴 후 main 브랜치로 전환하여 버그를 수정하고 커밋합니다. 그리고 다시 feature-A 브랜치로 돌아와 git stash pop 명령으로 이전에 저장해둔 로그인 폼 UI 작업 내용을 복원하여 중단했던 부분부터 계속 진행할 수 있습니다. git stash pop은 가장 최근의 스태시를 적용하고 해당 스태시를 목록에서 삭제하며, git stash apply는 스태시를 적용하되 목록에는 남겨둡니다.

마무리

이처럼 Git의 되돌리기 및 수정 관련 명령어들은 다양한 상황에서 유용하게 사용될 수 있으며, 각 명령어의 특성과 사용 시 주의사항을 잘 숙지한다면 더욱 안전하고 효율적인 버전 관리가 가능해집니다.

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤