학습계기
이전에 프로젝트를 하며 깃허브를 많이 사용해봤고, 풀리퀘스트를 날리거나 간단한 이슈생성도 해본 적이 있다. 하지만 commit, push, ammend 등 자주 사용하는 명령어만 계속 사용했고, 제대로 된 깃허브공부는 해본 적이 없었다. 그래서 깃허브의 head 개념도 잘 몰랐고, 커밋메시지를 바꾸는 등의 다양한 상황에서의 깃허브의 명령어도 사용해보지 못했다.
우테코 프리코스 과제를 진행하는 중에도 커밋메시지를 잘못 작성했을 때 많이 헤맸었다. 그래서 이번 기회에 테코톡 등을 보면서 깃허브 사용법에 대해 공부해보려고 한다!
첫 번째 학습 강의: merge, rebase, cherry-pick
(해당 강의를 듣고 정리합니다)
https://www.youtube.com/watch?v=b72mDco4g78
git은 서로 다른 작업을 하기 위한 별도의 공간을 생성할 때 브랜치를 사용한다. 기능구현을 위한 브랜치 생성하고, 브랜치에서 기능구현이 끝나면 해당 기능을 main 브랜치에 merge하는 식으로 개발이 진행된다.
1. Merge
(1) Fast-foward Merge
//브랜치 생성 및 이동
git checkout -b lime
//파일 수정 후
git add .
git commit -m "feat: 숫자야구 기능 구현 완료"
git push origin lime
일단 이런식으로 기능별 커밋을 한 상태에서 main브랜치에 머지를 하려고 한다.
//main브랜치로 이동
git checkout main
//lime브랜치에 Fast-foward merge
git merge lime
- 일단 main브랜치로 이동한 다음, 나한테 합칠 브랜치 이름을 적어 merge를 진행한다.
- 현재 브랜치가 가리키는 커밋(=main)을 머지할 브랜치(lime)가 가리키는 커밋으로 옮긴다고 생각하면 된다
- 이 merge방법을 fast-forward merge라고 한다.
- 현재 브랜치가 머지할 브랜치와 베이스가 같을 때 fast-forward merge를 제공한다고 한다.
- a브랜치에 b브랜치를 merge 할 때, b가 온전히 a이후 커밋들 가리키고 있으면 a브랜치를 b브랜치로 이동하기만 하면 되니까 그렇다
(2) 3-way-merge
- fast-forward merge는 특별한 분기점 없이 merge하는 과정이었다
- 이번 상황은 기능 브랜치를 구현하고 많은 커밋이 있는 상태에서 main에 분기점이 있는 경우이다
- main에서 분기한 브랜치에서 기능개발이 이루어지고 있는 상황에 기존의 main의 코드가 바뀐 상황이다
- 이 변경사항을 브랜치에 반영해야한다.(fix: change to user ... )
fast-forward merge의 조건은 두 브랜치 커밋의 베이스가 같아야하는 것이다. 이번 상황에선 두 브랜치의 커밋 베이스가 다르다
- fetch는 로컬 Git에게 원격 저장소에서 최신 메타데이터 정보를 확인하는 명령어이다(원격 저장소에 변경사항이 있는지 확인만 하고, 변경된 데이터를 가져오지는 않음)
- merge를 수행하면 다음과 같이 새로운 merge commit이 생성되면서 합쳐진다
merge를 수행한 후 계속 기능개발을 진행하면 된다
Conflict
만약 3-way merge 상황에서 두 브랜치가 같은 파일을 수정한 경우 conflict가 발생한다.
이 경우에는 automerge가 실패했다고 나오며 conflict를 해결하라는 메시지가 나온다.
git status로 자세한 결과를 확인가능하다. unmerge된 이력~
깃그래프를 확인해보면 머지되지 않은 회색커밋이 생성되어 있다
충돌을 해결하고 머지를 진행하기 위해서는 직접 수정하거나 자동수정을 해야한다.
수정 후 add로 파일 스테이징 해두면 기다리던 회색머지커밋에 포함된다
이후 commit하면 머지커밋 정상적으로 생성된다.
Non fast forward
- Fast-forward 명령어는 option을 제거하지 않으면 항상 Fast-forward 동작을 기본적으로 제공한다고 한다
- 풀리퀘스트를 보면 다르게 동작하는 3가지의 merge방법을 제공한다.
(1) create merge commit
- basebranch같아도 fast-forward 머지를 진행하지 않고, 하나의 머지커밋을 생성해서 머지를 진행하는 방법이다
- 이렇게 진행한다면 기능구현할 때 사용했던 커밋들이 하나하나 살아있으므로, 사용할 여지를 남길 수 있다
- 또한 만든 기능에 대해 머지 분기점이 생기므로, 어떤 기능을 만들어 main머지했는지 보기가 쉬워져 가독성 좋아진다
(2) Squash and merge
- 이 방법은 merge를 하면 하나의 머지커밋 생성하여 main이 해당 커밋 바라보게 한다
- 기능에 대해 작업한 모든 커밋을 하나의 커밋으로 통합해서 머지하는 작업이다
- 커밋이 너무 길면 여러 개의 기능 구현이 있을 때 알아보기 힘들다
- 이를 보완하는 방법이 Squash and merge이다
- 특정 기능에 대한 커밋을 하나만 둔다면 어떤 기능 작업했는지 한 눈에 보이므로, 가독성이 좋다
- 2가지의 커밋이 하나로 합쳐져 하나의 커밋으로 만들어진 것을 볼 수 있다
- 지금까지 만든 기능들이 하나로 통합되어 머지된다고 생각하면 된다
- 풀리퀘마다 각각 커밋을 하나만 남기게 된다
- 해당 커밋이 이름으로 작성되어 있다
- PR단위마다 커밋작성
- 누가 계획하고 완료했는지 가독성 좋아짐
(3) Rebase and merge
- rebase는 base를 다시 조정한다는 느낌이다
- feature에서 작업한 커밋들을 모두 현재 main브랜치 최상단에 복붙하는것이다!
- 커밋이 많을 때 한 줄로 모든 커밋 저장한다면 난잡해보일 수 있기 때문에 이 방법을 사용하기도 한다
PR시에 고를 수 있는 머지방법들은 각각 장단점이 있다 -> 팀에 필요한 적절한 방법 선택하자
2. Rebase
- rebase는 서로 다른 브랜치 합칠 때 사용한다는 점에서 merge와 비슷하다.
- merge의 경우 3 way merge에서는 머지커밋이 추가되고, 브랜치가 합쳐졌다는 기록이 남지만
- rebase를 사용한다면 깨끗한 히스토리를 만들 수 있다
이렇게 base가 갈라진 경우에 rebase로 브랜치 합치면
git fetch upstream main
git rebase upstream/main
- 깨끗한 커밋이 만들어진 걸 볼 수 있다
Rebase란 현재 branch의 Base를 재설정하여 합치는 것을 말한다
merge = 새커밋 만들기
rebase = base를 재설정하여 합치기
reabase upstream main을 실행하면 베이스가 upstream의 main이 가리키는 곳으로 변경된다
"나의 베이스를 main껄로 바꿔줘" 이런 느낌인 것 같다
쭉 하나로 연결되는 걸 볼 수 있다
기존 커밋들은 그대로 두고, base pointer만 변경하는 것처럼 보일 수 있지만, 사실 rebase는 Base가 바뀔 커밋들을 복사하여 연이어 붙인다
복사한 커밋을 연이어서 붙여서 base만 바뀌고 합쳐진 것처럼 보이게 된다
Rebase 전과 후의 commit id를 비교해보면 복사되었다는 걸 알 수 있다
3. Cherry-pick
cherry-pick은 다른 브랜치에 있는 커밋을 선택적으로 내 브랜치에 적용시키는 것이다
로컬에서 develop 브랜치 생성, 커밋 3개를 생성하였다
- develop 브랜치에서 개발을 하던 도중, 기능2를 당장 퍼블리싱해야하는 상황이 발생했다!
- 기능3은 아직 테스트되지 않아. main브랜치에 올리고싶지 않은 상황이다
- 이 때 기능2만 origin main에 포함하는 방법이 cherry-pick이다.
cherry-pick은 main에 내가 원하느 커밋만 merge하는 방법이다.
(1) cherry-pick 방법1
- main브랜치에서 체리픽하고, 바로 roigin main에 푸시하는 방법
- main으로 체크아웃 -> 기능2에 대한 커밋아이디 적고 체리픽 -> 푸쉬
- 하지만 이렇게 main에 직접 푸쉬하는 방법은 사용되지 않고, 주로 브랜치 따로 생성해서 main브랜치로의 머지pr요청, 머지되는경우가 많다
(2) cherry-pick 방법2
- 체리 만들어서 체리픽한다
- 그리고 깃헙에서 풀리퀘스트를 날린다
- 이렇게 하면 non forward 머지로 커밋 하나 생성되고, 기능2에 대한 커밋이 origin main에 포함된 것을 확인할 수 있다
- 원하는 커밋만 쏙쏙 골라 다른 브랜치에 반영이 가능하다!
cherry-pick 한 개만 가져오기
- 커밋아이디를 지정해준다
cherry-pick 여러 개 가져오기
cherry-pick 연속된 커밋 가져오기
4. cherry-pick conflict 해결하기
- conflict를 해결하기 위해 코드를 수정
- Git add로 수정된 코드 add
- Git cherry-pick --continue 입력
- 일단 이렇게 cherry-pick을 한 후 충돌이 발생했음을 확인한다
- 충돌을 해결하고 add한 후 cherry-pick --continue를 입력한다
- 다음과 같이 커밋들이 새롭게 복사되고, develop 브랜치에 연이어 붙여지는 걸 확인할 수 있다
- 체리픽하는 경우... 같은 내용 가진 커밋들이 여러 개 생기므로, 누가 누굴 체리픽했는지 모를 수 있다는 단점이 있다
- 이 점을 유의하여 사용해야한다!
요약
- cherry-pick으로 다른 브랜치 커밋을 내 브랜치로 가져올 수 있음
- cherry-pick이 된 커밋들은 복사됨
- cherry-pick 진행 시 충돌이 발생할 수 있으며, add와 continue 명령어로 해결한다
두 번째 강의: Git commands
https://www.youtube.com/watch?v=JsRD2AWxxFg
1. 브랜치가 뭔가요?
- 브랜치는 sw개발 시 개발자들 간 동일한 소스코드를 함께 공유할 때 만든다
- 동일 소스코드를 기반으로 다른 작업을 해야한다면 -> 브랜치를 만들어 분기점을 만든다
- 각 브랜치는 독립적 작업영역이며, 이를 통해 새로운 버전을 만들어낸다
브랜치 만들기
git branch step1
브랜치 이동하기
git switch step1
git checkout step1
//기존 기능이 많아서... 2.23버전 부터 swtich 기능 생김
//기존 checkout기능이 많아서 분리했다고 함. 이거도 가능하당
커밋하기
git commit
merge하는 법
- 커밋1작업 후 브랜치생성하여 추가커밋 생성
- 커밋1에서 버그발견 -> bugFix후 commit
- step1을 bugFix랑 합쳐야하는 상황
git switch step1
git merge bugFix
step1 브랜치로 이동한 후 bugFix와 merge해준다
- 커밋2 커밋3은 충돌이 날 수 있다. 충돌을 해결해주자!
- 아직 bugFix는 커밋3을 가리키므로, bugFix도 옮기자
git switch bugFix
git merge step1
이렇게 하면 bugFix와 step1이 같은 곳을 가리키게 된다
Rebase
- merge처럼 브랜치끼리 합치는 방법 Rebase
git rebase step1
- 이렇게 하면 커밋2뒤에 커밋3'가 생긴다
- 기존 커밋3이 연결된 게 아니라, 아예 연결을 끊고 커밋3 복사해서 올려놓을 것이다
- step1은 그대로기 때문에 step1의 위치도 옮겨준다
git swtich step1
git rebase bugFix
- merge와 차이점
- merge는 합치는 과정이 히스토리에 남아있음
- rebase는 새롭게 히스토리를 쓴다!
- rebase: 코드가 깔끔하고 정돈되어 보이는데, 단점은 재정돈 시 기록이 없어진다는 것이다
- 외국에선 merge사용, 한국은 rebase 선호한다??
브랜치의 HEAD 알아보기
git switch 커밋1hash
- head는 현재 체크아웃 된 커밋 = 현재 작업중인 커밋이다.
- head는 항상 작업트리의 최근 커밋을 가리킨다
- 작업트리에 변화주는 깃 명령어들은 대부분 head변경으로 시작함
- head는 일반적으로 브랜치 이름 가리킨다
git switch main
체크아웃하면 HEAD가 main을 가리킨다
상대참조
- 현재 step1의 위치는 커밋5이다
git switch HEAD^
- 이렇게 상대참조를 사용하면 커밋4에 head가 간다.
- ^는 캐럿이라고 부르며, 한 번 이전 커밋을 가리킨다
git switch HEAD^^
- 이렇게 하면 앞앞 커밋을 가리키게 된다
~는 틸드라고 부른다
- 틸드뒤에 숫자 붙이면 숫자만큼 이전 부모를 찾아가게 된다
- 틸드를 사용해 브랜치가 가리키는 커밋 위치를 바꿀 수 있다
git branch -f step1 step1~2
- step2가 커밋 3 가리키게 하러면 step1을 기준으로 이동할 수 있다 (step1의 두 번째 앞)
- head와 브랜치가 가리키는 커밋을 바꿀 수 있다!
작업 되돌리기
- 커밋3을 실수한 상황이다
- 로컬에서 혼자 작업할 때 완전히 기록을 지우는 방법은 reset이다
git reset HEAD~1
- 이렇게 하면 최근커밋 하나가 지워진다
- 애초에 커밋 안한상태로 만들기 위해 history를 고친다
- 이 방법은 다른사람이 사용하는 remote branch에서는 사용하지 않는다
다른 사람도 사용하는 remote branch에서는 rever를 사용한다
git revert HEAD
- 기존 커밋2 살아있으면서 2'가 생기는 걸 볼 수 있다
- 2'는 커밋2와 반대되는 내용이다
- revert -> 다른 사람에게도 변경내역을 보낼 수 있다!
삭제된 커밋, 브랜치 복구
- command를 입력하면 hash값이 생긴다.
- 우리가 제거한 모든 기록이 남는다!
- 여기서는 해당 해시값 찾아서 reset시키는 방법 사용한다
git reflog
- 모든 로그를 확인하여 해시값을 확인한다
- 삭제 전 해시값 입력 -> 커밋2가 살아난다
git reset --hard 커밋2의 HASH
git switch -c step3 커밋3hash
- 이 방법으로 reset도 복구가 가능하다
- 모든 건 기록되어있다! 해시값만 찾는다면 복구가 가능하다
상황: 미션 브랜치를 최신화하지 않고 작업
원격 저장소 등록하기
git clone https~
git remote -v
git remote add upstream https://mission~~
git remote -v
git fetch upstream silder
git pull upstream silder
- upsteram에서 필요한 브랜치정보를 가져오는 방법이다 fetch
git push origin wilder
체리-픽: 커밋로그를 내 마음대로 골라오는 법
- 좋은것만 골라먹기/ 커밋 골라서 가져오기
- git log로 커밋들 로그 확인가능하다 -> 작업중인 곳에서 해당 커밋 가져와서 등록가능
git cherry-pick 5basd
- 범위도 지정할 수 있다
git cherry-pick 13241-asd5d
- 해시값을 나열해도 골라올 수 있다
브랜치를 최신화하지 않고 작업했을 때 해결방법
- 원격저장소에서 머지된 브랜치정보 가져옴
- 브랜치를 로컬로 가져옴
- 동명의 브랜치 다른이름으로 바꾸고/ /기존 로컬 브랜치 제거
- 다음 원격저장소 머지된 1단계미션 브랜치 로컬로 가져와서 브랜치 원격저장소 저장함
- 복사한 로컬브랜치 커밋들 체리픽 -> 원격저장소 가져온 1단계 미션 브랜치에 커밋작성함
- 커밋 다 입력 후, 원격저장소 푸쉬 후 풀리퀘 요청
더 공부해보면 좋을 내용
- learngitbranching을 추천하신다고 한다
'협업 > Git' 카테고리의 다른 글
[Git] .gitignore 적용시키기 공부 (0) | 2024.01.28 |
---|---|
[GitHub] 커밋 메시지 바꾸기: amend, rebase, cherrypick (1) | 2023.11.01 |
Angular commit 메시지 가이드라인 정리 (0) | 2023.10.19 |
.gitignore 적용 안될 때 대처법 (0) | 2023.08.13 |