Codesigner

[Git] Git Advanced - Rebase -i 활용법 본문

Git /git

[Git] Git Advanced - Rebase -i 활용법

eunsukimme 2019. 4. 16. 19:22

저번 포스팅에서 rebase 가 무엇이고, 어떻게 활용하는지 알아보았다. 간단하게 정리해보자면, 브랜치를 병합하는 merge와는 또 다른 방법으로 각 커밋들의 변경 사항들을 차례대로 basebranch에 반영하여 결과적으로 선형적인 커밋 히스토리를 만들어 내는 기능이었다. 이번 포스팅에서는 이러한 작업을 대화형(interactive) 인터페이스를 제공함으로써 좀 더 정교한 작업을 가능하게 해주는 -i 옵션에 대해 알아보도록 하자

 

 

 

git rebase -i

 

git-scm에서는 -i 옵션에 대해 다음과 같이 설명하고 있다

 

Make a list of the commits which are about to be rebased. Let the user edit that list before rebasing. This mode can also be used to split commits

 

직역하면 rebase 될 커밋들의 리스트를 사용자가 편집할 수 있게 해 준다는 뜻이다. 아직 그 개념이 잘 잡히지 않는데, 다음과 같은 Git 환경을 만들고 실습하면서 더 자세히 알아보도록 하자. 현재 Git 프로젝트의 커밋 히스토리는 다음과 같다

 

<그림 1> 현재 커밋 히스토리

 

이제 이 커밋 리스트를 rebase 해보도록 하자. 이때 rebase 할 커밋의 범위를 지정해야 하는데, 이를 일반화한 명령은 다음과 같다

git rebase -i <작업할 커밋의 직전커밋>

이 말의 의미는 git rebase -i 의 파라미터로 주어진 커밋의 바로 다음 커밋부터 HEAD까지 rebase를 진행한다는 뜻이다. 만약 HEAD~3을 파라미터로 준다면 HEAD~2, HEAD~1, HEAD를 rebase 하겠다는 의미이다. HEAD~3을 파라미터로 준 결과 다음과 같이 편집기가 실행된다

 

<그림 2> git rebase todo

편집기의 상단에 rebase할 커밋들의 리스트가 나타난다. 아래에는 주석으로 몇 가지 명령어들이 나열돼 있는데, 이 명령들을 활용하여 커밋들을 자신의 입맛대로 rebase 할 수 있다. 지금부터 각 명령어들의 기능이 무엇인지 알아보도록 하자

 

 

 

pick

 

pick 은 고른다, 선택한다는 의미로 해당 커밋을 히스토리에 사용하겠다는 말이다. 현재 기본적으로 모든 커밋들이 pick 된 상태로 히스토리에 존재하고 있다

 

<그림 3> 현재 커밋 리스트

 

이 상태에서 커밋의 순서를 바꿀 수도 있고, 해쉬값을 이용하여 커밋을 가져올 수도 있다. 2번째(Add apple)와 3번째(Add cherry) 커밋을 바꿔보도록 하자

 

<그림 4> banana와 cherry 가 바뀐 커밋 리스트

 

저장하고 종료한 뒤 로그를 살펴보면 변경된 내용으로 커밋 히스토리가 작성되어 있는 걸 확인할 수 있다

 

<그림 5> 순서가 바뀐 커밋 히스토리

 

<그림 1>과 비교해서 보면, banana와 cherry의 커밋 해쉬값이 둘 다 변경된 걸 확인할 수 있다. 이는 내용은 같지만 완전히 새로운 커밋이란 걸 의미한다. 앞에서 커밋의 해쉬값을 이용해 커밋을 가져올 수 있다고 하였는데, 이를 테스트하기 위해 새로운 커밋을 작성하도록 하자

 

<그림 6> 새로운 커밋이 추가된 커밋 히스토리

 

지금 필자가 하고자 하는 것은 방금 생성한 커밋을 git reset 명령으로 되돌린 뒤 rebase의 todo 리스트에 해당 커밋 해쉬값을 가져와서 히스토리에 추가하는 것이다

git reset --hard HEAD~

로그를 다시 확인해보면 방금 생성한 커밋이 삭제된 걸 확인할 수 있다. 이제 git rebase -i HEAD~3 명령으로 편집창을 실행시키자. 조금 전에 생성한 커밋의 해쉬값은 d3816e0 이므로 이를 pick 하는 줄을 추가하도록 하자(해쉬값이 기억이 안 난다면 git reflog 명령으로 확인할 수 있다)

 

<그림 7> dragonFruit를 추가한 커밋 리스트

 

자, 이제 다시 로그를 확인해보면 삭제했던 커밋이 히스토리에 추가된 것을 확인할 수 있다(우와)! 또한 ls 명령으로 working directory를 확인해보면 해당 커밋의 작업 내용이 추가된 것도 확인할 수 있다

 

<그림 8> dragonFruit가 추가된 커밋 히스토리

 

rebase는 이런 강력한 기능을 제공한다... 안 쓰고는 못 배기지 않겠는가?

 

 

 

reword

 

reword는 커밋의 메시지를 변경하는 명령이다. 메시지를 변경할 커밋의 해쉬값 앞에 reword 키워드를 적어주면 해당 커밋의 메시지를 다시 작성하는 창이 열린다. 여기서는 apple의 메시지를 변경해보도록 하자

 

<그림 9> apple의 명령어를 reword 로 바꾼 커밋 리스트

 

저장하고 종료하면 apple 커밋의 메시지를 변경할 수 있는 편집창이 열린다. 메시지를 변경하고 저장하면 커밋 히스토리에서 변경된 커밋 메시지를 확인할 수 있다

 

<그림 10> apple 커밋의 메시지가 변경된 커밋 히스토리

 

단순히 HEAD 커밋의 메시지를 변경하고 싶으면 git commit --amend 명령을 사용하면 된다

 

 

 

edit

 

reword 가 메시지만 변경하는 작업이었다면 edit 은 커밋의 작업한 내용까지 변경할 수 있는 명령이다. 나의 경우 git rebase -i HEAD~4 명령을 실행한 후 dragonFruit 커밋의 명령을 edit으로 바꿔주었다

 

<그림 11> dragonFruit 커밋을 edit 명령하는 커밋 리스트

 

저장하고 종료하면 해당 커밋으로 checkout 되고, 작업을 수행할 수 있게 된다. 나의 경우 dragonFruit.txt를 삭제하고 donut.txt를 추가해 주었다

 

<그림 12> dragonFruit.txt 를 삭제하고 donut.txt 를 추가한 상태

 

그리고 다음 명령을 실행하여 커밋을 완료한다

git add .
git commit --amend

커밋 메시지도 변경하고 싶다면 마지막 명령 이후에 열리는 편집창에서 변경하면 된다. 나의 경우는 Add donut으로 메시지를 변경해 주었다. rebase를 계속하려면 다음 명령을 실행한다

git rebase --continue

커밋 히스토리를 확인해보면 다음과 같다

 

<그림 13> dragonFruit 커밋이 donut 커밋으로 변경된 커밋 히스토리

 

donut 메시지의 커밋이 dragonFruit 대신 히스토리에 나타나는 것을 확인할 수 있다

 

 

 

squash

 

squash는 해당 커밋을 이전의 커밋과 하나로 합치는 명령이다. 즉, 여기서 donut 커밋에 squash 명령을 적어주면 이전 커밋인 cherry와 하나로 합쳐진다

 

<그림 14> donut 커밋을 squash 하는 커밋 리스트

 

저장하고 종료하면 커밋 메시지를 변경할 수 있는 편집창이 나타난다. 그대로 저장하면 기존의 메시지가 그대로 하나의 커밋으로 히스토리에 나타나는 것을 확인할 수 있다

 

<그림 15> cherry와 donut의 커밋이 합쳐진 커밋 히스토리

 

 

 

fixup

 

fixup은 squash와 동일하게 이전 커밋과 합치는 명령이다 squash와 다른 점은 메시지는 합치지 않는다는 것으로, 이전 커밋 메시지만 히스토리에 남게 된다. 이 점을 제외하곤 squash와 동일하다

 

 

 

exec

 

exec 명령은 각 커밋이 rebase 된 후 실행될 쉘 명령을 지정할 수 있다. 필자의 경우 다음과 같이 각 커밋 리스트 사이에 exec 명령을 추가해서 커밋의 해쉬값과 메시지를 echo 하게 하였다

 

 

<그림 16> 각 커밋에 exec 명령을 추가한 커밋 리스트

 

저장하고 종료하면 다음과 같은 결과를 출력한다

 

<그림 17> rebase 되면서 exec 명령이 실행된다

 

drop

 

drop 은 커밋 히스토리에서 해당 커밋을 삭제하는 명령이다. 필자와 같이 편집창에서 커밋 앞에 drop 키워드를 적어주면 해당 커밋은 rebase 된 히스토리에 포함되지 않는다

 

<그림 18> drop 명령을 적어준 커밋 리스트

 

저장하고 종료한 뒤 커밋 히스토리를 보면 해당 커밋이 삭제된 걸 확인할 수 있다

 

<그림 19> cherry 커밋이 삭제된 커밋 히스토리

 

사실 커밋 리스트 편집창에서 한 줄을 지워줘도 같은 기능을 한다

 

 

 

Generalizations

 

지금까지 git rebase -i 를 활용하는 방법과 관련된 명령어들을 알아보았다. 배운 내용들을 정리하면 다음과 같다

 

  • git rebase -i 는 rebase 될 커밋 리스트를 직접 편집할 수 있게 해주는 기능이다
  • git rebase -i commit_sha - 주어진 해쉬값의 커밋 이후부터 HEAD까지 rebase 한다
  • git commit --amend - HEAD 커밋을 수정한다. 또한 rebase 중 커밋의 편집을 완료한다
  • git rebase --continue - 커밋을 편집한 뒤 rebase를 계속한다

 

우리는 또한 커밋 리스트를 편집하는 여러 명령어들을 알아보았는데, 각 명령어들의 기능은 다음과 같다

 

  • pick - 해당 커밋을 히스토리에 넣는다

  • reword - 해당 커밋의 메시지를 변경한다

  • edit - 해당 커밋의 메시지와 작업 내용을 변경한다

  • squash - 해당 커밋을 이전 커밋과 하나로 합친다

  • fixup - 해당 커밋을 이전 커밋과 하나로 합친다. 단, 메시지는 이전 커밋의 메시지로 합쳐진다

  • exec - 쉘 명령을 실행한다

  • drop - 커밋을 히스토리에서 삭제한다

 

 

처음에는 rebase의 개념이 잘 잡히지 않고 사용하기도 꺼려지지만, rebase에 익숙해지면 merge보다 더 자주 사용하게 될 것이다. 또한 로컬에서 작업하고  커밋 히스토리를 정리하고 싶을 때 rebase가 유용하게 쓰이고, -i 옵션을 이용해서 원하는 작업을 수행할 수 있을 것이다

 

 

 

Comments