January 31, 2022 • ☕️☕️☕️☕️ 21 min read
이제는 정복해 보자
3년이나 개발을 공부해왔음에도 불구하고 매번 답이 안 나오는 git… 🤦♀️ 회사 코드를 본격적으로 뜯어보기 전, 설날 기념 잉여인간일 때 git을 정복해야겠다는 생각이 들었다. GUI 도구인 Git Kraken을 사용하고 싶기도 하고…
(2/6 추가) 많이 공부하긴 했으나 더 알아야 할 내용이 많다. 언젠간 추가할 것. 이라고 해놓으면 분명 안하겠지만 두번째 민족 대명절인 추석이 오기 전에는 (…) 할 것이라고 믿는다.
git을 사용하는 로컬 저장소의 workflow는 크게 세 가지 단계로 나뉜다.
작업 디렉토리(working directory): 현재 작업중인 공간으로, 마음대로 편집할 수 있다.
스테이징 영역(staging area)
: commit할 준비가 된 파일을 잠시 올려두는 공간으로, 작업 디렉토리와 HEAD 사이 징검다리 역할을 한다.
💡 스테이징 영역은 공식적으로는 인덱스(index) 라고 표현하고 있지만, 여기서는 일상적으로 사용하는 용어인 스테이징 영역을 사용한다.
HEAD: 스테이징 영역에 있는 데이터의 스냅샷을 찍어 커밋(commit)을 생성하여 git이 관리하게 되는 공간이다. 가장 최근 변경본을 가리킨다.
이때 작업 디렉토리의 파일들은 다시 두 가지로 나뉜다. 한번이라도 git의 관리 대상이 되어 스냅샷이 있는 파일의 경우에는 tracked, 새롭게 추가되어 git이 아직 모르는 파일은 untracked 파일로 구분한다.
‘a.txt’라는 파일을 생성하고 내용을 입력한 뒤, git status
를 통해 작업 트리를 확인해보자.
💡
git status
명령어는 작업 디렉토리와 스테이징 영역의 상태를 확인하기 위해서 사용한다.
아직 한번도 git이 관리한 적이 없는 a.txt 파일이 Untracked files에 빨간색 글씨로 표시된다. git add
명령어로 스테이징 영역에 파일을 올려보자.
git add a.txt
💡
git add
에 옵션을 붙여 스테이지(stage)할 파일의 목록을 선택할 수 있다.
git add .
: 현재 디렉토리의 모든 변경 내용을 스테이징할 때git add -A
: 모든 디렉토리의 모든 변경 내용을 스테이징할 때
그러면 다음과 같이 a.txt가 commit할 준비가 되어있음을 알려준다.
이제 변경된 파일을 커밋해보자. git commit
명령어로 현재 스테이징된 파일들을 커밋한다.
git commit
위 명령어만 입력하면 다음과 같이 커밋 메시지를 입력할 수 있는 쉘이 뜬다.
쉘을 이용하는 건 상당히 무섭고 귀찮은 일이므로, 명령어 자체에 커밋 메시지를 추가해보도록 하자.
git commit -m [커밋 메시지]
이제 git log
로 커밋 내역을 확인해보자.
로그를 살펴보면 HEAD -> main
이라고 되어있는 것을 확인할 수 있다. 여기서 HEAD
는 현재 브랜치를 가리키는 포인터이며, 지금의 HEAD
가 가리키는 커밋은 바로 다음 커밋의 부모가 된다.
여기서 다시! a.txt 파일의 내용을 수정한 후, git status
를 실행해 보자.
여전히 빨간 글씨로 표시되지만, 처음에 a.txt를 만들어 Untracked 영역에 있을 때와는 달리 Changes not staged for commit
이라는 메시지를 볼 수 있다. Tracked 영역, 즉 이미 git이 관리하고 있는 파일에 수정이 발생한 것이기 때문이다.
이렇게 작업을 했던 워킹 디렉토리의 변경사항을 날리고 싶다면 git restore a.txt
명령어를 실행해준다.
💡
git restore
명령어는 작업 디렉토리에서 수정한 파일을 되돌리는 명령어다. 이미 스테이징한 파일을 되돌릴 때는git restore --staged
명령어를 사용하면 된다.
git remote
명령어를 통해 로컬과 연결되어 있는 모든 저장소의 이름을 확인할 수 있다. git clone
을 통해 리모트 저장소를 복제했다면 리모트 저장소 이름은 기본적으로 origin
이 되며, git remote rename [이전 이름] [새로운 이름]
명령어로 로컬에서 사용할 리모트 저장소 각각의 이름을 바꿀 수 있다.
git remote add [단축 이름] [URL]
명령어를 통해 기존 워킹 디렉토리에 새 리모트 저장소를 추가할 수도 있다.
💡 유용한
git remote
옵션
git remote -v
: 리모트 저장소들의 이름을 URL과 함께 볼 수 있다.git remote show [리모트 저장소 이름]
: 리모트 저장소의 구체적인 정보(브랜치 등)를 확인할 수 있다.git commit
으로 파일의 변경 이력을 로컬 저장소에 남겼다. 이제 리모트 저장소인 github에 코드의 변경 이력을 반영하기 위해서는 git push
명령어로 로컬 저장소의 내용을 전송해줘야 한다.
git push
명령어는 기본적으로 리모트 저장소 이름과 브랜치 이름을 인자로 받는다.
git push [저장소 이름] [브랜치 이름]
git push
명령어를 사용할 때마다 매번 저장소명과 브랜치명을 입력하는 게 귀찮다면, -u
옵션을 사용하여 최초 한번만 저장소명과 브랜치명을 입력하고 그 이후에는 인자들을 생략할 수 있다.
git push -u origin main
git log
로 확인해보면 아래와 같이 커밋에 빨간 글씨로 origin/main
과 origin/HEAD
가 추가된 것을 볼 수 있다.
리모트 저장소에서 데이터를 가져올 때는 fetch
명령어를 사용한다.
git fetch [리모트 저장소 이름]
이 명령은 로컬에는 없지만, 리모트 저장소에는 있는 데이터를 모두 가져온다. 이때 데이터를 ‘가져오기’만 하고 merge 시켜주지는 않는다.
pull
명령어는 리모트 저장소의 브랜치에서 데이터를 가져올 뿐 아니라, 자동으로 로컬 브랜치의 현재 코드에 merge시켜준다. fetch
와 merge
를 합친 명령어다.
pull
명령어는 아래 브랜치 시스템을 이해하며 더 자세히 알아보자.
모든 버전 관리 시스템은 브랜치를 지원하며, git 역시 훌륭한 브랜치 전략을 지원한다. 잘 사용하지 못하는 것은 우리의 책임인 것이다.
git은 데이터를 change set이나 변경사항(diff)으로 기록하지 않고 일련의 스냅샷으로 기록한다는 점이 특징적이다. 새로 브랜치를 만들 때 프로젝트를 통째로 복사하는 대신, git의 브랜치는 어떤 한 커밋을 가리키는 SHA-1 체크섬 파일에 불과하기 때문에 생성과 삭제가 쉽고 효율적인 메모리 관리가 가능하다.
브랜치를 직접 만들어보자.
git branch feat1
위 명령어로 브랜치를 만들 수도 있지만, 브랜치 생성과 동시에 해당 브랜치로 이동까지 해줄 수도 있다.
git checkout -b feat1
git은 지금 작업 중인 (로컬) 브랜치를 HEAD
라는 특수한 포인터로 파악한다. 방금 전 feat1
브랜치로 이동했으므로 이제 HEAD
는 feat1
브랜치를 가리키게 된다.
이 상태에서 ‘feat1.txt’ 파일을 생성하고 커밋을 새로 해보자.
git add feat1.txt
git commit -m "create feat1.txt"
이제 HEAD
가 가리키는 feat1
브랜치가 main
보다 한 단계 앞서있다.
다시 main
브랜치로 되돌아가보자.
git checkout main
이제 main
브랜치가 가리키는 커밋을 HEAD
가 가리키게 되었으며, 워킹 디렉토리의 파일도 그 시점으로 되돌려 놓았다.
💡 유용한 git branch 옵션들
- git branch -r: 리모트 브랜치 목록 보기
- git branch -a: 로컬 브랜치 목록 보기
- git branch -m [이전 이름] [새 이름]: 브랜치 이름 바꾸기
- git branch -d [브랜치 이름]: 브랜치 삭제하기
현재 main
브랜치에서 작업을 하다가 특정한 이슈를 처리하기 위해 feat1
브랜치를 만들었다.
이때 갑자기 프로젝트에 문제가 생겨서 급하게 버그를 해결해야 하는 경우가 발생한다. hotfix 내용에 feat1
의 작업 내역이 섞이는 것을 방지하기 위해 다시 main
브랜치로 돌아간 후 hotfix1
브랜치를 만들어 이동한다.
git checkout main
git checkout -b hotfix1
💡 이때
feat1
에 아직 커밋하지 않은 파일이 있고, 해당 파일이main
브랜치와 충돌이 난다면main
브랜치로 checkout할 수 없다. 이때는git stash
명령어를 통해 워킹 디렉토리를 정리할 수 있다.
hotfix1
브랜치에서 버그 수정 후 커밋을 하면 브랜치 히스토리는 아래와 같이 그려질 것이다.
hotfix 내용을 운영 환경에 적용하기 위해 hotfix1
브랜치를 main
브랜치에 합쳐야 한다. 여기서 git merge
를 사용한다.
git checkout main
git merge hotfix1
hotfix1
브랜치가 가리키는 커밋이 main
이 가지고 있는 커밋 히스토리를 모두 포함하고 있기 때문에 브랜치 포인터는 그저 최신 커밋으로 이동한다. 이렇게 단순히 브랜치 포인터를 이동하는 merge 방식을 Fast-forward(빨리감기)라고 부른다. 이때는 merge commit이 생기지 않는다.
이제 main
과 hotfix1
브랜치 포인터는 같은 커밋을 가리키게 되었다.
이제 필요 없어진 hotfix1
브랜치는 삭제하고, 원래 작업하던 feat1
브랜치로 돌아가 작업을 계속한다.
git branch -d hotfix1
git checkout feat1
feat1
에서 어느 정도 작업을 한 이후에는 해당 작업 내용을 main
브랜치에 merge해야 한다. 현재 브랜치 히스토리는 아래와 같다.
git checkout main
git merge feat1
hotfix1
을 merge했을 때와는 메시지가 다르다. 현재 브랜치가 가리키는 커밋이 merge할 브랜치의 조상이 아니므로 (merge할 브랜치의 모든 커밋을 포함하고 있지 않으므로) Fast-forward merge가 불가능하다. 이 경우 git은 각 브랜치가 가리키는 커밋 두 개와 공통 조상 하나를 사용하여 3-way merge를 한다.
단순히 브랜치 포인터를 최신 커밋으로 옮기는 게 아니라, 3-way merge의 결과를 별도의 커밋으로 만든 후 해당 브랜치가 그 커밋을 가리키도록 이동시키는 것이다.
이때 만약 hotfix1
브랜치와 feat1
브랜치가 같은 부분을 수정했다면 작업 내역에 충돌이 발생할 수도 있다. 이 경우 git은 자동 merge를 하지 못하며, 개발자가 Conflict를 해결해줘야 한다.
충돌한 부분을 모두 해결하고 git add
명령어로 다시 git에 저장한 후, 커밋을 해주면 merge가 완료된다!
지금까지의 작업은 모두 로컬 브랜치에서 진행한 내용이다. 이제 리모트 저장소로 가보자!
리모트 트래킹 브랜치는 리모트 브랜치를 추적하는 레퍼런스이며 브랜치다. 리모트 트래킹 브랜치는 일종의 북마크로, 리모트 저장소에 마지막으로 연결했던 순간에 브랜치가 무슨 커밋을 가리키고 있었는지를 나타낸다.
리모트 트래킹 브랜치의 이름은 [리모트 저장소 이름]/[브랜치 이름]
형식으로 되어 있다. github의 레퍼지토리를 clone 받는다면 git은 이 리모트 저장소에 자동으로 origin
이라는 이름을 붙이고, main
브랜치를 가리키는 포인터를 만든다. 이 포인터가 바로 origin/main
이며, 로컬의 main
브랜치는 origin/main
을 가리키게 된다.
리모트 저장소의 main
브랜치에 누군가 다른 내용을 변경한 커밋을 push했다면 로컬의 main
브랜치와 히스토리가 서로 달라지게 된다.
이때는 우선 fetch
명령어로 리모트 저장소에 있는 브랜치의 내용을 가져온다.
git fetch origin
💡 이때 리모트 저장소에서 가져온 최신 커밋 이력은 이름 없는 브랜치로 로컬에 불러와지며, 임시로
FETCH_HEAD
라는 특수한 이름을 가진다.git checkout FETCH_HEAD
명령어로 리모트 저장소에서 가져온 업데이트를 확인해볼 수 있다.
그러면 브랜치 히스토리는 아래와 같아진다.
fetch
를 통해 서버의 데이터를 받아와서 저장했지만, 워킹 디렉토리의 파일 내용은 변경되지 않고 그대로 남아있다. 새로 받은 브랜치의 내용을 합쳐주기 위해서 merge
를 실행한다.
git merge origin/main
pull
명령어를 사용하면 fetch
와 merge
를 한번에 해줄 수 있다.
git pull origin main
💡 협업 시 사용했던 리모트 브랜치를 만든 후 작업을 마쳐 더 이상 해당 브랜치가 필요하지 않게 되었다면,
git push origin --delete [브랜치 이름]
명령어로 리모트 브랜치를 삭제해준다.
고통의 rebase 단계에 진입했다…
merge 말고도 git에서 한 브랜치에서 다른 브랜치로 합치는 방법이 있다. 바로 rebase다.
위 그림과 같이 브랜치가 분기되어있을 때, merge
명령어를 사용하여 합치면 3-way merge로 새로운 커밋이 생성된다.
이때 C4
의 변경 사항을 Patch로 만들고 이를 다시 C5
에 적용시키는 방법이 있다. 이를 바로 rebase라고 한다. rebase
명령어로 한 브랜치의 변경 사항을 다른 브랜치에 적용할 수 있다.
git checkout feat1
git rebase main
실제로 rebase가 진행되는 과정은 다음과 같다.
main
, feat1
두 브랜치가 나뉘기 전인 공통 브랜치(C3
)로 이동한 후 그 커밋부터 지금 checkout한 브랜치가 가리키는 커밋까지 diff를 차례대로 만들어 어딘가에 임시로 저장해 놓는다.feat1
)가 합칠 브랜치(main
)이 가리키는 커밋을 가리키게 하고, 아까 저장해 놓았던 변경사항을 차례대로 적용한다.rebase 이후의 브랜치 히스토리는 아래와 같다.
이제 main
브랜치도 C4'
커밋을 가질 수 있도록 main
브랜치를 Fast-forward시켜준다.
git checkout main
git merge feat1
짠!✨ 새로운 머지 커밋 없이 히스토리가 예쁘게 정리되었다.
💡 merge vs rebase
- merge: 변경 내용의 이력이 모두 그대로 남아 있기 때문에 이력이 복잡해진다.
- rebase: 이력은 단순해지지만, 원래의 커밋 이력이 변경된다. 정확한 이력을 남겨야 할 경우에는 사용하면 안 된다.
rebase 방식을 사용할 때는 병합 충돌(merge conflict)이 발생하는 모든 커밋들에 대해 충돌을 일일이 해결해줘야 한다. 충돌난 커밋이 너무 많을 때는 rebase보다는 단순 병합(pull)을 하는 것이 더 나을 수도 있다.
rebase --onto
는 다른 토픽 브랜치에서 갈라져 나온 토픽 브랜치와 같은 히스토리가 있을 경우에 사용할 수 있는 명령어다. server
브랜치를 만들어서 서버 기능을 추가하고 그 브랜치에서 다시 client
브랜치를 만들어 클라이언트 기능을 추가한다. 마지막으로 server
브랜치로 돌아가서 몇 가지 기능을 더 추가한다.
아. 그림 그리기 너무 힘들다. 여기서부터는 출처서 가져온 그림을 그대로 활용하자. 그림의 master
를 main
으로 바꿔서 생각하면 된다. 아무튼 요즘 github은 main
이라는 이름을 사용하고 있으니.
이때 server
브랜치는 그대로 두고 client
브랜치만 main
으로 합치는 상황을 생각해보자. server
와는 아무 관련 없는 client
커밋은 C8
, C9
이다. 이 두 커밋을 main
브랜치에 적용하기 위해서 --onto
옵션을 사용한다. 첫 번째 인자로는 새로운 조상이 될 베이스 커밋을 넘겨주고, 두 번째 인자로는 기존 베이스 커밋을 넘겨준다.
git rebase --onto main server client
server
브랜치와 server
브랜치와 client
브랜치의 공통 조상까지의 커밋을 client
브랜치에서 없애고 그 이후의 client
커밋들을 main
브랜치를 새로운 베이스로 하여 rebase해준다. 어려워서 토나올 것 같지만, 분명 이런 게 필요한 경우도 맞닥뜨려봤다.
이제 main
브랜치로 돌아가서 Fast-forward시킬 수 있다.
git checkout main
git merge client
이제 홀로 남겨진 server
브랜치의 작업이 마무리되면, server 브랜치로 이동 후 git rebase main
명령어로 server
를 main
에 rebase시켜준다. 결과는 아래와 같다.
💡 Tip!
git rebase [베이스 브랜치] [토픽 브랜치]
로 인자를 입력하면, 토픽 브랜치로 checkout하지 않고도 rebase할 수 있다.git rebase main server
이제 다시 main
브랜치를 Fast-forward시키면 최종적인 브랜치 히스토리가 완성된다.
git checkout main
git merge server
💡 rebase 시 주의사항 이미 공개 저장소에 push한 커밋을 rebase하면 안 된다. rebase는 기존의 커밋을 그대로 이용하는 것이 아니라, 내용은 같지만 다른 커밋을 새로 만든다. 새 커밋을 push하고 동료 중 누군가가 그 커밋을 pull해서 작업을 한다고 하자. 그런데 그 커밋을
git rebase
로 바꿔서 push해버리면 동료가 다시 push했을 때 동료는 다시 merge해야 한다. 그리고 동료가 다시 merge한 내용을 pull하면 내 코드는 정말 엉망이 되어버린다. 🤯
리모트 저장소와 로컬 저장소가 각각 다른 변경 사항을 가지고 있는 경우, Fast-forward merge가 불가능하다. 단순 pull
을 하는 경우 별도의 merge commit이 생긴다.
Merge branch 'main' of https://github.com/zigsong/git-commands-test
이러한 병합 커밋을 만들지 않으려면 --rebase
옵션으로 rebase merge를 하면 된다. 이는 git fetch
와 git rebase
명령어를 순서대로 실행하는 것과 같다.
git checkout feat1
git pull --rebase origin main
이렇게 하면 로컬 저장소의 브랜치가 리모트 저장소의 브랜치의 최신 커밋으로 rebase되어 병합 커밋이 남지 않는다.
rebase로 모든 병합 충돌을 해결했다면, 강제(force) 푸쉬를 해줘야 한다.
git push -f origin feat1
강제 푸쉬를 하지 않으면 리모트 저장소의 feat1
브랜치를 pull 받아야 하는데, 그러면 로컬 저장소에서 rebase한 커밋과 리모트 저장소의 커밋이 중복되어 동일한 내용의 커밋이 생겨버리기 때문이다.
reset
명령어는 현재 HEAD
를 특정한 상태로 되돌린다. 다만 checkout
명령어처럼 HEAD
가 가리키는 브랜치를 바꾸지는 않으며, 계속 현재 브랜치를 가리키면서 현재 브랜치가 가리키는 커밋을 바꾼다.
reset
에는 크게 3가지 옵션이 있는데, 각 옵션을 통해 워킹 디렉토리(working directory) 와 스테이징 영역(staging area), 그리고 현재 브랜치를 가리키는 HEAD 를 어떻게 업데이트할 것인지 결정할 수 있다.
첫 번째로, reset
에 --soft
옵션을 붙여 실행하면 워킹 디렉토리와 스테이징 영역은 건드리지 않고 브랜치가 가리키는 커밋만 이전으로 되돌린다.
git reset --soft HEAD~1
💡
HEAD
뒤에 오는 숫자는 돌아가고 싶은 커밋의 개수이다.HEAD~1
대신 특정 커밋의 해쉬를 입력해도 된다. 바로 직전 커밋으로 돌아가고 싶다면HEAD^
를 사용할 수 있다.
다음으로, reset
명령을 실행할 때 아무 옵션도 주지 않았을 때는 기본적으로 --mixed
옵션을 생략한 것과 같다. 이때 스테이징 영역을 현재 HEAD
가 가리키는 스냅샷으로 업데이트할 수 있다.
git reset [--mixed] HEAD~1
이 경우 워킹 디렉토리는 건드리지 않지만, 스테이징 영역을 비운다. git commit
명령과 git add
명령을 모두 되돌리는 것이다.
마지막으로 reset
에 --hard
옵션을 사용하면 워킹 디렉토리까지 되돌린다.
git reset --hard HEAD~1
--hard
옵션을 사용한 경우 워킹 디렉토리의 파일까지 강제로 덮어쓰게 되며, 결과를 되돌리는 것이 불가능하므로 주의해서 사용해야 한다.
reset
명령어를 정리하면 다음과 같다.
--soft
옵션이 붙으면 여기까지)--hard
옵션이 붙지 않았으면 여기까지)💡 이때 reset을 취소하고 싶다면? >
git reflog
를 통해 작업 내역을 확인한 후, 몇 번째HEAD
로 돌아갈지 선택한 후 아래와 같이 실행한다.git reset HEAD@{1}
reset
명령어는 github과 같은 온라인 원격 저장소에 올라가지 않은 상태의, 로컬 커밋을 되돌리는 명령어다. 작업 내역을 원격 저장소에 올린 경우라도 해당 브랜치를 자기 자신만 사용하고 있다면 문제가 되지 않겠지만, 다른 동료도 해당 브랜치를 사용하는 경우 함부로 reset
을 해서는 안 된다. 이때는 revert를 사용한다.
revert
는 reset
과 달리 커밋을 삭제하는 것이 아니라, 커밋을 추가한다. 커밋을 추가하기 때문에 아래와 같은 메시지가 추가된다.
Revert '...'
revert
는 reset
과 마찬가지로 soft
, hard
등의 옵션이 붙을 수 있다.
아래의 예시를 살펴보자.
git commit -m "1번 커밋"
git commit -m "2번 커밋"
git commit -m "3번 커밋"
git revert [1번 커밋의 해쉬]
위처럼 명령어를 실행하면 ‘1번 커밋’ 이후의 커밋들이 삭제되는 것이 아니라, ‘1번 커밋’에 해당하는 내용만 삭제된다. 그리고 revert 커밋이 남게 된다. git log
명령어를 통해 확인할 수 있다.
Revert "1번 커밋"
3번 커밋
2번 커밋
1번 커밋
여러 개의 커밋을 되돌릴 때는 아래와 같이 사용한다.
git revert [from 커밋의 해쉬]..[to 커밋의 해쉬]
reset
과 revert
의 차이를 그림으로 정리해 보자.
reset
과 달리 revert
는 중간 커밋만 삭제할 수 있고, 이전으로 되돌린 커밋의 이력이 모두 커밋 메시지를 통해 남기 때문에 히스토리 유지 차원에서 더 유용하다고 할 수 있다.
github에서 PR(Pull Request)을 보내는 경우, merge하려는 베이스 브랜치와 충돌이 없다면 Merge Pull Request 버튼이 활성화된다. 이때 오른쪽의 역삼각형 모양을 눌러보면, 아래와 같이 3가지 옵션이 표시된다.
팀 프로젝트를 할 때 항상 헷갈렸던 세 가지 옵션들에 대해 살펴보자!
🔖 Create a merge commit
PR의 베이스가 되는 main
브랜치와 토픽 브랜치(여기서는 feat1
이라고 하겠다.)의 작업 이력이 다르다면 새로운 merge commit을 생성하게 된다. 히스토리는 아래 그림과 같다.
🔖 Squash a merge
squash는 한국어로도 스쿼시란다. ㅡㅡ
‘찌부러뜨리다’라는 말 정도로 해석이 가능한데, 여기서는 PR에 딸려 있는 모든 커밋들을 하나로 합쳐 새로운 커밋으로 만들어 베이스 브랜치인 main
에 추가시키는 방식이다. 토픽 브랜치의 커밋 히스토리를 합쳐서 깔끔하게 만들기 위해 사용한다. 역시나 새로운 merge commit이 추가된다.
🔖 Rebase a merge
PR에 포함된 모든 커밋들이 합쳐지지 않고 각각 main
브랜치에 추가된다. 각 commit은 모두 하나의 부모를 가진다. 이때 새로운 merge commit을 생성하지 않기 때문에 커밋 히스토리를 하나로 깔끔하게 만들 수 있다. (다만 하나의 브랜치에서 작업한 것처럼 보이게 된다!)
이렇게 어떤 방법으로든 스무스하게 merge가 되면 좋겠지만… 그렇지 못한 경우는 늘상 발생한다. main
브랜치에서 파생한 feat1
브랜치에서 작업 후 main
브랜치를 베이스로 PR을 열어놓은 사이, main
브랜치에서 파생한 또다른 브랜치인 feature2
의 작업 내역이 먼저 PR merge됐다면 어떨까?
(자동 merge가 불가능하다는 회색 버튼이 뜬다 - 사진을 추가해야할 것이다)
이때 github 상에서 conflict을 해결해줄 수도 있지만, 그러면 새로운 merge commit이 생기는 만큼(확실한가?) 다른 방식을 생각해보게 될 것이다. 이때 git rebase
가 다시 한번 등장한다.
(다음 시간에…)
git에서 사용할 명령어를 별칭으로 기록해두는 거~ (작성중)
git이 관리할 필요가 없는 파일(ex. 로그 파일, 환경변수 파일)들은 .gitignore
파일에 작성하여 git의 관리 대상에서 제외시켜줄 수 있다. 리액트 프로젝트의 경우 용량이 큰 node_modules
폴더와 빌드 아웃풋인 dist
폴더를 .gitignore
로 제외시켜주는 것이 일반적이다.
node_modules/
dist/
.env
워킹 디렉토리의 내용을 잠시 올려두는 거~ (작성중)
특정 브랜치에서 작업을 하던 도중, 다른 브랜치에서 특정 커밋만을 가져올 수 있다.
git checkout feat1
git log
feat1
브랜치의 ...
번 커밋을 feature2
브랜치로 가져오고 싶을 때 cherrypick 명령어를 사용한다.
git checkout feature2
git cherry-pick `...`
태그로 프로젝트 히스토리에 버전을 명시해주는 거~ (작성중)
https://git-scm.com/docs https://git-scm.com/book/ko/v2/ https://kyounghwan01.github.io/blog/etc/git/git-reset-revert/이-작업을-하는-이유 https://im-developer.tistory.com/182