국문과 유목민

[git]Git 심화 (ammend, reset, rebase) 본문

IT 견문록/추가 학습 정리

[git]Git 심화 (ammend, reset, rebase)

논곰 2022. 5. 12. 00:29
해당 게시글은 부스트캠프 AITech 중 이고잉님의 Git강의를 정리한 내용입니다. 아는 내용 등은 조금 빼고 이해가 필요했던 혹은 다시 볼만한 부분에 대해서 간략히 정리했습니다.

--ammend

 ammend는 commit한 내용을 수정하고 싶을 때 사용한다. --ammend를 하면 기존 commit을 복사해서 새로운 이전 Parent(부모)와 동일하게 연결된 commit이 생성돼 master와 연결된다. 단, ammend는 push를 하기 전에만 해야 한다. ammend한 내용을 복원할 수도 있는데, 이때는Reset을 사용하면 된다. (Reset과 Revert, Reset과 Checkout은 헷갈리는 경우가 있다고 한다) 

HEAD와 master

HEAD와 master. HEAD가 master에 attached된 상태

 

  • HEAD는 Working direcoty가 어떤 버전과 같은지를 나타내는 Pointer와 같은 역할
  • master: branch에서 가장 마지막 작업한 곳

Checkout을 하면 HEAD가 움직이게 된다. HEAD가 branch에 연결되어 있다면 attached 상태라고 하고, 떨어져 있다면 detached되어있다고 한다. 

reset

 Reset은 헤드가 가리키는 branch를 움직이는 역할을 한다. 따라서 만약 원하지 않는 commit을 삭제하고 싶다면, reset을 통해 브랜치를 내려주면 삭제된 효과를 얻을 수 있다. 하지만 reset을 한다고 아예 지워지는 것은 아니고, 복원도 할 수 있다. 하지만 이렇게 하기 위해서는 commit log가 필요한데, 이때 log를 모르면 Reflog를 사용하면 된다.

 Reset 시 HEAD가 attached돠었을 때와 Detached되었을 때 서로 다른 결과가 발생한다. 만약 HEAD가 attached 되어있다면 이때, branch가 같이 움직인다. 하지만 detached되었을 때는 (가리키는 branch가 없기 때문에) HEAD만 움직인다(checkout과 같다).  

Reflog

Reflog는 git에서 자동적으로 HEAD의 변화에 대해 tracking한 결과를 볼 수 있는 명령어이다.  따라서 이를 통해 reset으로 지워진 commit id도 확인해서 가져올 수 있다(따라서 지워진 commit id로 reset을 하면 지웠던 commit을 복원할 수 있다). git의 특성 중 immutability에 대해 얘기할 때 나오는 부분이라고 한다.
immutability (불변성): Tracking하기 좋고, Recovery하기도 좋다. 

Tag

tag는 branch와 다르게, 정적이인 좌표라고 할 수 있다. 변하지 않고 tag는 해당 위치의 계속해서 남아있게 된다. (나는 베이스캠프 같은 느낌으로 이해했다)

git push --set-upstream origin master

 원격저장소의 master와 로컬의 master가 동일한 이름을 가질 것으로 Setting할 때 사용한다. --set-upstream은 로컬 저장소의 브랜치를 원격저장소의 어디로 흘려보낼 것인지를 결정한다는 느낌으로 생각하면 된다. .git폴더 내 config에서 확인이 가능하다. (한 번만 설정하면 됨) --set-upstream을 -u로 대체해서 사용할 수 있다.

3-way-merge

원래 git은 merge를 수행할 때, 3-way-merge라는 방법을 사용한다고 한다. 따라서 conflict가 발생하면 base를 기준으로 바뀐 두 commit값을 비교해서 conflict 등을 발생시킨다. 이때, mergetool을 활용하면 conflict가 발생했을 때 3-way-merge과정을 좀 더 직관적으로 해결할 수 있다고 한다. (VSCode가 더 편한 것 같기도...base는 보이지 않지만) 원래는 conflict를 해결하고 나서 add를 해줘야 하지만, git mergetool을 활용하면 conflict를 해소하고, add까지 해준다고 한다. 사용법은 크게 어렵지는 않아보인다. 

 

git mergetool tkdydgkrl

# kdiff3 설치 & Setting
git config --global --add merge.tool kdiff3
git config --global --add mergetool.kdiff3.path "C:/Program Files/KDiff3/kdiff3.exe"
git config --global --add mergetool.kdiff3.trustExitCode false

git config --global --add diff.guitool kdiff3
git config --global --add difftool.kdiff3.path "C:/Program Files/KDiff3/kdiff3.exe"
git config --global --add difftool.kdiff3.trustExitCode false

rebase

 rebase는 부모 commit을 바꾸는 것을 의미한다. 좀 더 풀어서 설명하자면, 원격의 흐름에 로컬의 흐름을 이어서 하나의 branch로 보이게 하는 방법이다. rebase든 merge든 병합된 결과는 반드시 같아야 한다. rebase는 3-way-merge로 이해할 수 있다(아래 그림1의 분홍색 선). conflict가 발생하면, 원 Parent에서 서로 달라진 부분을 바교해서 둘 다 수정했다면 conflict가 된다. 결과적으로 origin/master(원격)에서 작업한 이후, local_master(로컬)에서 작업한 것처럼 하나의 branch로 합쳐진 것처럼 보이게 할 수 있다 (그림2).

rebase 전 상태 - rebase(초록색)
rebase를 수행한 상태

  • rebase는 branch 흐름 자체를 조작하기 때문에 로컬 저장소에 있는 것만 수정해야 한다. 만약 협업을 하는 중 rebase를 사용해 push하게 되면, 큰 문제가 생길 수 있다.
  • 그리고 rebase를 했다면 절대 pull을 하면 안 되고, 대신 fetch를 활용해야 하는데, 그 이유는 fetch는 merge를 하지 않기에 Conflict가 발생하지 않기 때문이다. 
  • rebase 중 conflict가 발생했다면, conflict를 해소하고, 이후 git rebase --continue를 해주면 된다 (중지된 rebase를 재개한다는 뜻이다).
  • 마지막으로 rebase를 한다면 자주자주 업데이트를 시켜줘야 한다. 

revert

revert는 특정 위치의 commit을 취소하는 방법으로 예전 commit을 골라 그 부분만 취소할 수 있다. (근데 revert는 조금 이해하기 어려운 것 같다..) 여기서 예전 commit이라는 것은 이전까지의 모든 커밋이 아닌 특정 commit만 지울 수 있다는 뜻이다. 
 revert에서도 conflict가 발생할 수 있긴 한데, 이 conflict는 이전 단계의 commit과 발생된다.

기타

  • git log --oneline --all --graph: gitlog를 그래프 형식으로 보여주는 코드
  • -d는 삭제를 의미한다. 
  • pull은 fetch + merge이다.
  • Fast-forward merge: 현재 브랜치의 commit을 대상 브랜치의 commit까지 옮기는 작업인데, 같은 branch에 있어서 큰 변화없이 단순히 이동하기만 해도 되는 merge를 의미한다. 
  • push --force를 활용하면 원격 저장소의 상황을 무시하고, 강제로 push할 수 있다. 단, 혼자할 때만 사용해야 하지 안 그러면 협업 과정에서 아주 큰 재앙이 생길 것이다.