Rebase 활용한 특정 커밋 수정/제거

Dong In Kim·2021년 4월 15일
2

Git은 기록(커밋)을 변경하는 도구를 제공하진 않지만, rebase를 이용하는 방법은 제시하고 있다.

rebase의 주목적은 커밋 기록을 선형으로 만들기 위함.

1. 개념


  1. 몇몇 커밋들을 본래 베이스였던 HEAD(현재 git이 바라보는 상태)로 rebase 실행
    • 베이스 커밋은 변경을 원하는 커밋들 중 가장 오래된 커밋보다 이전의 커밋으로 지정
    • interactive 모드 활용 → 수정 대상 커밋의 생성 직후 시점에 멈춰서 작업하기 위함
  2. 수정 대상 커밋의 생성 직후 시점에 멈춘 상태에서 파일을 추가하거나 커밋을 제거하는 등 원하는만큼 변경
  3. rebase 마무리

[수정 예시]

6개(C1~C6) 커밋이 있고, C4 커밋을 수정하는 과정은 아래와 같다.

  1. C3로 베이스로, C4에 대한 작업을 edit로 지정해서 rebase를 실행
  2. HEAD가 C4 커밋 시점으로 이동
  3. C4 커밋을 취소
  4. C4 커밋에 포함되어 있던(staging 되어있는) 변경사항을 수정하여 커밋 생성 (새로운 커밋 C7)
  5. ...
  6. HEAD를 이동하며 기존 커밋들을 재생성 (새로운 커밋 C5, C6)
  7. ...
  8. rebase 마무리 (가장 최근 시점의 커밋에 위치한 HEAD 확인)

2. Git 명령어 활용한 예제


예제를 위한 선행 커밋 상태
(예제 github: https://github.com/owljoa/git-study)

순서commit hash기본값
1f90bb27add file1.txt
20640343add file2.txt
38180ad4add file3.txt
4f2cb8d1emodify file1.txt
55de25f75modify file2.txt, file3.txt
641420152modify file2.txt

4번 커밋 수정사항

# 4번 커밋 이전 file1.txt
[빈 파일]

# 4번 커밋 이후 file1.txt
I'm file1.txt!

1. 특정 커밋 제거

- 목표: file1.txt를 수정한 4번 커밋 기록 지우기

  1. 베이스 커밋을 4번 커밋의 바로 이전 커밋인 3번 커밋으로 지정하여 interactive 모드로 rebase 시작

    git rebase -i 8180ad4
    
    or
    
    git rebase --interactive 8180ad4
  2. 지정한 베이스 커밋 이후 커밋들에 어떤 타입의 작업을 할지 명령어(command)를 지정하는 vi 에디터가 열림

    1. 4번 커밋(f2cb8d1e)을 제거하는 것이 목적이므로 해당 커밋의 작업 타입을 pick → drop으로 변경
    2. 저장 후 종료
    # 베이스 커밋 이후에 쌓여있던 커밋들에 대해 실행할 명령어 결정 (명령어가 작업 종류를 결정함)
    # pick f2cb8d1 modify file1.txt -> **drop** f2cb8d1 modify file1.txt
    drop f2cb8d1 modify file1.txt
    pick 5de25f7 modify file2.txt, file3.txt
    pick 4142015 modify file2.txt again
    
    # Rebase 8180ad4..4142015 onto 8180ad4 (3 commands)
    #
    # Commands:
    # p, pick <commit> = use commit -> 해당 커밋을 그대로 사용
    # r, reword <commit> = use commit, but edit the commit message     # e, edit <commit> = use commit, but stop for amending -> 커밋을 사용하되, 변경을 위해 해당 커밋 이후 시점에 멈춤
    # s, squash <commit> = use commit, but meld into previous commit
    # f, fixup <commit> = like "squash", but discard this commit's log message
    # x, exec <command> = run command (the rest of the line) using shell 
    # b, break = stop here (continue rebase later with 'git rebase --continue')
    # d, drop <commit> = remove commit -> 해당 커밋 제거
    # l, label <label> = label current HEAD with a name
    # t, reset <label> = reset HEAD to a label
    # m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
    # .       create a merge commit using the original merge commit's
    # .       message (or the oneline, if no original merge commit was
    # .       specified). Use -c <commit> to reword the commit message.
    #
    # These lines can be re-ordered; they are executed from top to bottom.
    #
    # If you remove a line here THAT COMMIT WILL BE LOST.
    #
    # However, if you remove everything, the rebase will be aborted.
    #
    # Note that empty commits are commented out
    1. 결과 확인
    • git log 명령으로 4번 커밋(f2cb8d1e)이 사라졌음을 확인
    # vi 에디터에서 저장 후 종료했을 때 성공 시 출력되는 문구
    Successfully rebased and updated refs/heads/rebase_delete_commit.
    
    # git log 명령으로 
    git log
    
    commit 3109b93b097d214b42f7fddafa3f3546d19ffbc3 (HEAD -> rebase_delete_commit)
    Author: owljoa <needshield@gmail.com>
    Date:   Wed Apr 7 21:04:12 2021 +0900
    
        modify file2.txt again
    
    commit e97f2ac91a391618e550d2714858f5667d636868
    Author: owljoa <needshield@gmail.com>
    Date:   Wed Apr 7 21:03:37 2021 +0900
    
        modify file2.txt, file3.txt
    
    ~~commit f2cb8d1ea78784c8796aebfe6953d0d8ce72a21d
    Author: Kim Dong In <needshield@airi.kr>
    Date:   Wed Apr 7 21:03:05 2021 +0900
    
        modify file1.txt~~
    
    commit 8180ad4499153ce949bcbe6f13276e1a320f1b6f
    Author: owljoa <needshield@gmail.com>
    Date:   Wed Apr 7 21:00:47 2021 +0900
    
        add file3.txt
    
    commit 064034361e01095b88026ff1a1cf887e6c8dcfeb
    Author: owljoa <needshield@gmail.com>
    Date:   Wed Apr 7 21:00:06 2021 +0900
    
        add file2.txt
    
    commit f90bb2717944fecf39bd4f4597625f402620e0f0
    Author: owljoa <needshield@gmail.com>
    Date:   Wed Apr 7 20:57:00 2021 +0900
    
        add file1.txt

2. 특정 커밋 수정

- 목표: 4번 커밋에서 추가된 텍스트를 "Hello World"로 변경

  1. 베이스 커밋을 4번 바로 이전 커밋인 3번 커밋으로 지정하여 interactive 모드로 rebase 시작

    git rebase -i 8180ad4
    
    or
    
    git rebase --interactive 8180ad4
  2. 지정한 베이스 커밋 이후 커밋들에 어떤 작업을 할지 명령어를 지정하는 vi 에디터가 열림

    1. 4번 커밋(f2cb8d1e)을 수정하는 것이 목적이므로 해당 커밋의 작업 타입을 pick → edit으로 변경
    2. 저장 후 종료
    # 베이스 커밋 이후에 쌓여있던 커밋들에 대해 실행할 명령어 결정 (명령어가 작업 종류를 결정함)
    # pick f2cb8d1 modify file1.txt -> edit f2cb8d1 modify file1.txt
    edit f2cb8d1 modify file1.txt
    pick 5de25f7 modify file2.txt, file3.txt
    pick 4142015 modify file2.txt again
    
    # Rebase 8180ad4..4142015 onto 8180ad4 (3 commands)
    #
    # Commands:
    # p, pick <commit> = use commit -> 해당 커밋을 그대로 사용
    # r, reword <commit> = use commit, but edit the commit message
    # e, edit <commit> = use commit, but stop for amending -> 커밋을 사용하되, 변경을 위해 해당 커밋 이후 시점에 멈춤
    # s, squash <commit> = use commit, but meld into previous commit
    # f, fixup <commit> = like "squash", but discard this commit's log message
    # x, exec <command> = run command (the rest of the line) using shell 
    # b, break = stop here (continue rebase later with 'git rebase --continue')
    # d, drop <commit> = remove commit -> 해당 커밋 제거
    # l, label <label> = label current HEAD with a name
    # t, reset <label> = reset HEAD to a label
    # m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
    # .       create a merge commit using the original merge commit's
    # .       message (or the oneline, if no original merge commit was
    # .       specified). Use -c <commit> to reword the commit message.
    #
    # These lines can be re-ordered; they are executed from top to bottom.
    #
    # If you remove a line here THAT COMMIT WILL BE LOST.
    #
    # However, if you remove everything, the rebase will be aborted.
    #
    # Note that empty commits are commented out
  3. edit를 설정한 커밋 이후의 시점에 멈춘 상태 확인

    ➜  git-study (rebase_edit_commit)git rebase -i 8180ad4             
    Stopped at f2cb8d1...  modify file1.txt
    You can amend the commit now, with
    
      git commit --amend 
    
    Once you are satisfied with your changes, run
    
      git rebase --continue

  1. 이전 커밋(4번 커밋) 되돌리기

    # 커밋 되돌리기
    git reset HEAD~
    
    # 확인 (4번 커밋의 변경사항은 비어있던 file1.txt 파일에 I'm file1.txt! 라는 내용을 추가한 것)
    git diff {file1.txt가 속한 디렉토리}/file1.txt
    
    ==== git diff {file1.txt가 속한 디렉토리}/file1.txt 명령 결과 ====
    diff --git a/file1.txt b/file1.txt
    index e69de29..9c888c6 100644
    --- a/file1.txt
    +++ b/file1.txt
    @@ -0,0 +1 @@
    +I'm file1.txt!
    \ No newline at end of file
  2. 원하는 작업(4번 커밋에서 추가된 텍스트를 "Hello World"로 변경)을 한 이후 다시 커밋

    # file1.txt의 내용을 I'm file1.txt! -> Hello World로 변경 
    
    git add {file1.txt가 속한 디렉토리}/file1.txt
    git commit -m "modify file1.txt"
  3. rebase 마무리 및 결과 확인

    • file1.txt의 내용이 Hello World로 변경되었음을 확인할 수 있음
    • 로그에서 베이스 커밋 이후의 커밋 해시가 모두 달라진 것을 확인할 수 있음
    # rebase를 이어서 진행 (interactive에서 pick이 아닌 다른 타입의 작업을 지정하지 않았고,
    # 새로운 커밋 생성 중 conflict 발생하지 않는다면 rebase가 마무리됨
    ➜  git-study (e88f496)git rebase --continue
    Successfully rebased and updated refs/heads/rebase_edit_commit.
    
    git log
    ============= git log 결과 ===============
    commit **07116c5d88a08a508350e2931913c029f5bc568e** (HEAD -> rebase_edit_commit)
    Author: Kim Dong In <needshield@airi.kr>
    Date:   Wed Apr 7 21:04:12 2021 +0900
    
        modify file2.txt again
    
    commit **4cbf4586492cf6a03224faefe29a481146fe7d7f**
    Author: Kim Dong In <needshield@airi.kr>
    Date:   Wed Apr 7 21:03:37 2021 +0900
    
        modify file2.txt, file3.txt
    
    commit **e88f496c4c50a9ac6e9928d22d7447f57d1b6397**
    Author: Kim Dong In <needshield@airi.kr>
    Date:   Tue Apr 13 21:53:16 2021 +0900
    
        modify file1.txt
    
    commit 8180ad4499153ce949bcbe6f13276e1a320f1b6f
    Author: Kim Dong In <needshield@airi.kr>
    Date:   Wed Apr 7 21:00:47 2021 +0900
    
        add file3.txt
    
    commit 064034361e01095b88026ff1a1cf887e6c8dcfeb
    Author: Kim Dong In <needshield@airi.kr>
    Date:   Wed Apr 7 21:00:06 2021 +0900
    
        add file2.txt
    
    commit f90bb2717944fecf39bd4f4597625f402620e0f0
    Author: Kim Dong In <needshield@airi.kr>
    Date:   Wed Apr 7 20:57:00 2021 +0900
    
        add file1.txt

3. 주의사항


  • 이미 원격 저장소에 공유되어 있는 커밋에 대해서는 rebase 하지 않는 것이 바람직함
    • rebase를 수행하면 베이스 커밋 이후에 존재했던 기존의 커밋들이 새롭게 생성되면서 커밋의 고유 해시값도 변경됨
      • Git은 커밋을 해시 값으로 식별
      • Git은 커밋 내의 변경사항이 같아도 해시 값이 다르면 새로운 커밋으로 인식함
    • 사고 Example
      • 원격 저장소에 공유되어있는 커밋들을 rebase로 변경 후 원격 저장소로 다시 push
      • 기존의 커밋들을 병합한 상태인 다른 개발자의 로컬 저장소에서는 커밋 내의 변경사항이 같더라도 해시값이 달라진 커밋들에 대해서 Merge 작업을 수행함
      • 동일한 저자, 커밋 날짜, 메시지를 가진 커밋이 두개가 존재하는 상황 발생

[참고 링크]

https://git-scm.com/book/ko/v2/Git-브랜치-Rebase-하기

https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History

https://www.atlassian.com/git/tutorials/rewriting-history/git-rebase

https://milooy.wordpress.com/2018/10/25/git-rebase-or-merge-commit/

profile
좋아하다

0개의 댓글