#git amend - 커밋 객체를 새로 만들어 히스토리를 조작하는 터프한 명령
amend는 약간 수정한다는 뜻. 소심하게 보인다. 하지만 터프해. git 히스토리(history)를 조작한다. git은 히스토리 조작이 범죄가 아니다. 염려하지 않아도 된다. 다만 다른 버전 컨트롤 시스템에서는 금지하는 영역에 손을 담근 것이기에 아주 조심해야 한다.
$ vim hello.txt
$ git add hello.txt
$ git commit -m "bad commit"
[master 7fa9157] bad commit
1 file changed, 1 insertion(+), 1 deletion(-)
잘못 커밋했다. 크게 잘못한 건 아니고 조금만 고치면 된다. 그래서 amend
옵션을 사용하려고 한다.
$ git commit --amend -m "good commit(amend)"
[master ab1d543] good commit(amend)
주석만 수정했다. 옵션만 붙이면 되니 조금만 싹 바꾸는 것 같다. 하지만 조금만 바뀌는 게 아니다. 커밋 객체(commit object)를 새로 만든다. 이전 커밋 객체 id는 7fa9157
이고 amend한 커밋 객체 id는 ab1d543
이다. HEAD와 같은 ref가 아닌 git database에 저장하는 객체는 다 변하지 않는다(immutable).
커밋 객체를 새로 만들면 이전 커밋 객체에 포함된 정보를 복사해야 한다. amend
옵션이 이 과정을 대신 해준다. 그래서 편하게 조금만 수정할 수 있다.
$ git cat-file -p 7fa9157
...
parent 28cfd2f3a22405fcde5cdcba0f006544cc92b341
...
$ git cat-file -p ab1d543
...
parent 28cfd2f3a22405fcde5cdcba0f006544cc92b341
...
$ cat .git/HEAD
ref: refs/heads/master
$ cat .git/refs/heads/master
ab1d543a7bb350271b513e0a0abf5798f5edb9d8
amend 이전 이후 커밋 객체는 모두 git database에 저장된다. amend 이전 커밋 객체가 사라지는 게 아니다. 그리고 부모 커밋 객체는 같다. HEAD는 amend한 커밋 객체를 가리킨다.
$ git push
$ vim hello.txt
$ git commit --amend ...
$ git push
만약 이렇게 리모트 저장소에 푸시(push)한 후에 amend를 한다면 어떻게 될까?
$ git status
# On branch master
# Your branch and 'origin/master' have diverged,
# and have 1 and 1 different commit each, respectively.
# (use "git pull" to merge the remote branch into yours)
#
nothing to commit, working directory clean
머지(merge)가 필요한 상태가 된다. 공통 부모는 28cfd2f
로 같지만 서로 다른 커밋 객체를 HEAD가 가리킨다.
$ git branch amend-fix
$ git reset --hard HEAD~1
$ git pull
이렇게 하면 amend 이전 커밋 객체가 복구된다.
$ git merge amend-fix
$ git commit -m "merged"
$ git log
commit 2bd07f74c53f730f41c8e61d414d319004c6f66e
Merge: 7fa9157 ab1d543
...
merged
commit ab1d543a7bb350271b513e0a0abf5798f5edb9d8
...
good commit(amend)
commit 7fa9157c52955ce5db1d93337d18312cb43107e7
...
bad commit
이렇게 머지해야 한다. amend가 amend가 아니여. 푸시 이후 amend에 미련을 가지지 말자. push --force
이거 했다간 1년 동안 갈굼 당한다.
git config --system receive.denyNonFastForwards true
참고로 중앙 저장소에서 설정하면 force
를 막을 수 있다.