[Git] 7.4. CLI로 rebase 해보기

업데이트:

카테고리:

태그: , , ,

Rebase 사용하기

3-way 병합을 하면 병합 커밋이 생성되기 때문에 트리가 다소 지저분해진다는 단점이 있다. rebase는 소스트리 한글판에서 ‘재배치’라고 표기된다. 즉 내 브랜치의 커밋들을 재배치하는 것.


Rebase의 원리

1. HEAD와 대상 브랜치의 공통 조상을 찾는다.
2. 공통 조상 이후에 생성한 커밋들을 대상 브랜치 뒤로 재배치한다.


Rebase 실습

앞 절에서 만들었던 병합 커밋을 되돌리고 rebase를 한다. [feature1] 브랜치를 한 단계 되돌릴 때는 git reset –hard 명령을 사용한다.

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (feature1)
$ git checkout feature1 # feature1으로 전환.
Already on 'feature1'

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (feature1)
$ git reset --hard HEAD~ # 현재 브랜치에서 한 단계 되돌린다
HEAD is now at c201e9e 새로운 기능 1 추가

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (feature1)
$ git log --oneline --graph --all -n3 # 로그 확인

- bc26b63 (origin/master, master, hotfix) hotfix 실습
  | \* c201e9e (HEAD -> feature1) 새로운 기능 1 추가
  |/
- 9e5110b (tag: v0.1) mybranch1 두 번째 커밋

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (feature1)
$ git rebase master # HEAD 브랜치의 커밋들을 master로 재배치
error: could not apply c201e9e... 새로운 기능 1 추가
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply c201e9e... 새로운 기능 1 추가
Auto-merging file1.txt
CONFLICT (content): Merge conflict in file1.txt

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (feature1|REBASE 1/1)
$ git push
fatal: You are not currently on a branch.
To push the history leading to the current (detached HEAD)
state now, use

    git push origin HEAD:<name-of-remote-branch>

위 코드의 실행결과는 다음과 같다.

image

$ git reset --hard HEAD~ 명령으로 커밋을 한 단계 이전으로 되돌렸다. 되돌린 커밋은 병합하는 커밋이었으므로 병합 커밋이 사라진다. $ git rebase --abort 명령으로 rebase를 시도하였으나, merge에서와 마찬가지로 충돌로 인해 rebase는 실패한다. 실패 메시지에는 수동으로 충돌을 해결한 후에 스테이지에 추가할 것을 알려준다. 그 후 git rebase –continue 명령을 수행하라는 것을 알려준다. (몇 가지 rebase 옵션들을 더 알려주지만 충돌을 해결하고 다시 continue 해보겠다.)


충돌 해결 및 rebase 이어서 하기

오류 발생으로 이전 git bash 입력창을 날려버렸기 때문에 입력 명령을 나열하며 설명한다.

  • $ git status 명령을 입력해 충돌 대상을 확인한다. Unmerged paths : both modified: file1.txt 가 있었다.

  • VSC에서 file1.txt를 직접 Open하여 수동으로 충돌을 해결한다.

  • $ git add file1.txt를 이용해 변경사항을 스테이징하고 상태를 확인한ㄴ다. $ git status를 이용해 file1.txt가 modified 된 것을 확인한다.

  • $ git rebase --continue 명령을 입력하자 아래와 같은 실행 결과가 나왔다.

image

뭔가 무시무시한 경고창이 떴다. 일단 (E)dit anyway의 E를 입력하자 commit message을 편집하는 창이 나왔다. 나는 그 창에서 커밋 메시지를 입력하고 저장하는 방법을 찾을 수 없었기 때문에 끄고 다시 git bash를 실행했다.

image

$ git status 명령을 이용해 지금 무슨 상황인지를 확인하는 것이다. ‘interactive rebase in progress’ 라는 빨간 글씨가 나왔다. 유추하건데, rebase가 진행되고 있다는 뜻인가?

다시 오류가 뜰 지 모르지만, 용기를 무릅쓰고 $ git rebase --continue 명령을 입력하였다. Successfully rebased… 라는 메시지가 나왔다. rebase가 성공한 모양이다.

이를 정확히 확인하고자 $ git log --oneline --graph --all -n2 명령을 사용하였고, 가지 치는 브랜치 없이 이쁘게 커밋이 정리된 것을 확인할 수 있었다. 이제 필요 없어진 [feature1] 브랜치는 $ git merge feature1 명령을 이용해 병합시켜 버렸다.


유용한 Rebase의 사용법 : 뻗어나온 가지 없애기

뻗어나온 가지 커밋을 없애고 정리하는 방법을 고민해보자.


보통 커밋 만들기

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ echo "master1" >> master1.txt # master1.txt 파일 생성 후 "master1" 텍스트 입력

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ git add master1.txt # 스테이지에 추가
warning: LF will be replaced by CRLF in master1.txt.
The file will have its original line endings in your working directory

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ git commit -m "master 커밋 1" # 커밋
[master 8768eab] master 커밋 1
1 file changed, 1 insertion(+)
create mode 100644 master1.txt

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ git push origin master # 푸시
Enumerating objects: 8, done.
Counting objects: 100% (8/8), done.
Delta compression using up to 8 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 580 bytes | 580.00 KiB/s, done.
Total 6 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To https://github.com/Orchemi/hello-git-cli.git
bc26b63..8768eab master -> master

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ git log --oneline -n1 # 로그 확인
8768eab (HEAD -> master, origin/master) master 커밋 1

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ ls # 작업 디렉토리 상태 확인
file1.txt master1.txt

위 코드의 실행결과는 다음과 같다.

image


가지 커밋 만들기

$ git reset --hard HEAD~ # 한 단계 이전으로 HEAD 되돌리기
HEAD is now at 9c9f39f 새로운 기능 1 추가

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ echo "master2" >> master2.txt # master2.txt 만들고 "master2" 텍스트 입력

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ git add . # 스테이지에 추가
warning: LF will be replaced by CRLF in master2.txt.
The file will have its original line endings in your working directory

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ git commit -m "master2 커밋" # 커밋
[master 993bace] master2 커밋
1 file changed, 1 insertion(+)
create mode 100644 master2.txt

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ git log --oneline --graph --all -n3 # 로그 확인

- 993bace (HEAD -> master) master2 커밋
  | \* 8768eab (origin/master) master 커밋 1
  |/
- 9c9f39f (feature1) 새로운 기능 1 추가

위 코드를 실행한 결과이다.

image

예상대로 같은 커밋인 ‘새로운 기능 1 추가’ 커밋에서부터 2개의 서로 다른 커밋을 만들었기 때문에 가지가 생성되었다. 이 상황에서 git pull을 하게 되면 git pull = git fetch + git merge 이기 때문에 가지를 병합하기 위한 병합 커밋이 생기고, 이에 따라 커밋 히스토리가 지저분해진다.


git pull 수행결과

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ git pull # git pull 수행, merge message 창은 그냥 닫는다.
.
.
.

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ git status # git 상태 확인
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
(use "git push" to publish your local commits)

nothing to commit, working tree clean

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ git log --oneline --graph --all -n4 # 로그 확인

- 0497765 (HEAD -> master) Merge branch 'master' of https://github.com/Orchemi/hello-git-cli
  |\
  | \* 8768eab (origin/master) master 커밋 1
- | 993bace master2 커밋
  |/
- 9c9f39f (feature1) 새로운 기능 1 추가

위의 코드를 실행하면 다음과 같다.

image

이렇게 불필요한 가지 커밋과 병합 커밋이 발생하였다. 이를 rebase를 이용해 정리해보자.


rebase로 가지 없애기

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ git reset --hard HEAD~ # 병합 커밋 되돌리기
HEAD is now at 993bace master2 커밋

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ git rebase origin/master # rebase 수행으로 현재 커밋 재배치
Successfully rebased and updated refs/heads/master.

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ git log --oneline --all --graph -n3 # 로그 확인

- 3fc1633 (HEAD -> master) master2 커밋
- 8768eab (origin/master) master 커밋 1
- 9c9f39f (feature1) 새로운 기능 1 추가

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ git push
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 323 bytes | 323.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/Orchemi/hello-git-cli.git
8768eab..3fc1633 master -> master

위 코드의 실행결과는 다음과 같다.

image

rebase를 실행한 결과, 지저분한 가지 커밋들이 사라지고 일련의 커밋 히스토리를 확인할 수 있다.


임시 브랜치 사용하기

많은 입문자들은 충돌 해결, merge, rebase 등을 할 때 소스가 깨지거나 작업한 내용이 사라지는 두려움, Git의 커밋 히스토리가 꼬일 것 같은 두려움을 가진다. 이럴 때 사용하는 것이 바로 임시 브랜치이다.

원래 작업하려고 햇던 브랜치의 커밋으로 임시 브랜치를 만들고 나면 해당 브랜치에서는 아무 작업이나 막 해도 전혀 상관 없다. 임시 브랜치가 필요 없어지는 시점에 CLI에서 $ git branch -D <브랜치 이름> 명령으로 브랜치를 삭제할 수 있다.


임시 브랜치 생성 사용 및 삭제

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ git branch test feature1 # [feature1] 브랜치로부터 [test] 임시 브랜치 생성

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ git checkout test # [test] 브랜치 체크아웃
Switched to branch 'test'

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (test)
$ echo "아무말 대잔치" > test.txt

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (test)
$ cat test.txt # test.txt 파일 내용 확인
아무말 대잔치

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (test)
$ git add .
warning: LF will be replaced by CRLF in test.txt.
The file will have its original line endings in your working directory

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (test)
$ git commit -m "임시 커밋"
[test d753e8f] 임시 커밋
1 file changed, 1 insertion(+)
create mode 100644 test.txt

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (test)
$ git log --oneline --graph --all -n4 # 로그 확인

- d753e8f (HEAD -> test) 임시 커밋
  | _ 3fc1633 (origin/master, master) master2 커밋
  | _ 8768eab master 커밋 1
  |/
- 9c9f39f (feature1) 새로운 기능 1 추가

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (test)
$ git checkout master # 임시 브랜치 삭제
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ git branch -D test
Deleted branch test (was d753e8f).

tmdgn@ASUSSH MINGW64 ~/Documents/hello-git-cli (master)
$ git log --oneline --graph --all -n3 # 로그 확인

- 3fc1633 (HEAD -> master, origin/master) master2 커밋
- 8768eab master 커밋 1
- 9c9f39f (feature1) 새로운 기능 1 추가

위 코드의 실행 결과는 다음과 같다.

image


Reference

  • 팀 개발을 위한 Git GitHub 시작하기, 한빛미디어, 정호영,진유림

댓글남기기