GIT 기본 정리
- 커밋 : 내가 현재 작업하고 있는 작업물의 진행상황의 체크포인트라고 비유할 수 있다.다만 이러한 체크포인트에 아래와 같은 메시지를 함께 넣어 쉽게 식별하도록 한다.
- 커밋은 사실 다단계로 이루어진 절차이다.git add -> git commit
- Working Dir -> Staging Area -> Repo
- Atomic Commit : 기능 하나하나 마다 커밋을 유지하며 커밋하는것.만약 커밋을 롤백해야하는 상황이 올 경우,기능 마다 커밋을 유지할 경우 최대한 기존의 작업물을 살리면서 롤백이 가능하다.
Branch
기본적으로는 Master(또는 Main) 브랜치가 생성이 되고 이러한 디폴트 브랜치를 기준으로 아래의 그림과 같이 뻗어 나가며 개별적인 작업을 수행할 수 있게 해준다.
HEAD의 경우 현재 사용자가 위치해 있는 브랜치를 가르키고 있는 포인터이다.실제로 .git 디렉토리에 들어가서 찾아보면 커밋 해시값으로 이루어져 있다.
Merging
특정 커밋이 아니라 브랜치를 병합한다.항상 HEAD가 가르키는 브랜치를 병합한다.
- 우리가 병합하길 원하는 Receiving Branch로 이동한다.(HEAD를 변경한다)
- 이후 merge 명령어를 통해 원하는 Branch를 병합시킨다.
아래의 그림을 보면 좀 더 쉽게 이해할 수 있다.
- Master에 Bugfix를 병합시키기 전 상태,HEAD는 Bugfix
- 헤드를 Master,즉 Receiving 브랜치로 이동
- Merge 수행,HEAD가 가르키는 포인터가 Bugfix의 커밋으로 변경됨
위와 같이 기존의 236ff라는 커밋을 가르키고 있던 HEAD가 Merge가 되면서 2456이라는 커밋을 가르키는것을 Fast-Forward 방식의 Merge라고 한다.쉽게 말하면 236f 커밋에서 2456커밋 사이의 모든 커밋들을 건너뛰면서 헤드가 변경되는 방식이라고 이해하면 된다.물론 모든 Merge가 위와 같은 방식으로 진행되지는 않는다.
다음으로는 Merge Commit을 알아보자.
우선 위의 상황과 같이 Bugfix라는 브랜치에서 작업을 하고 있는데 여기서 누군가가 Master 브랜치에서 작업을 수행하여 Bugfix에는 온전히 없는 작업이 생겼다.이러한 경우 앞서 배운 Fast-Forward를 통해 Merge가 불가능하다.그래서 아래 그림과 같이 Merge를 수행할 경우 새로운 커밋을 생성하며 Merge를 수행한다.하지만 아래와 같은 구조는 Master 브랜치와 Bufgix 브랜치에서 동일한 파일의 동일한 Line을 수정했다면 Conflict가 발생할 수 있다.
Stash
어떠한 브랜치에서 작업을 하던 중, 다른 브랜치로 HEAD를 변경해야할 일이 생겼을 때 사용할 수 있다.브랜치를 Switch할 때 두가지 가능한 시나리오가 있다.
- Fast-Forward처럼 Switch하려는 브랜치와 Switch되는 브랜치간의 작업 변경사항이 겹치는 것이 없는경우
- 이와 반대로 서로 같은 부분을 수정 또는 작업한 경우
전자의 경우 이러한 변경사항들이 그대로 따라오면서 switch를 진행할 수 있다.하지만 두번째의 경우 Aborting이 발생하며 Switch 또는 Checkout을 할 수 없다.
위와 같은 상황에서 작업한 파일들을 임시로 stashing 시켜 다른 브랜치로 이동할 수 있게한다.
git stash
: stash 영역에 현재 작업중인 코드들은 저장,즉 변경사항으로 취급되지 않아 switch가 가능해진다.git stash pop
: stash 영역 보관되어 있는 코드들을 다시 변경사항으로 가져온다.
Git 변경 사항 취소하기
git checkout <커밋해시>
-> detach HEAD 알람이 뜬다.즉,해당하는 커밋 해시로 HEAD를 되돌려서 해당 커밋이 될 시점으로 이동한다.기본적으로 HEAD가 가르키는 Refer는 브랜치이다.그리고 이러한 브랜치가 가르키는 Ref가 가장 최근의 커밋 Hash이다.하지만 checkout을 통해 HEAD를 분리시킨다는것은 HEAD가 브랜치를 가르키지 않고 이전의 커밋 해시를 가르키고 있다는것이다.cat ./git/HEAD
를 통해 확인가능하다. Detached HEAD의 역할은 단순히 예전 커밋의 작업물들을 Read 할 수 있다.만약 현재 작업 상태로 돌아가려면 git checkout <현재 브랜치>
로 돌아갈 수 있다.
HEAD~1
: 헤드 기준 1개의 커밋만큼 이전으로 돌아간다.
git restore <file-name>
: HEAD 기준으로 특정 파일을 해당 시점으로 되돌려준다.--source
옵션을 통해 특정 커밋의 파일로도 되돌아갈 수 있다.현재 옵션으로는 스테이징 되지 않은 변경사항을 되돌릴 수 있다.
git restore --staged <file-name>
: 스테이징 된 파일을 되돌리 수 있다.
git reset
은 커밋을 취소하는 기능이다.다음과 같이 두가지 경우가 존재한다.
- Plain(no option) : 현재 커밋 기준으로 돌아가려는 커밋으로 되돌린다.다만 되돌릴 경우 작업했던 내용이 전부 남아있기에 돌아가고 나서
git switch -c
등의 옵션을 사용해서 취소한 커밋의 작업물을 다른 브랜치에 옮겨서 커밋할 수 있다. - Hard(—-hard option) : 위와 마찬가지로 현재 돌아가려는 커밋으로 돌아가지만 작업했던 내용(코드)마저 모두 사라진다.
git revert
는 git reset과 동일한게 변경 사항을 취소하는것은 동일하지만 변경 사항을 취소한 작업물을 새로운 커밋을 생성하며 되돌린다.reset의 경우 현재 헤드를 과거 커밋으로 되돌려버리기에 공동으로 작업할 경우 나에게 없지만 남에게 그대로 남아있는 문제가 발생합니다.이러한 상황에서는 revert를 통해 새로운 커밋을 생성하고 해당 커밋으로 HEAD를 돌리는것이 효율적입니다.
Git push
git push -u
는 사실 상 현재 내 로컬의 브랜치를 깃헙의 해당 브랜치와 연결해주는 옵션이다.git push --set-upstream
과 동일하며 해당 옵션 없이 푸쉬를 해도 지장없이 코드는 깃헙레포에 저장된다.해당 옵션으로 push를 하면 Branch set up to track remote branch
라는 log가 출력된다.또한 단순히 git push
만으로도 현재 커밋내역을 깃헙 origin에 푸쉬가 가능해진다.(-u 설정을 하지 않고 git push만 하게 되면 에러가 발생한다)결론적으로 보통 처음 해당 원격 레포에 푸쉬할 때 -u 옵션을 붙여서 해준다.
Remote Tracking Branches
Git clone을 통해 레포를 로컬에 복사할 경우 로컬에는 마치 하나의 master 브랜치만 있는것처럼 보이지만 git branch -r
을 통해 보면 master과 origin/master 이렇게 두가지 브랜치가 존재한다.여기서 origin/master 브랜치가 remote tracking branch이다.이는 remote로 설정된 origin에 대한 기본 브랜치이기에 clone만 할 경우에도 생긴다.쉽게 말하면 로컬에서의 작업을 나타내는 브랜치와 로컬에서 작업한 내용이 remote에도 push가 되었는지 확인하는 깃헙용 브랜치 두가지가 생성된다. (Remote Tracking Branches는 remain last known spot이라는 표현이 좀 더 직관적이라고 생각한다)
레포를 클론 후 작업을 하고 커밋을 하고나서 git status
를 하면,your branch is ahead of origin/master
by 1 commit이라는 로그가 남는다.이는 곧 로컬의 master 브랜치는 새로운 커밋을 가르키지만,origin/master는 여전히 clone이 되었을 당시의 최신 커밋을 가르킨다는 것이다.
여기서 git push origin master
를 통해 작업 내용을 깃헙에 푸쉬하면 origin/master 브랜치가 현재 로컬의 master 브랜치를 따라잡으며 git status
시 your branch is up to date..
라는 로그가 남는다.(따라잡는다는 표현 대신 동일한 커밋을 바라본다라고도 가능하다)
만약 cloen한 레포의 특정 브랜치를 그대로 로컬 브랜치로 만들고 작업하고 싶을 경우는 다음과 같이 하면된다.우선 clone을 하면 디폴트로 main 또는 master 브랜치에 HEAD가 존재한다.여기서 git switch <내가 작업하려는 특정 브랜치>
를 하게 되면 자동으로 로컬에 해당 브랜치와 origing/해당브랜치를 연결시켜준다.
Fetch and Pull
Fetch의 경우 원격 repo의 변경 사항을 내 Local Repo에만 가져오는것이다.즉,작업 영역에는 어떠한 변화가 생기지 않는다.아래의 그림으로 이해하자.
조금 더 설명하면 현재 내 작업에 어떠한 영향도 주지 않고 누군가가 올린 작업을 공유할 때 사용할 수 있다.git fetch origin 특정 브랜치
를 한 뒤,해당 작업을 보려면 git checkout origin/작업을 fetch한 브랜치
를 통해 detached HEAD를 만든 뒤 확인할 수 있다.만약 원래 내가 작업하던 곳으로 돌아가려면 git switch master
를 통해 빠져나갑니다.다시 정리하면 fetch는 깃헙에 존재하는 remote branch들을 내 Local Repo에 땡겨오는것이고 이러한 branch들의 작업영역이 로컬 Working Space에는 반영되지 않는다.
Pull은 Fetch + Merge와 같은 방식으로 동작한다.즉,깃헙 레포의 변경된 내역을 로컬의 Working Space까지 반영한다.Merge의 동작이 포함되어있기에 conflict가 일어날 가능성이 존재한다.