개요
클론 코딩을 하거나 토이 프로젝트를 진행하면서, git history가 엉망진창이 되는 경우가 종종 있었다. 엉켜버린 실타래를 풀다가 지쳐서 결국 프로젝트를 삭제하고, 다시 클론하거나 처음부터 다시 시작했던 경험이 여러 번 있었다. 🤦♂️
커밋 내역이 마음에 들지 않거나, 특정 커밋에 불필요한 변경 사항이 포함된 경우, 커밋을 너무 세분화하거나 오랫동안 커밋을 하지 않다가 한꺼번에 커밋을 하게 되는 경우 등 여러 상황에서 이런 문제들이 발생했다. 이런 상황에서 git 히스토리를 깔끔하게 관리할 수 있는 명령어들인 ammend
, restore
, reset
, revert
명령어에 대해서 알아보자!
git commit –amend
- 최근 커밋 메시지를 수정하거나 업데이트할 때 사용한다.
주요 사용 사례
- 커밋 메시지 수정
# 마지막 커밋 메시지 수정
git commit --amend -m "수정할 메시지"
- 누락된 파일 추가 및 커밋 내용 수정 (업데이트)
1
2
3
4
5
6
7
| # 커밋 후에 추가하려는 파일을 누락했음을 알게 되었을 경우.
git add [filename]
git commit --amend
# 커밋 내용 수정 -> 이미 커밋된 파일을 수정한 후 변경사항 반영
git add [filename]
git commit --amend
|
주의 사항
- 브랜치와 원격 저장소 : 수정된 커밋을 원격 저장소에 푸시하려면, 강제 푸시가 필요하다. (
git push --force
) 이 경우 협업 중인 사람들의 로컬 저장소에 문제가 발생할 수 있으므로 신중하게 사용해야 한다.
git restore
- 커밋되지 않은 로컬 변경 사항. 즉, 작업 디렉토리와 인덱스(staging area)에서의 파일 변경을 복구하는 데 사용된다.
주요 사용 사례
- 작업 디렉토리의 변경사항 복구
1
2
| # 작업 디렉토리에서 변경된 파일을 마지막 커밋 내용으로 복구
git restore [filename]
|
- 스테이징된 변경사항 취소
1
2
3
| # 인덱스에 있는 파일을 다시 unstage
# 파일을 인덱스에서 제거하고, 해당 파일을 작업 디렉토리의 현재 상태로 유지.
git restore --staged|-S [filename]
|
- 스테이징과 작업 디렉토리 모두에서 변경사항 취소
1
2
3
4
5
| # 작업 디렉토리와 인덱스 모두에서 변경사항을 취소하고, 마지막 커밋 상태로 복원한다.
git restore --source|-s [HashCode] [filename]
git restore --source|-s [HEAD~1] [filename]
# 프로젝트의 모든 파일을 특정 커밋 상태로 복원
git restore --source|-s [HashCode]
|
주의 사항
git restore
은 워킹 디렉토리와 인덱스의 상태를 변경하므로, 저장되지 않은 변경 사항이 있는 경우 작업이 손실될 수 있다. (예를 들어, 수정한 a 파일을 저장하지 않고 있다가 a 파일을 restore 하게 되면 수정한 내역이 모두 손실된다.)
git reset
- 인덱스 또는 워킹 디렉토리의 상태를 되돌릴 때 사용하는 명령어이다.
- 세 가지 주요 모드로 작동하며, 각각의 모드에 따라 커밋 기록, 스테이징된 파일, 작업 디렉토리에 미치는 영향이 다르다.
- 주로 최근 커밋을 취소하거나 스테이징된 파일을 언스테이지할 때 사용된다.
주요 사용 사례
- –soft : HEAD를 지정한 커밋으로 이동시키며, 인덱스와 워킹 디렉토리의 변경 사항은 유지된다. 즉, 해당 커밋 이후의 모든 변경사항을 인덱스 상태로 유지한다.
1
2
3
4
| # 최근 커밋을 취소하고, 그 변경 사항을 다시 스테이징하고 싶을 때
git reset --soft [HashCode]
# 마지막 커밋 취소하고 변경사항 스테이징
git reset --soft HEAD~1
|
- –mixed(기본값) : HEAD를 지정한 커밋으로 이동시키며, 인덱스를 초기화하지만, 워킹 디렉토리의 변경 사항은 유지한다. 즉, 변경사항은 unstage 되지만, 파일은 수정된 상태로 남는다.
1
2
3
4
| # 특정 커밋 이후의 모든 변경 사항을 언스테이지하고, 작업 디렉토리에 남겨두고 싶을 때
git reset --mixed [HashCode]
# 스테이징된 파일을 언스테이지하고 워킹 디렉토리에 변경 사항을 남겨두고 싶을 때
git reset HEAD [filename]
|
- –hard : HEAD를 지정한 커밋으로 이동시켜, 인덱스와 워킹 디렉토리의 모든 변경 사항을 취소한다. 즉, 지정한 커밋 상태로 완전히 되돌아가며, 취소된 변경 사항은 복구할 수 없다.
1
2
3
4
5
| # 특정 커밋 이후의 모든 변경 사항을 완전히 삭제하고,
# 해당 커밋의 상태로 워킹 디렉토리와 인덱스를 되돌리고 싶을 때
git reset --hard [HashCode]
# 현재 작업 중인 모든 변경 사항을 완전히 취소하고, 특정 커밋 상태로 돌아가고 싶을 때
git reset --hard HEAD
|
주의 사항
- hard 옵션으로 reset 후, push 할 때
- 로컬 저장소의 커밋 히스토리가 원격 저장소의 커밋 히스토리보다 뒤에 있어(non-fast-forward)인 경우, 푸시를 시도하면 오류가 발생한다.
- 이런 상황에서는
push -f
옵션을 사용하여 강제로 덮어써야 한다. 하지만 이는 원격 저장소의 커밋 히스토리를 재작성하여 다른 사람의 작업 내용을 덮어쓸 수 있으므로 매우 신중히 사용해야 한다.
- reset을 원격저장소에 사용할 때
- 협업 중인 브랜치에서는 reset을 사용할 때 주의해야 한다. 특히
--hard
옵션을 사용하는 경우, 해당 브랜치에서 삭제될 커밋에 다른 사람이 작성한 커밋이 포함될 수 있다. - 또한, 삭제될 커밋들이 이미 다른 사람의 로컬 저장소로 전파된 경우, 그것들은 여전히 해당 커밋을 가지고 있을 것이다. 이는 협업 시 혼란을 초래할 수 있다.
- 따라서, reset은 혼자 사용하는 브랜치에서만 사용!하거나, 다른 사람들이 해당 브랜치를 pull 해가지 않았다는 것이 확인된 경우에만 사용하는 것이 안전하다.
git revert
- reset과 다르게 특정 커밋의 변경 사항을 되돌리는 새로운 커밋을 생성하는 명령어이다.
- 기존 커밋 히스토리를 유지하면서 변경 사항을 취소하기 때문에 협업 환경에서 안전하게 사용할 수 있다.
- 커밋 자체를 삭제하는 것이 아니라, 지정된 커밋을 “역으로” 적용하여 그 커밋의 영향을 취소하는 방식으로 동작한다.
- 되돌리는 과정에서 변경 사항이 현재 코드와 충돌할 수 있다. 이 경우, git은 충돌을 해결할 수 있도록 사용자에게 알리며, 사용자는 수동으로 충돌을 해결한 후
git revert --continue
명령을 사용해 되돌리기 가정을 완료할 수 있다. - 원격 저장소에 푸시된 커밋을 되돌릴 대 적합하다. 커밋을 삭제하지 않고 새로운 커밋을 생성하기 때문에, 협업 중인 다른 개발자들의 작업에 영향을 최소화할 수 있다.
- history 관리 차원에서
reset
보다는 revert
를 쓰도록 하자! 😊
주요 사용 사례
- 커밋 되돌리기
1
2
3
| # 특정 커밋을 선택하여 그 커밋의 변경 사항을 취소하는 새로운 커밋을 만든다.
# [HashCode]의 변경사항을 되돌리는 새로운 커밋을 생성한다.
git revert [HashCode]
|
- 여러 커밋 되돌리기
1
2
3
4
5
| # 연속된 여러 커밋을 되돌리고 싶을 때, 시작 커밋과 끝 커밋을 지정한다.
# 지정된 범위의 커밋을 각각 개별적으로 되돌리는 것이다.
git revert [시작커밋]..[끝 커밋]
# 마지막 세 개의 커밋을 역순으로 하나씩 되돌리는 새로운 커밋을 생성한다.
git revert HEAD~3..HEAD
|
주의 사항
- 병합 커밋 되돌리기 : 병합 커밋을 되돌릴 때는 주의가 필요하다. 병합 커밋을 되돌릴 경우, 어떤 부모 커밋을 기준으로 되돌릴지를 지정해야 하며, 상황에 따라
-m
옵션이 필요할 수 있다. - 충돌 발생 가능성 : 되돌리는 과정에서 코드가 현재 워킹 디렉토리와 충돌할 수 있으므로, 충돌 해결 능력이 필요하다.