Git을 공부하면서 알게 된 것들

dogyeong·2021년 5월 29일
1

요즘 출퇴근하면서 Pro Git이라는 책을 조금씩 읽고있다. 읽으면서 내가 그동안 몰랐거나 알더라도 정확히 모르고 있었던 부분들을 기록하고자 글을 쓴다.

1. branch

브랜치는 커밋 객체를 가리키는 포인터 같은 것이다. 브랜치를 활용하면 하나의 코드를 여러 버전으로 작성할 수 있다.

위의 그림에도 나와있듯이 Git에는 HEAD라는 특수한 포인터가 있다. HEAD는 현재 작업중인 로컬 브랜치를 가리키는 역할을 한다.

  • git switch(checkout) 명령어는 HEAD를 원하는 커밋으로 옮겨준다.

2. merge

개발할 때 기능별로 브랜치를 만들어 작업한 후, 메인 브랜치에 합치는 과정을 거친다. 이 때 merge 기능을 사용하게 된다.
merge는 3가지 유형이 있다.

fast-forward

위 그림과 같이 master브랜치에서 hotfix브랜치를 만들어서 커밋을 한 번 한 상황이 있다고 하자.
master브랜치로 옮긴 다음 hotfix브랜치를 merge하면 다음과 같은 상태가 된다.

$ git checkout master
$ git merge hotfix

hotfix브랜치는 master브랜치에 기반한 브랜치이기 떄문에 merge과정없이 브랜치 포인터만 C4커밋으로 바꿔주게 된다. 이렇게 브랜치를 끌어올리는 방식을 fast-forward라고 한다.

3-way merge

그러면 아까 예시에서 iss53브랜치를 master에 merge하는 과정을 보자.

$ git checkout master
$ git merge iss53

master브랜치는 iss53의 조상이 아니기 때문에 master와 iss53의 공통조상 커밋(C2)를 찾아서 새로운 커밋(C6)을 만든다. 새로 만들어진 merge 커밋은 합쳐진 두 커밋을 가리키게 되고, master브랜치는 이 커밋을 가리키게 된다.
이렇게 3개의 커밋을 기반으로 새로운 merge 커밋을 만드는 방식을 3-way merge라고 부른다.

rebase

또 다시 처음 예시처럼 브랜치가 나눠진 상황에서 마지막으로 rebase기반으로 합쳐보자.

$ git checkout experiment
$ git rebase master

그러면 위의 그림과 같은 상태가 되는데, rebase 과정을 나타내면 다음과 같다.

  1. experiment브랜치를 master브랜치와의 공통조상 커밋(C2)로 옮긴다
  2. 공통조상 커밋(C2)부터 원래 가리키던 커밋(C4)까지의 변경사항을 만든다.
  3. experiment브랜치를 master브랜치의 커밋으로 옮긴다
  4. 2번에서 만들었던 변경사항을 적용하여 새로운 커밋을 만든다(C4')

3-way merge vs rebase

rebase로 합치게 되면 커밋 히스토리가 깔끔해지는 장점이 있다. 하지만 rebase는 기존 커밋을 사용하는게 아니라 같은 내용의 새로운 커밋을 만들어서 사용하기 떄문에 이미 리모트 저장소에 push된 커밋을 rebase하면 문제가 생길 수 있다.
그래서 일반적으로 다른 사람이 작업하고 있는 커밋은 3-way merge로 합치는 것이 좋다.


3. git rebase --onto

rebase를 단순히 브랜치를 합치는 것보다 다양하게 사용할 수 있다. --onto 옵션을 사용하면 특정 브랜치의 커밋을 제외하고 rebase할 수 있다.
나는 이 기능을 한 브랜치를 여러 브랜치로 나눌 때 종종 사용한다. 작업을 하다보면 여러 기능을 이어서 쭉 개발해야 하는 경우가 많고, PR을 날릴 때는 개발한 기능들을 여러개의 PR로 나눠서 보내고 싶은 경우가 있는데, 이럴 때 활용할 수 있다.

먼저 아래 그림과 같이 develop브랜치를 시작으로 여러 기능을 순서대로 개발했다고 가정하자.

먼저 feature3 브랜치를 분리시키기 위해서 다음 명령어를 입력한다

$ git rebase --onto develop feature2 feature3

feature2브랜치를 제외한 feature3브랜치가 develop으로 rebase된다. 결과는 다음과 같다.

이제 다시 feature2를 분리시킨다.

$ git rebase --onto develop feature1 feature2

최종 결과는 다음과 같다.

rebase는 새로운 커밋을 새롭게 만든다는 것에 주의하자

rebase중 충돌 발생 시, 파일을 수정하여 해결한 후 다시 git addstage상태로 바꾼 다음 git rebase --continue로 rebase를 완료시키면 된다.


4. delete remote branch

리모트 저장소의 브랜치를 삭제할 떄는 다음 명령어로 삭제할 수 있다

$ git push {remote-name} --delete {branch-name}

좀 더 간단하게 빈 내용을 push하는 방법으로 삭제할 수도 있다 👍

$ git push {remote-name} :{branch-name}

5. git add --patch

커밋을 하기 위해선 먼저 stage에 커밋할 파일의 스냅샷을 올려야 한다. 지금껏 작업하면서 파일별로 add하여 커밋했는데, 파일의 변경내역중엔 커밋되면 안되는 부분도 있고 디버깅할 목적으로 추가한 내용들이 있었던 적이 많았다.
이런 경우에 stage에 파일을 추가할 때 patch 옵션을 쓰면 파일 내용 중 원하는 부분만 추가할 수 있다.

$ git add -p
$ git add --patch

git add -p를 하면 수정된 파일을 부분별로 나눠서 추가할 지를 물어본다. 변경사항의 한 부분을 hunk라고 부른다. 이제 git과 대화형으로 각 hunk를 처리할 수 있다. ?를 입력하면 도움말을 볼 수 있다.

Stage this hunk [y,n,q,a,d,e,?]? ?
y - stage this hunk
n - do not stage this hunk
q - quit; do not stage this hunk or any of the remaining ones
a - stage this hunk and all later hunks in the file
d - do not stage this hunk or any of the later hunks in the file
e - manually edit the current hunk
? - print help
  • 이렇게 --patch 옵션을 이용하면 한 파일 내에서 원하는 수정내용만 커밋할 수 있고, 수정내용을 한 번 더 체크하기 때문에 실수를 방지할 수 있다.
  • untracked 파일은 -p를 할 때 나오지 않는다. 그래서 새로운 파일을 추가하는 행위를 의식적으로 할 수 있어서 실수를 줄여준다.

6. git stash

stash는 작업하던 내용을 커밋하지 않고 브랜치를 바꿀 수 있게 작업내용을 임시로 스택에 저장해주는 기능이다.
stash는 워킹 디렉토리에서 수정한 파일들만 저장한다. stash는 Modified이면서 Tracked 상태인 파일과 Staging Area에 있는 파일들을 보관해두는 장소다.

관련 명령어 중 자주 쓸만한 것들을 요약해보면 다음과 같다.

  • git stash push: 로컬의 변경사항을 스택에 추가한다. 이 때 push는 생략가능하다.
  • git stash list: 만들어진 stash 상태 리스트를 출력한다.
  • git stash apply {stash-name}: stash 상태를 적용한다. 이 때 이름을 생략하면 가장 최근의 stash를 적용한다.
  • git stash pop {stash-name}: stash를 적용하고 해당 stash를 제거한다. 이름을 생략하면 가장 최근의 stash를 적용하고 제거한다.
  • git stash drop {stash-name}: 해당 stash를 제거한다. 이름을 생략하면 가장 최근의 stash를 제거한다.

Reference

https://blog.outsider.ne.kr/1247
https://git-scm.com/book/ko/v2

profile
Engineer

0개의 댓글