ziglog

    Search by

    git 명령어 부셔보기

    January 31, 2022 • ☕️☕️☕️☕️ 21 min read

    이제는 정복해 보자

    3년이나 개발을 공부해왔음에도 불구하고 매번 답이 안 나오는 git… 🤦‍♀️ 회사 코드를 본격적으로 뜯어보기 전, 설날 기념 잉여인간일 때 git을 정복해야겠다는 생각이 들었다. GUI 도구인 Git Kraken을 사용하고 싶기도 하고…

    (2/6 추가) 많이 공부하긴 했으나 더 알아야 할 내용이 많다. 언젠간 추가할 것. 이라고 해놓으면 분명 안하겠지만 두번째 민족 대명절인 추석이 오기 전에는 (…) 할 것이라고 믿는다.


    ✅ 변경 이력 등록하기

    git을 사용하는 로컬 저장소의 workflow는 크게 세 가지 단계로 나뉜다.

    1. 작업 디렉토리(working directory): 현재 작업중인 공간으로, 마음대로 편집할 수 있다.

    2. 스테이징 영역(staging area)

      : commit할 준비가 된 파일을 잠시 올려두는 공간으로, 작업 디렉토리와 HEAD 사이 징검다리 역할을 한다.

      💡 스테이징 영역은 공식적으로는 인덱스(index) 라고 표현하고 있지만, 여기서는 일상적으로 사용하는 용어인 스테이징 영역을 사용한다.

    3. HEAD: 스테이징 영역에 있는 데이터의 스냅샷을 찍어 커밋(commit)을 생성하여 git이 관리하게 되는 공간이다. 가장 최근 변경본을 가리킨다.

    이때 작업 디렉토리의 파일들은 다시 두 가지로 나뉜다. 한번이라도 git의 관리 대상이 되어 스냅샷이 있는 파일의 경우에는 tracked, 새롭게 추가되어 git이 아직 모르는 파일은 untracked 파일로 구분한다.

    01

    ‘a.txt’라는 파일을 생성하고 내용을 입력한 뒤, git status를 통해 작업 트리를 확인해보자.

    💡 git status 명령어는 작업 디렉토리와 스테이징 영역의 상태를 확인하기 위해서 사용한다.

    02

    아직 한번도 git이 관리한 적이 없는 a.txt 파일이 Untracked files에 빨간색 글씨로 표시된다. git add 명령어로 스테이징 영역에 파일을 올려보자.

    Copy
    git add a.txt

    💡 git add에 옵션을 붙여 스테이지(stage)할 파일의 목록을 선택할 수 있다.

    • git add .: 현재 디렉토리의 모든 변경 내용을 스테이징할 때
    • git add -A: 모든 디렉토리의 모든 변경 내용을 스테이징할 때

    그러면 다음과 같이 a.txt가 commit할 준비가 되어있음을 알려준다.

    03

    이제 변경된 파일을 커밋해보자. git commit 명령어로 현재 스테이징된 파일들을 커밋한다.

    Copy
    git commit

    위 명령어만 입력하면 다음과 같이 커밋 메시지를 입력할 수 있는 쉘이 뜬다.

    04

    쉘을 이용하는 건 상당히 무섭고 귀찮은 일이므로, 명령어 자체에 커밋 메시지를 추가해보도록 하자.

    Copy
    git commit -m [커밋 메시지]

    이제 git log로 커밋 내역을 확인해보자.

    05

    로그를 살펴보면 HEAD -> main이라고 되어있는 것을 확인할 수 있다. 여기서 HEAD는 현재 브랜치를 가리키는 포인터이며, 지금의 HEAD가 가리키는 커밋은 바로 다음 커밋의 부모가 된다.

    여기서 다시! a.txt 파일의 내용을 수정한 후, git status를 실행해 보자.

    06

    여전히 빨간 글씨로 표시되지만, 처음에 a.txt를 만들어 Untracked 영역에 있을 때와는 달리 Changes not staged for commit이라는 메시지를 볼 수 있다. Tracked 영역, 즉 이미 git이 관리하고 있는 파일에 수정이 발생한 것이기 때문이다.

    이렇게 작업을 했던 워킹 디렉토리의 변경사항을 날리고 싶다면 git restore a.txt 명령어를 실행해준다.

    💡 git restore 명령어는 작업 디렉토리에서 수정한 파일을 되돌리는 명령어다. 이미 스테이징한 파일을 되돌릴 때는 git restore --staged 명령어를 사용하면 된다.


    🥊 리모트 저장소 이용하기

    remote

    git remote 명령어를 통해 로컬과 연결되어 있는 모든 저장소의 이름을 확인할 수 있다. git clone을 통해 리모트 저장소를 복제했다면 리모트 저장소 이름은 기본적으로 origin이 되며, git remote rename [이전 이름] [새로운 이름] 명령어로 로컬에서 사용할 리모트 저장소 각각의 이름을 바꿀 수 있다.

    git remote add [단축 이름] [URL] 명령어를 통해 기존 워킹 디렉토리에 새 리모트 저장소를 추가할 수도 있다.

    💡 유용한 git remote 옵션

    • git remote -v: 리모트 저장소들의 이름을 URL과 함께 볼 수 있다.
    • git remote show [리모트 저장소 이름]: 리모트 저장소의 구체적인 정보(브랜치 등)를 확인할 수 있다.

    push

    git commit으로 파일의 변경 이력을 로컬 저장소에 남겼다. 이제 리모트 저장소인 github에 코드의 변경 이력을 반영하기 위해서는 git push 명령어로 로컬 저장소의 내용을 전송해줘야 한다.

    git push 명령어는 기본적으로 리모트 저장소 이름과 브랜치 이름을 인자로 받는다.

    Copy
    git push [저장소 이름] [브랜치 이름]

    git push 명령어를 사용할 때마다 매번 저장소명과 브랜치명을 입력하는 게 귀찮다면, -u 옵션을 사용하여 최초 한번만 저장소명과 브랜치명을 입력하고 그 이후에는 인자들을 생략할 수 있다.

    Copy
    git push -u origin main

    git log로 확인해보면 아래와 같이 커밋에 빨간 글씨로 origin/mainorigin/HEAD가 추가된 것을 볼 수 있다.

    07

    fetch

    리모트 저장소에서 데이터를 가져올 때는 fetch 명령어를 사용한다.

    Copy
    git fetch [리모트 저장소 이름]

    이 명령은 로컬에는 없지만, 리모트 저장소에는 있는 데이터를 모두 가져온다. 이때 데이터를 ‘가져오기’만 하고 merge 시켜주지는 않는다.

    pull

    pull 명령어는 리모트 저장소의 브랜치에서 데이터를 가져올 뿐 아니라, 자동으로 로컬 브랜치의 현재 코드에 merge시켜준다. fetchmerge를 합친 명령어다.

    pull 명령어는 아래 브랜치 시스템을 이해하며 더 자세히 알아보자.


    🌴 브랜치 시스템 이해하기

    브랜치 만들고 이동하기

    모든 버전 관리 시스템은 브랜치를 지원하며, git 역시 훌륭한 브랜치 전략을 지원한다. 잘 사용하지 못하는 것은 우리의 책임인 것이다.

    git은 데이터를 change set이나 변경사항(diff)으로 기록하지 않고 일련의 스냅샷으로 기록한다는 점이 특징적이다. 새로 브랜치를 만들 때 프로젝트를 통째로 복사하는 대신, git의 브랜치는 어떤 한 커밋을 가리키는 SHA-1 체크섬 파일에 불과하기 때문에 생성과 삭제가 쉽고 효율적인 메모리 관리가 가능하다.

    브랜치를 직접 만들어보자.

    Copy
    git branch feat1

    위 명령어로 브랜치를 만들 수도 있지만, 브랜치 생성과 동시에 해당 브랜치로 이동까지 해줄 수도 있다.

    Copy
    git checkout -b feat1

    git은 지금 작업 중인 (로컬) 브랜치를 HEAD라는 특수한 포인터로 파악한다. 방금 전 feat1 브랜치로 이동했으므로 이제 HEADfeat1 브랜치를 가리키게 된다.

    08

    이 상태에서 ‘feat1.txt’ 파일을 생성하고 커밋을 새로 해보자.

    Copy
    git add feat1.txt
    git commit -m "create feat1.txt"

    이제 HEAD가 가리키는 feat1 브랜치가 main보다 한 단계 앞서있다.

    09

    다시 main 브랜치로 되돌아가보자.

    Copy
    git checkout main

    이제 main 브랜치가 가리키는 커밋을 HEAD가 가리키게 되었으며, 워킹 디렉토리의 파일도 그 시점으로 되돌려 놓았다.

    10

    💡 유용한 git branch 옵션들

    • git branch -r: 리모트 브랜치 목록 보기
    • git branch -a: 로컬 브랜치 목록 보기
    • git branch -m [이전 이름] [새 이름]: 브랜치 이름 바꾸기
    • git branch -d [브랜치 이름]: 브랜치 삭제하기

    브랜치 merge하기

    현재 main 브랜치에서 작업을 하다가 특정한 이슈를 처리하기 위해 feat1 브랜치를 만들었다.

    이때 갑자기 프로젝트에 문제가 생겨서 급하게 버그를 해결해야 하는 경우가 발생한다. hotfix 내용에 feat1의 작업 내역이 섞이는 것을 방지하기 위해 다시 main 브랜치로 돌아간 후 hotfix1 브랜치를 만들어 이동한다.

    Copy
    git checkout main
    git checkout -b hotfix1

    💡 이때 feat1에 아직 커밋하지 않은 파일이 있고, 해당 파일이 main 브랜치와 충돌이 난다면 main 브랜치로 checkout할 수 없다. 이때는 git stash 명령어를 통해 워킹 디렉토리를 정리할 수 있다.

    hotfix1 브랜치에서 버그 수정 후 커밋을 하면 브랜치 히스토리는 아래와 같이 그려질 것이다.

    11

    hotfix 내용을 운영 환경에 적용하기 위해 hotfix1 브랜치를 main 브랜치에 합쳐야 한다. 여기서 git merge를 사용한다.

    Copy
    git checkout main
    git merge hotfix1
    12

    hotfix1 브랜치가 가리키는 커밋이 main이 가지고 있는 커밋 히스토리를 모두 포함하고 있기 때문에 브랜치 포인터는 그저 최신 커밋으로 이동한다. 이렇게 단순히 브랜치 포인터를 이동하는 merge 방식을 Fast-forward(빨리감기)라고 부른다. 이때는 merge commit이 생기지 않는다.

    이제 mainhotfix1 브랜치 포인터는 같은 커밋을 가리키게 되었다.

    13

    이제 필요 없어진 hotfix1 브랜치는 삭제하고, 원래 작업하던 feat1 브랜치로 돌아가 작업을 계속한다.

    Copy
    git branch -d hotfix1
    git checkout feat1

    feat1에서 어느 정도 작업을 한 이후에는 해당 작업 내용을 main 브랜치에 merge해야 한다. 현재 브랜치 히스토리는 아래와 같다.

    14
    Copy
    git checkout main
    git merge feat1
    15

    hotfix1을 merge했을 때와는 메시지가 다르다. 현재 브랜치가 가리키는 커밋이 merge할 브랜치의 조상이 아니므로 (merge할 브랜치의 모든 커밋을 포함하고 있지 않으므로) Fast-forward merge가 불가능하다. 이 경우 git은 각 브랜치가 가리키는 커밋 두 개와 공통 조상 하나를 사용하여 3-way merge를 한다.

    16

    단순히 브랜치 포인터를 최신 커밋으로 옮기는 게 아니라, 3-way merge의 결과를 별도의 커밋으로 만든 후 해당 브랜치가 그 커밋을 가리키도록 이동시키는 것이다.

    이때 만약 hotfix1 브랜치와 feat1 브랜치가 같은 부분을 수정했다면 작업 내역에 충돌이 발생할 수도 있다. 이 경우 git은 자동 merge를 하지 못하며, 개발자가 Conflict를 해결해줘야 한다.

    충돌한 부분을 모두 해결하고 git add 명령어로 다시 git에 저장한 후, 커밋을 해주면 merge가 완료된다!

    리모트 브랜치와 싱크 맞추기

    지금까지의 작업은 모두 로컬 브랜치에서 진행한 내용이다. 이제 리모트 저장소로 가보자!

    리모트 트래킹 브랜치는 리모트 브랜치를 추적하는 레퍼런스이며 브랜치다. 리모트 트래킹 브랜치는 일종의 북마크로, 리모트 저장소에 마지막으로 연결했던 순간에 브랜치가 무슨 커밋을 가리키고 있었는지를 나타낸다.

    리모트 트래킹 브랜치의 이름은 [리모트 저장소 이름]/[브랜치 이름] 형식으로 되어 있다. github의 레퍼지토리를 clone 받는다면 git은 이 리모트 저장소에 자동으로 origin이라는 이름을 붙이고, main 브랜치를 가리키는 포인터를 만든다. 이 포인터가 바로 origin/main이며, 로컬의 main 브랜치는 origin/main을 가리키게 된다.

    17

    리모트 저장소의 main 브랜치에 누군가 다른 내용을 변경한 커밋을 push했다면 로컬의 main 브랜치와 히스토리가 서로 달라지게 된다.

    18

    이때는 우선 fetch 명령어로 리모트 저장소에 있는 브랜치의 내용을 가져온다.

    Copy
    git fetch origin

    💡 이때 리모트 저장소에서 가져온 최신 커밋 이력은 이름 없는 브랜치로 로컬에 불러와지며, 임시로 FETCH_HEAD라는 특수한 이름을 가진다. git checkout FETCH_HEAD 명령어로 리모트 저장소에서 가져온 업데이트를 확인해볼 수 있다.

    그러면 브랜치 히스토리는 아래와 같아진다.

    19

    fetch를 통해 서버의 데이터를 받아와서 저장했지만, 워킹 디렉토리의 파일 내용은 변경되지 않고 그대로 남아있다. 새로 받은 브랜치의 내용을 합쳐주기 위해서 merge를 실행한다.

    Copy
    git merge origin/main

    pull 명령어를 사용하면 fetchmerge를 한번에 해줄 수 있다.

    Copy
    git pull origin main

    💡 협업 시 사용했던 리모트 브랜치를 만든 후 작업을 마쳐 더 이상 해당 브랜치가 필요하지 않게 되었다면, git push origin --delete [브랜치 이름] 명령어로 리모트 브랜치를 삭제해준다.


    👩‍🔧 고통의 rebase

    고통의 rebase 단계에 진입했다…

    merge 말고도 git에서 한 브랜치에서 다른 브랜치로 합치는 방법이 있다. 바로 rebase다.

    20

    위 그림과 같이 브랜치가 분기되어있을 때, merge 명령어를 사용하여 합치면 3-way merge로 새로운 커밋이 생성된다.

    이때 C4의 변경 사항을 Patch로 만들고 이를 다시 C5에 적용시키는 방법이 있다. 이를 바로 rebase라고 한다. rebase 명령어로 한 브랜치의 변경 사항을 다른 브랜치에 적용할 수 있다.

    Copy
    git checkout feat1
    git rebase main

    실제로 rebase가 진행되는 과정은 다음과 같다.

    1. main, feat1 두 브랜치가 나뉘기 전인 공통 브랜치(C3)로 이동한 후 그 커밋부터 지금 checkout한 브랜치가 가리키는 커밋까지 diff를 차례대로 만들어 어딘가에 임시로 저장해 놓는다.
    2. rebase할 브랜치(feat1)가 합칠 브랜치(main)이 가리키는 커밋을 가리키게 하고, 아까 저장해 놓았던 변경사항을 차례대로 적용한다.

    rebase 이후의 브랜치 히스토리는 아래와 같다.

    21

    이제 main 브랜치도 C4' 커밋을 가질 수 있도록 main 브랜치를 Fast-forward시켜준다.

    Copy
    git checkout main
    git merge feat1
    22

    짠!✨ 새로운 머지 커밋 없이 히스토리가 예쁘게 정리되었다.

    💡 merge vs rebase

    • merge: 변경 내용의 이력이 모두 그대로 남아 있기 때문에 이력이 복잡해진다.
    • rebase: 이력은 단순해지지만, 원래의 커밋 이력이 변경된다. 정확한 이력을 남겨야 할 경우에는 사용하면 안 된다.

    rebase 방식을 사용할 때는 병합 충돌(merge conflict)이 발생하는 모든 커밋들에 대해 충돌을 일일이 해결해줘야 한다. 충돌난 커밋이 너무 많을 때는 rebase보다는 단순 병합(pull)을 하는 것이 더 나을 수도 있다.

    rebase onto

    rebase --onto는 다른 토픽 브랜치에서 갈라져 나온 토픽 브랜치와 같은 히스토리가 있을 경우에 사용할 수 있는 명령어다. server 브랜치를 만들어서 서버 기능을 추가하고 그 브랜치에서 다시 client 브랜치를 만들어 클라이언트 기능을 추가한다. 마지막으로 server 브랜치로 돌아가서 몇 가지 기능을 더 추가한다.

    아. 그림 그리기 너무 힘들다. 여기서부터는 출처서 가져온 그림을 그대로 활용하자. 그림의 mastermain으로 바꿔서 생각하면 된다. 아무튼 요즘 github은 main이라는 이름을 사용하고 있으니.

    23

    이때 server 브랜치는 그대로 두고 client 브랜치만 main으로 합치는 상황을 생각해보자. server와는 아무 관련 없는 client 커밋은 C8, C9이다. 이 두 커밋을 main 브랜치에 적용하기 위해서 --onto 옵션을 사용한다. 첫 번째 인자로는 새로운 조상이 될 베이스 커밋을 넘겨주고, 두 번째 인자로는 기존 베이스 커밋을 넘겨준다.

    Copy
    git rebase --onto main server client

    server 브랜치와 server 브랜치와 client 브랜치의 공통 조상까지의 커밋을 client 브랜치에서 없애고 그 이후의 client 커밋들을 main 브랜치를 새로운 베이스로 하여 rebase해준다. 어려워서 토나올 것 같지만, 분명 이런 게 필요한 경우도 맞닥뜨려봤다.

    24

    이제 main 브랜치로 돌아가서 Fast-forward시킬 수 있다.

    Copy
    git checkout main
    git merge client
    25

    이제 홀로 남겨진 server 브랜치의 작업이 마무리되면, server 브랜치로 이동 후 git rebase main 명령어로 servermain에 rebase시켜준다. 결과는 아래와 같다.

    26

    💡 Tip! git rebase [베이스 브랜치] [토픽 브랜치]로 인자를 입력하면, 토픽 브랜치로 checkout하지 않고도 rebase할 수 있다.

    Copy
    git rebase main server

    이제 다시 main 브랜치를 Fast-forward시키면 최종적인 브랜치 히스토리가 완성된다.

    Copy
    git checkout main
    git merge server
    27

    💡 rebase 시 주의사항 이미 공개 저장소에 push한 커밋을 rebase하면 안 된다. rebase는 기존의 커밋을 그대로 이용하는 것이 아니라, 내용은 같지만 다른 커밋을 새로 만든다. 새 커밋을 push하고 동료 중 누군가가 그 커밋을 pull해서 작업을 한다고 하자. 그런데 그 커밋을 git rebase로 바꿔서 push해버리면 동료가 다시 push했을 때 동료는 다시 merge해야 한다. 그리고 동료가 다시 merge한 내용을 pull하면 내 코드는 정말 엉망이 되어버린다. 🤯

    pull rebase

    리모트 저장소와 로컬 저장소가 각각 다른 변경 사항을 가지고 있는 경우, Fast-forward merge가 불가능하다. 단순 pull을 하는 경우 별도의 merge commit이 생긴다.

    Copy
    Merge branch 'main' of https://github.com/zigsong/git-commands-test

    이러한 병합 커밋을 만들지 않으려면 --rebase 옵션으로 rebase merge를 하면 된다. 이는 git fetchgit rebase 명령어를 순서대로 실행하는 것과 같다.

    Copy
    git checkout feat1
    git pull --rebase origin main

    이렇게 하면 로컬 저장소의 브랜치가 리모트 저장소의 브랜치의 최신 커밋으로 rebase되어 병합 커밋이 남지 않는다.

    rebase로 모든 병합 충돌을 해결했다면, 강제(force) 푸쉬를 해줘야 한다.

    Copy
    git push -f origin feat1

    강제 푸쉬를 하지 않으면 리모트 저장소의 feat1 브랜치를 pull 받아야 하는데, 그러면 로컬 저장소에서 rebase한 커밋과 리모트 저장소의 커밋이 중복되어 동일한 내용의 커밋이 생겨버리기 때문이다.


    🚪 작업 이력 되돌리기

    reset

    reset 명령어는 현재 HEAD를 특정한 상태로 되돌린다. 다만 checkout 명령어처럼 HEAD가 가리키는 브랜치를 바꾸지는 않으며, 계속 현재 브랜치를 가리키면서 현재 브랜치가 가리키는 커밋을 바꾼다.

    reset에는 크게 3가지 옵션이 있는데, 각 옵션을 통해 워킹 디렉토리(working directory)스테이징 영역(staging area), 그리고 현재 브랜치를 가리키는 HEAD 를 어떻게 업데이트할 것인지 결정할 수 있다.

    첫 번째로, reset--soft 옵션을 붙여 실행하면 워킹 디렉토리스테이징 영역은 건드리지 않고 브랜치가 가리키는 커밋만 이전으로 되돌린다.

    Copy
    git reset --soft HEAD~1

    💡 HEAD 뒤에 오는 숫자는 돌아가고 싶은 커밋의 개수이다. HEAD~1 대신 특정 커밋의 해쉬를 입력해도 된다. 바로 직전 커밋으로 돌아가고 싶다면 HEAD^를 사용할 수 있다.

    다음으로, reset 명령을 실행할 때 아무 옵션도 주지 않았을 때는 기본적으로 --mixed 옵션을 생략한 것과 같다. 이때 스테이징 영역을 현재 HEAD가 가리키는 스냅샷으로 업데이트할 수 있다.

    Copy
    git reset [--mixed] HEAD~1

    이 경우 워킹 디렉토리는 건드리지 않지만, 스테이징 영역을 비운다. git commit 명령과 git add 명령을 모두 되돌리는 것이다.

    마지막으로 reset--hard 옵션을 사용하면 워킹 디렉토리까지 되돌린다.

    Copy
    git reset --hard HEAD~1

    --hard 옵션을 사용한 경우 워킹 디렉토리의 파일까지 강제로 덮어쓰게 되며, 결과를 되돌리는 것이 불가능하므로 주의해서 사용해야 한다.

    reset 명령어를 정리하면 다음과 같다.

    1. HEAD가 가리키는 브랜치를 옮긴다. (--soft 옵션이 붙으면 여기까지)
    2. 스테이징 영역을 HEAD가 가리키는 상태로 만든다. (--hard 옵션이 붙지 않았으면 여기까지)
    3. 워킹 디렉토리를 스테이징 영역의 상태로 만든다.

    💡 이때 reset을 취소하고 싶다면? > git reflog를 통해 작업 내역을 확인한 후, 몇 번째 HEAD로 돌아갈지 선택한 후 아래와 같이 실행한다.

    Copy
    git reset HEAD@{1}

    revert

    reset 명령어는 github과 같은 온라인 원격 저장소에 올라가지 않은 상태의, 로컬 커밋을 되돌리는 명령어다. 작업 내역을 원격 저장소에 올린 경우라도 해당 브랜치를 자기 자신만 사용하고 있다면 문제가 되지 않겠지만, 다른 동료도 해당 브랜치를 사용하는 경우 함부로 reset을 해서는 안 된다. 이때는 revert를 사용한다.

    revertreset과 달리 커밋을 삭제하는 것이 아니라, 커밋을 추가한다. 커밋을 추가하기 때문에 아래와 같은 메시지가 추가된다.

    Copy
    Revert '...'

    revertreset과 마찬가지로 soft, hard 등의 옵션이 붙을 수 있다.

    아래의 예시를 살펴보자.

    Copy
    git commit -m "1번 커밋"
    git commit -m "2번 커밋"
    git commit -m "3번 커밋"
    
    git revert [1번 커밋의 해쉬]

    위처럼 명령어를 실행하면 ‘1번 커밋’ 이후의 커밋들이 삭제되는 것이 아니라, ‘1번 커밋’에 해당하는 내용만 삭제된다. 그리고 revert 커밋이 남게 된다. git log 명령어를 통해 확인할 수 있다.

    Copy
    Revert "1번 커밋"
    3번 커밋
    2번 커밋
    1번 커밋

    여러 개의 커밋을 되돌릴 때는 아래와 같이 사용한다.

    Copy
    git revert [from 커밋의 해쉬]..[to 커밋의 해쉬]

    resetrevert의 차이를 그림으로 정리해 보자.

    28 29

    reset과 달리 revert는 중간 커밋만 삭제할 수 있고, 이전으로 되돌린 커밋의 이력이 모두 커밋 메시지를 통해 남기 때문에 히스토리 유지 차원에서 더 유용하다고 할 수 있다.


    ☀️ 추가! Pull Request & Merge PR

    github에서 PR(Pull Request)을 보내는 경우, merge하려는 베이스 브랜치와 충돌이 없다면 Merge Pull Request 버튼이 활성화된다. 이때 오른쪽의 역삼각형 모양을 눌러보면, 아래와 같이 3가지 옵션이 표시된다.

    30

    팀 프로젝트를 할 때 항상 헷갈렸던 세 가지 옵션들에 대해 살펴보자!

    🔖 Create a merge commit

    PR의 베이스가 되는 main 브랜치와 토픽 브랜치(여기서는 feat1이라고 하겠다.)의 작업 이력이 다르다면 새로운 merge commit을 생성하게 된다. 히스토리는 아래 그림과 같다.

    31

    🔖 Squash a merge

    squash는 한국어로도 스쿼시란다. ㅡㅡ

    32

    ‘찌부러뜨리다’라는 말 정도로 해석이 가능한데, 여기서는 PR에 딸려 있는 모든 커밋들을 하나로 합쳐 새로운 커밋으로 만들어 베이스 브랜치인 main에 추가시키는 방식이다. 토픽 브랜치의 커밋 히스토리를 합쳐서 깔끔하게 만들기 위해 사용한다. 역시나 새로운 merge commit이 추가된다.

    33

    🔖 Rebase a merge

    PR에 포함된 모든 커밋들이 합쳐지지 않고 각각 main 브랜치에 추가된다. 각 commit은 모두 하나의 부모를 가진다. 이때 새로운 merge commit을 생성하지 않기 때문에 커밋 히스토리를 하나로 깔끔하게 만들 수 있다. (다만 하나의 브랜치에서 작업한 것처럼 보이게 된다!)

    34

    이렇게 어떤 방법으로든 스무스하게 merge가 되면 좋겠지만… 그렇지 못한 경우는 늘상 발생한다. main 브랜치에서 파생한 feat1 브랜치에서 작업 후 main 브랜치를 베이스로 PR을 열어놓은 사이, main 브랜치에서 파생한 또다른 브랜치인 feature2의 작업 내역이 먼저 PR merge됐다면 어떨까?

    (자동 merge가 불가능하다는 회색 버튼이 뜬다 - 사진을 추가해야할 것이다)

    이때 github 상에서 conflict을 해결해줄 수도 있지만, 그러면 새로운 merge commit이 생기는 만큼(확실한가?) 다른 방식을 생각해보게 될 것이다. 이때 git rebase가 다시 한번 등장한다.

    (다음 시간에…)


    🎸 기타

    git alias

    git에서 사용할 명령어를 별칭으로 기록해두는 거~ (작성중)

    .gitignore

    git이 관리할 필요가 없는 파일(ex. 로그 파일, 환경변수 파일)들은 .gitignore 파일에 작성하여 git의 관리 대상에서 제외시켜줄 수 있다. 리액트 프로젝트의 경우 용량이 큰 node_modules 폴더와 빌드 아웃풋인 dist 폴더를 .gitignore로 제외시켜주는 것이 일반적이다.

    Copy
    node_modules/
    dist/
    .env

    git stash

    워킹 디렉토리의 내용을 잠시 올려두는 거~ (작성중)

    cherrypick

    특정 브랜치에서 작업을 하던 도중, 다른 브랜치에서 특정 커밋만을 가져올 수 있다.

    Copy
    git checkout feat1
    git log

    feat1 브랜치의 ...번 커밋을 feature2 브랜치로 가져오고 싶을 때 cherrypick 명령어를 사용한다.

    Copy
    git checkout feature2
    git cherry-pick `...`

    git tag

    태그로 프로젝트 히스토리에 버전을 명시해주는 거~ (작성중)


    Ref

    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


    zigsong

    지그의 개발 블로그

    RotateLinkImg-iconRotateLinkImg-iconRotateLinkImg-icon