git은 HEAD
가 변경될 때마다의 기록을 가지고 있다.
가령 commit1에서 commit2를 새로 만들면 HEAD의 위치가 commit1에서 commit2로 바뀌기 때문에 다음과 같이 reflog에 기록으로 남는다.
Commit 1 - New commit
Commit 2 - New commit
만약, feature
branch에 있는 commit8
으로 옮기고 싶다면 git switch
나 git checkout
을 쓰게 될 것이다. 이러한 경우도 HEAD가 변경되므로 다음의 결과가 기록된다.
Commit 1 - New commit
Commit 2 - New commit
Commit 8 - Switched to feature from main
이러한 기록을 reflog
로 볼 수 있는 것이다.
실제로 .git
디렉터리에 들어가면 .git/logs/HEAD
file이 있는데, 이 정보들이 바로 해당 정보이다.
demo directory를 만들어서 실제로 변동 사항을 봐보자.
mkdir demo && cd ./demo
git init
git switch -c main
echo "main commi1" >> ./README.md
git add ./README.md
git commit -m "commit1"
.git/logs/HEAD
에 들어가보도록 하자.
cat .git/logs/HEAD
0000000000000000000000000000000000000000 e05e4dd8a71bfb50d84c06276c072c91735e8dc2 colt <colt@colt.com> 1737543870 +0900 commit (initial): commit1
HEAD가 움직인 기록이 바로 여기에 저장되는 것이다.
다음으로 commit을 하나 더 만들어보도록 하자.
echo "commit2" >> ./README.md
git add ./README.md
git commit -m "commit2"
commit을 하나 더 만들었다면 HEAD가 commit2
로 이동했을 것이다.
cat .git/logs/HEAD
0000000000000000000000000000000000000000 e05e4dd8a71bfb50d84c06276c072c91735e8dc2 colt <colt@colt.com> 1737543870 +0900 commit (initial): commit1
e05e4dd8a71bfb50d84c06276c072c91735e8dc2 50b34324e453232bd07508c026fcb09ef36b2198 colt <colt@colt.com> 1737543936 +0900 commit: commit2
다음으로 branch를 하나 더 만들고 commit을 추가해보도록 하자.
git switch -c feature
echo "feature1" >> ./README.md
git add ./README.md
git commit -m "feature commit1"
echo "feature2" >> ./README.md
git add ./README.md
git commit -m "feature commit2"
.git/logs/HEAD
를 확인해보도록 하자.
cat .git/logs/HEAD
0000000000000000000000000000000000000000 e05e4dd8a71bfb50d84c06276c072c91735e8dc2 colt <colt@colt.com> 1737543870 +0900 commit (initial): commit1
e05e4dd8a71bfb50d84c06276c072c91735e8dc2 50b34324e453232bd07508c026fcb09ef36b2198 colt <colt@colt.com> 1737543936 +0900 commit: commit2
50b34324e453232bd07508c026fcb09ef36b2198 50b34324e453232bd07508c026fcb09ef36b2198 colt <colt@colt.com> 1737544057 +0900 checkout: moving from main to feature
50b34324e453232bd07508c026fcb09ef36b2198 f83b7e83bc3371b00f77878e3528b819a040e2eb colt <colt@colt.com> 1737544057 +0900 commit: feature commit1
f83b7e83bc3371b00f77878e3528b819a040e2eb 0c7d6e423f1b94bee1a1308b24856a7a63b06856 colt <colt@colt.com> 1737544058 +0900 commit: feature commit2
branch
를 이동한 것부터 해서 2개의 feature commit을 만든 기록이 남는다. 이것들 모두가 HEAD가 움직였기 때문에 기록이 남는 것이다.
단, 중요한 점은 reflog는 오직 local에만 적용이 된다는 것이다. 나랑 같은 github repo를 사용하고 있다고 해도 reflog
는 서로 다르다. 각자가 어떻게 HEAD를 움직였냐에 따라 다르기 때문이다. 또한, 영구적이지 않다. 90일이 지나면 만료된다는 특징이 있다. 오래된 항목은 제거되는 것이다.
git reglog 관련 명령어로 아래의 예시들 말고는 딱히 쓰일 일도 없고, docs에서도 다른 명령어를 쓰는 것을 추천하지도 않는다.
git reflog show HEAD
우리의 실습 예제에서 실행하면 다음과 같이 결과가 나온다.
git reflog show HEAD
0c7d6e4 (HEAD -> feature) HEAD@{0}: commit: feature commit2
f83b7e8 HEAD@{1}: commit: feature commit1
50b3432 (main) HEAD@{2}: checkout: moving from main to feature
50b3432 (main) HEAD@{3}: commit: commit2
e05e4dd HEAD@{4}: commit (initial): commit1
위가 가장 최근의 작업이고, 아래가 가장 오래전 작업들이다.
git log
와 달리 checkout 기록도 남아있다. 이는 reset, revert, rebase 등의 기록도 남는다는 것이다. 단, git log는 commit위주로 기록되기 때문에 해당 명령어들이 사용되어 commit이 수정, 삭제 될 수 있다.
단, HEAD@{}
안에 있는 숫자들은 고정적이지 않다. 계속해서 작업이 수행되고 HEAD가 바뀌면, 가장 최근 작업에 대해서 HEAD가 0인 것이고 나머지들은 기존보다 +가 되어 값이 커진다.
HEAD@{}
을 사용해서 특정 reference를 지정할 수도 있다.
git reflog show HEAD@{2}
50b3432 (main) HEAD@{2}: checkout: moving from main to feature
50b3432 (main) HEAD@{3}: commit: commit2
e05e4dd HEAD@{4}: commit (initial): commit1
2부터의 HEAD 이동 history들이 나온다.
reflog를 통해서 강력한 rollback을 진행할 수 있다.
먼저, 현재까지 진행했던 project를 정리하자.
git branch
* feature
main
git switch main
Switched to branch 'main'
git log --oneline
50b3432 (HEAD -> main) commit2
e05e4dd commit1
2개의 commit으로 이루어진 상태이다.
다음으로 veggies.txt
라는 file을 만들어보도록 하자.
echo Broccoli >> ./veggies.txt
git add ./veggies.txt
git commit -m "commit3 add veggies.txt"
그런데 추가할 야채들이 더 생겼다면? 다음과 같이 쓸 수 있다.
echo Tomato >> ./veggies.txt
echo Carrot >> ./veggies.txt
echo Kale >> ./veggies.txt
git add ./veggies.txt
git commit -am "commit4 add new veggies.txt"
다음과 같이 나온다.
git log --oneline
8b7959d (HEAD -> main) commit4 add new veggies.txt
c743eb0 commit3 add veggies.txt
50b3432 commit2
e05e4dd commit1
그런데, 생각해보니 새로운 야채가 필요없을 것 같다는 생각이 들어서 reset
을 시켜보기로 했다.
git reset --hard c743eb0
이제 Tomato
, Carrot
, Kale
이 사라졌을 것이다.
Broccoli
그런데, 다시 생각해보니 그 야채들을 넣어도 될 것 같아서 보여니까, commit log에 없다.
git log --oneline
c743eb0 (HEAD -> main) commit3 add veggies.txt
50b3432 commit2
e05e4dd commit1
이러한 경우 git reflog
로 rollback을 할 수 있다.
git reflog
c743eb0 (HEAD -> main) HEAD@{0}: reset: moving to c743eb0
8b7959d HEAD@{1}: commit: commit4 add new veggies.txt
c743eb0 (HEAD -> main) HEAD@{2}: commit: commit3 add veggies.txt
왜 git reflog에는 commit: commit4 add new veggies.txt
가 남아있냐면 HEAD가 이동했었던 기록이었기 때문이다.
git checkout
으로 새로운 야채들이 있던 8b7959d
commit으로 원상복구 시켜보도록 하자.
git checkout 8b7959d
HEAD is now at 8b7959d commit4 add new veggies.txt
veggies를 확인해보자.
Broccoli
Tomato
Carrot
Kale
원상복구 된 것을 볼 수 있다. 다음으로 git log
를 확인해보도록 하자.
git log --oneline
8b7959d (HEAD) commit4 add new veggies.txt
c743eb0 (main) commit3 add veggies.txt
50b3432 commit2
e05e4dd commit1
HEAD
가 main
과 분리된 것을 볼 수 있다. 이는 새로운 branch로 이동했다는 것이다. git branch
를 보도록 하자.
git branch
* (HEAD detached at 8b7959d)
feature
main
git checkout
으로 이동하는 방법은 좋은 방법이 아니다. 다시 main
branch로 와보도록 하자.
git switch main
Warning: you are leaving 1 commit behind, not connected to
any of your branches:
8b7959d commit4 add new veggies.txt
Switched to branch 'main'
git log --oneline
c743eb0 (HEAD -> main) commit3 add veggies.txt
50b3432 commit2
e05e4dd commit1
이전의 새로운 야채가 없던 상태로 원상복구 되었다.
다시 git reflog
를 사용하여 새로운 야체가 있던 때로 되돌아가고 HEAD
도 main
과 맞춰주기로 하자.
git reflog
c743eb0 (HEAD -> main) HEAD@{0}: checkout: moving from 8b7959d64ea147e8cfedc6822e2cdcb8c27075eb to main
8b7959d HEAD@{1}: checkout: moving from main to 8b7959d
c743eb0 (HEAD -> main) HEAD@{2}: reset: moving to c743eb0
8b7959d HEAD@{3}: commit: commit4 add new veggies.txt
c743eb0 (HEAD -> main) HEAD@{4}: commit: commit3 add veggies.txt
50b3432 HEAD@{5}: reset: moving to 50b3432
HEAD@{3}
이 부분의 reflog로 reset을 해주면 된다.
git reset --hard HEAD@{3}
HEAD is now at 8b7959d commit4 add new veggies.txt
--hard
를 써주어야 working directory에도 반영이 된다.
이제 git log로 현재 HEAD과 main이 붙어있는 지 확인해보도록 하자.
git log --oneline
8b7959d (HEAD -> main) commit4 add new veggies.txt
c743eb0 commit3 add veggies.txt
50b3432 commit2
e05e4dd commit1
잘 붙어있는 것을 볼 수 있다. 따라서 reflog로 특정 작업을 rollback하려면 다음의 과정을 거쳐야하는 것이다.
1. git reflog
로 특정 reference확인
2. reference로 git reset --hard
사용
3. 현재 branch와 HEAD가 동일한 지 확인(git log --oneline
)
git reflog를 통해서 rebase를 취소할 수도 있다.
rebase를 위해서 새로운 branch인 flowers
를 만들자
git switch -c flowers
다음의 파일을 추가해주도록 하자.
echo "Salmon Zinnias" >> ./flowers.txt
echo "Queen Lime Zinnias" >> ./flowers.txt
echo "Old Rose Dahlias" >> ./flowers.txt
git add ./flowers.txt
git commit -m "flowers commit1"
echo "Coral Fountain Amaranth" >> ./flowers.txt
git add ./flowers.txt
git commit -m "flowers commit2"
echo "Hot Biscuits Amaranth" >> ./flowers.txt
git add ./flowers.txt
git commit -m "flowers commit3"
echo "Coral Reaf Celosia" >> ./flowers.txt
git add ./flowers.txt
git commit -m "flowers commit4"
git log
를 확인해보도록 하자.
git log --oneline
24d68d9 (HEAD -> flowers) flowers commit4
e271dad flowers commit3
4f75660 flowers commit2
e7d8c65 flowers commit1
795137b flowers commit3
a398041 flowers commit2
ae99cc6 flowers commit1
8b7959d (main) commit4 add new veggies.txt
c743eb0 commit3 add veggies.txt
50b3432 commit2
e05e4dd commit1
interactive rebase를 사용해서 특정 commit을 없애도록 하자.
git rebase -i HEAD~4
다음과 같은 text editor가 나오게 된다.
pick e7d8c65 flowers commit1
pick 4f75660 flowers commit2
pick e271dad flowers commit3
pick 24d68d9 flowers commit4
# Rebase 795137b..24d68d9 onto 24d68d9 (4 commands)
#
# Commands:
다음과 같이 변경하도록 하자.
pick e7d8c65 flowers commit1
fixup 4f75660 flowers commit2
fixup e271dad flowers commit3
fixup 24d68d9 flowers commit4
# Rebase 795137b..24d68d9 onto 24d68d9 (4 commands)
#
# Commands:
fixup
을 했으니, 해당 commit과 내용들은 사라지지 않고 flowers commit1
에 들어가게 된다. :wq!
를 누르고 저장하도록 하자.
git log
를 확인해보도록 하자.
7c63f6b (HEAD -> flowers) flowers commit1
8b7959d (main) commit4 add new veggies.txt
c743eb0 commit3 add veggies.txt
50b3432 commit2
e05e4dd commit1
3개의 commit들이 flowers commit1
하나로 뭉쳐진 것을 볼 수 있다. flowers.txt
파일을 확인해보자.
Salmon Zinnias
Queen Lime Zinnias
Old Rose Dahlias
Coral Fountain Amaranth
Hot Biscuits Amaranth
Coral Reaf Celosia
내용은 그대로 있다.
그런데, 다시 해당 commit들을 되살려야할 때가 있다. 이를 위해서는 git rebase
를 취소해야하는데, git reflog
가 rebase를 rollback할 수 있다.
git reflog
7c63f6b (HEAD -> flowers) HEAD@{0}: rebase -i (finish): returning to refs/heads/flowers
7c63f6b (HEAD -> flowers) HEAD@{1}: rebase -i (fixup): flowers commit1
afedcff HEAD@{2}: rebase -i (fixup): # This is a combination of 3 commits.
e529277 HEAD@{3}: rebase -i (fixup): # This is a combination of 2 commits.
e731214 HEAD@{4}: rebase -i (start): checkout HEAD~4
fddb21b HEAD@{5}: commit: flowers commit4
b2a5cf7 HEAD@{6}: commit: flowers commit3
38da9f9 HEAD@{7}: commit: flowers commit2
e731214 HEAD@{8}: commit: flowers commit1
8b7959d (main) HEAD@{9}: checkout: moving from main to flowers
rebase
를 시작하기 전 HEAD@{5}
로 돌아가면 된다. fddb21b
을 사용하여 git reset --hard
를 사용하자.
git reset --hard fddb21b
git log로 현재의 commit상태를 확인해보도록 하자.
git log --oneline
fddb21b (HEAD -> flowers) flowers commit4
b2a5cf7 flowers commit3
38da9f9 flowers commit2
e731214 flowers commit1
8b7959d (main) commit4 add new veggies.txt
c743eb0 commit3 add veggies.txt
50b3432 commit2
e05e4dd commit1
원상 복구가 된 것을 볼 수 있다.
이렇게 rebase를 통해서 사라진 commit들이나 수정된 commit들을 git reflog
를 통해서 특정 시점으로 rollback이 가능하다.