Git
Git Interactive Rebase로 두 커밋 하나로 합치기
과기정통부에서 진행하는 오픈소스 컨트리뷰션 아카데미 2024 Git & JS 참여형 프로젝트에 참가하여 학습한 내용을 기록한다. git의 interactive rebase가 무슨 기능을 하는지, 어떻게 사용하는지를 정리했다.
인터랙티브 리베이스(Interactive Rebase)
Interactive Rebase
커밋 히스토리를 재구성하고 수정할 수 있는 도구. 만능 맥가이버 칼이라고 볼 수 있다.
- 커밋 메시지 수정: 커밋 메시지를 변경하거나 오타를 수정할 수 있어요.
- 커밋 합치기: 연속적인 커밋들을 하나로 합칠 수 있어요.
- 커밋 분리: 하나의 커밋을 두 개 이상의 작은 커밋으로 분리할 수 있어요.
- 커밋 순서 변경: 커밋들의 순서를 변경하거나 다른 브랜치로 옮길 수 있어요.
- 커밋 삭제: 불필요한 커밋들을 제거할 수 있어요.
2개의 커밋을 하나로 합쳐야 할 때
[시나리오] 어떤 팀에서는 하나의 티켓 당 하나의 커밋을 하라고 한다. 만약 여러 개의 커밋을 작성했다면 어떻게 수정해서 PR을 올릴 수 있을까? Interactive Rebase로 커밋을 합쳐보자.
git rebase -i HEAD~2 # 인터랙티브 리베이스 명령어 git rebase -i HEAD~{number} # head부터 number만큼 조작 or git rebase -i {hash} # hash의 다음 커밋부터 head까지 조작
git rebase -i
: interactive rebase 명령어로, -i는 interactive를 뜻한다.
HEAD~2
: HEAD로부터 2개의 커밋을 리베이스한다는 뜻이다.
명령어를 입력하면 아래와 같이 git-rebase-todo가 뜬다. GUI도 있지만 text 모드로 해보자.
깃 그래프와 다르게 왜 git-rebase-todo의 커밋 순서는 반대로 정렬되어 보여주고 있을까?
- 이렇게 rebase로 붙을 때, 역순으로 오래된 커밋부터 master 브랜치에 붙인다.
- 때문에 커밋 순서가 반대로 되어 있다.
인터랙티브 리베이스 명령어들
# 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 [-C | -c] <commit> = like "squash" but keep only the previous # commit's log message, unless -C is used, in which case # keep only this commit's message; -c is same as -C but # opens the editor # 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 # u, update-ref <ref> = track a placeholder for the <ref> to be updated # to this position in the new commits. The <ref> is # updated at the end of the rebase # # 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. #
우리는 두 커밋을 합칠 것이므로 squash, s 명령어를 사용한다.
squash는 ‘밀어넣다’는 뜻을 가지고 있다.
pick 3947110 신동호 폴더 추가 squash b3cb0da 자기 소개 추가
저장하고, 커밋 메시지도 수정해준다. squash 명령어는 해당 커밋을 이전 커밋에 밀어넣기 때문에, 이전 커밋이 있어야 하므로 최신 커밋을 squash 해야 한다.
origin 저장소의 브랜치에는 2개의 커밋이 있지만, 로컬에는 1개의 커밋으로 합쳐져 있다. 때문에 이대로 푸시하면 충돌하기 때문에 푸시할 수 없다.
git push -f
옵션을 넣어서 강제로 덮어쓰기 해주자.git push -f
upstream 저장소의 PR에 가보면, 커밋이 업데이트 되어있다.
[시나리오] 나의 PR이 머지되기 전에, 다른 커밋들이 쌓였다. 다시 작업을 수정했다면? 다시 작업을 시작할 때는 원격 저장소에 있는 반영 사항을 내 브랜치에 반영해야 한다.
“사람은 git rebase를 씁니다.”
git rebase upstream/main
명령어를 통해 새 변경 사항을 가져온다. 그리고 git push -f
명령어를 통해 다시 푸시해본다.fork한 저장소에 가보면, 변경 사항이 잘 적용되어 있는 것을 확인할 수 있다.
Tips
클론할 때 새 폴더에 복사하는 방법
최근 commit만 포함하여 클론할 수 있는 방법
git clone --depth 1 http://~.git
- depth 옵션은 저장소를 클론할 때 히스토리를 얼마나 가져올지 결정할 때 사용한다.
- 예를 들어,
--depth 1
은 가장 최신의 1개 커밋만 가져온다는 뜻이 된다.
- 이 옵션을 사용해서 저장소의 전체 히스토리가 아닌 특정 커밋의 스냅샷만 가져올 수 있다.
- 히스토리가 크거나 저장소가 큰 경우에 유용하지만, 히스토리를 거슬러 올라갈 수 없게 된다.
왜 표시된 것보다 더 많이 설치되나요?
package.json에는 1456개의 의존성 라이브러리가 없는데 설치는 1456개의 패키지가 설치되었다고 뜬다. 그 이유는 의존성 라이브러리에서 또 다른 라이브러리를 가져다 사용하는 경우에 모든 라이브러리들의 꼬리를 물고 물어서 설치되기 때문이다.
git remote -v에 upstream이 없는 경우
포크한 저장소를 클론하여 작업하고 있다고 가정하자.
가끔
git remote -v
명령어를 쳤을 때, 아래와 같이 origin만 뜨는 경우가 있다. clone된 저장소만 인식하고 원본 저장소(upstream)를 인식하고 있지 않은 상태다.여기서 upstream을 추가 해주기 위해 명령어를 통해 upstream도 추가해주면 된다.
git remote add upstream https://github.com/~.git