Git

khxxjxx·2022년 8월 24일
0

1. Git이란?

  • 형상 관리 도구 중 하나로 형상 관리 도구는 버전 관리 시스템이라고도 합니다.
  • 소스코드를 주고 받을 필요 없이 같은 파일을 여러 명이 동시에 작업하는 병렬 개발이 가능하기 때문에 효율적인 협업이 가능합니다.

    버전 관리 시스템은 파일 변화를 시간에 따라 기록했다가 나중에 특정 시점의 버전을 다시 꺼내올 수 있는 시스템입니다.

SVN vs Git

  • 버전 관리 툴은 현재 크게 중앙집중식(Subversion, SVN)과 분산관리식(Git) 두가지로 나뉘어집니다.
  • 이 둘은 별도의 비용이 필요하지 않는 오픈소스 툴이고 누구나 무료로 사용할 수 있어 대다수의 프로그래머들이 이들 두개중 한개를 사용중입니다.

SVN

  • 중앙 서버에 소스코드와 히스토리를 저장
  • 로컬 PC에서 Commit을 하면 바로 중앙저장소에 반영

Git

  • 소스코드를 여러 개발 PC와 저장소에 분산해서 저장
  • 로컬 PC에서 Commit을 하면 로컬저장소에 반영되고 로컬저장소에서 push를 하면 원격저장소에 반영

Git을 사용하는 이유

  • 모든 작업이 로컬에서 이루어지고 네트워크 사용은 원격 저장소로 저장할 때 한번 이루어지므로 개발 시 처리속도가 빠릅니다.
  • 중앙 저장소에 에러가 생기면 모든 작업이 마비되는 SVN과 다르게 분산 버전 관리이기 때문에 인터넷이 연결되지 않은 곳에서도 개발을 진행할 수 있으며 중앙 저장소가 날라가버려도 다시 원상복구할 수 있습니다.
  • 사본을 로컬에서 관리하기 때문에 SVN에 비해 가볍고 훨씬 빠릅니다.

Git 호스팅 서비스

  • GitHub, Gitbucket, Gitlab

2. Git 내부 동작 원리

Git 저장소

  • 말 그대로 파일이나 폴더를 저장해두는 곳으로 local 저장소와 remote(원격) 저장소로 나뉩니다.
    • local repository : 나의 pc에 저장되는 개인 전용 저장소
    • remote repository : 원격 저장소 전용 서버에 저장되는 저장소
  • 평소에는 내 pc의 로컬 저장소에서 작업하다가 작업한 내용을 공개하고 싶을 때 원격 저장소에 업로드합니다.
  • 반대로 원격 저장소에서 다른 사람이 작업한 파일을 로컬 저장소로 가져올 수도 있습니다.

index

  • git에서는 commit을 하기 전에 그 사이의 공간인 index라는 공간에 파일의 상태를 기록하게 되어있습니다.
  • 그 이유는 만약 두개의 파일을 생성하고 작업하고 있을 때 하나의 작업이 덜 된 상태에서 필요에 의해 작업이 완료 된 상태의 파일만 저장소에 올리고 싶을 때가 있습니다. 이 때 필요한게 인덱스 입니다. 자신의 로컬 저장소에서 변경/작업한 파일 중 선택하는 작업을 인덱스에 등록 또는 스테이징이라 표현합니다.
  • $ git add를 하면 index파일이 생겨나고 git add를 할 때마다 index에 기록이 업데이트 됩니다.

objects

  • 여러 종류의 객체가 보관되어 있는 폴더로 뭔가를 기억할 필요가 있을 때마다 하나씩 생겨납니다.
  • 각 객체에는 고유한 키 값 40자 중 2자는 폴더명으로, 38자는 파일명으로 두어 식별자로 활용하고 Git은 이 키값을 이용해 모든 객체를 구별하고 포인터처럼 활용하기도 합니다.
  • 저장되는 파일의 형태는 4가지로 blob, tree, commit, tag가 있습니다.
  • 내용을 보고 싶으면 $ git cat-file –p <객체명> 명령어를 통해 확인할 수 있습니다.

    위에서 설명한 식별자는 체크섬이라고 불리며 체크섬은 SHA-1해시를 사용해서 구하기 때문에 해시라고도 부릅니다.

blob

  • 파일을 $ git add 할때마다 생겨나며 그때의 내용이 그대로 스캔되듯 담겨있습니다. 파일의 메타 데이터를 저장하지 않고 데이터 자체만을 저장합니다.
  • index에 저장되는건 해당 blob의 id라고 생각하면 됩니다.

tree

  • $ git commit 하는 순간 스테이지(index)에 있던 작업 파일들의 id와 파일 식별자, 파일의 이름이 저장됩니다.
  • 파일 식별자는 100644(읽기 파일), 100755(실행 파일), 040000(폴더) 세가지로만 구성됩니다.

commit

  • 각각의 커밋별로 하나의 커밋 파일로 저장됩니다. 이때 트리의 id, 부모커밋의 id, author, committer, 커밋 메시지의 정보가 저장됩니다.
  • 부모커밋의 id는 직전 커밋을 의미합니다.

tag

  • $ git tag 할 때 생성되며 commit의 id, tag 이름, tagger, 태그 메시지의 정보가 저장됩니다.

3. Git 설치와 초기 설정

Git 설치

  • Linux 또는 macOS 환경에서는 대부분 이미 Git이 설치되어 있습니다.
  • 터미널을 실행 후 $ git 명령어를 실행합니다.
  • 만약 위와 같이 실행이 되지 않다면 https://git-scm.com/downloads 사이트 접속 후 본인에게 맞는 설치 파일을 다운로드 하고 실행합니다.

Git

  • Linux 또는 macOS => Terminal 실행
  • Windows => 시작메뉴 ➝ Git ➝ Git Bash 실행

Git 초기 설정

  • 저장소에 코드를 반영할 때 등록될 사용자 정보를 설정합니다.
  • 만약 프로젝트 마다 다른 사용자 정보를 지정하고 싶으면 저장소를 생성 후 --global 옵션을 빼고 실행해줍니다.
  • $ git config --list 명령어를 통해 앞에서 설정한 내용을 확인할 수 있습니다.

4. Git 저장소 생성

기존의 디렉토리를 git repository로 생성

  • git을 사용할 프로젝트 폴더로 이동 후 $ git init 명령어를 실행합니다.
  • 이후 $ ls -al 명령어를 실행하면 .git 디렉토리가 생성되는것을 확인할 수 있습니다.
  • 현재 상태는 로컬 저장소만 생성된 것이므로 로컬 저장소에 원격 저장소를 연결하기 위해 git 호스팅 사이트에서 원격 저장소를 만든 후 $ git remote add <원격 저장소명> <원격 저장소 주소> 명령어를 실행합니다.

    원격저장소 역시 git 저장소입니다. 다만 working directory가 존재하지 않으며 이를 bare 저장소라고 부릅니다.

  • $ git remote 명령어를 이용하면 연결된 원격 저장소 이름들을 확인할 수 있고 -v 옵션을 사용하면 지정한 저장소의 이름과 주소도 함께 볼 수 있습니다.
  • $ git remote rename origin git_test 명령어를 통해 이미 만들어진 원격 저장소 이름을 origin 에서 git_test로 변경할 수도 있습니다.
  • 만약 주소가 변경되었거나 필요 없어진 저장소는 $ git remote rm <원격 저장소명> 명령어를 통해 삭제 할 수 있습니다.

    여기서 origin이란
    특별한 의미를 가진것은 아니고 clone시 자동으로 만들어주는 원격 저장소 이름이 origin이기 때문에 기본적으로 원격 저장소의 이름을 origin으로 사용하는 것이 일반적입니다.
    클론시 $ git clone -o <원격 저장소명> -o 옵션을 주어 사용자가 이름을 지정할 수도 있습니다.

기존의 git repository를 복사

  • 깃랩 또는 깃허브의 원하는 프로젝트에서 clone 버튼을 눌러 원격 저장소의 주소를 복사합니다.
  • $ git clone <원격 저장소 주소> 명령어를 이용하여 복사해줍니다.
  • 기본적으로 clone을 하게 될 경우 default 브랜치의 내용을 복사하지만 원하는 브랜치의 내용을 복사하고 싶은 경우 -b <브랜치명> 옵션을 추가할 수 있습니다.
  • 또한 clone시 별도의 폴더명을 지정해 주지 않으면 원격 저장소의 이름으로 새로운 폴더가 생기고 해당 폴더는 로컬 저장소로 사용되게 됩니다.
  • 새로운 폴더를 만들지 않고 현재 폴더를 로컬 저장소로 사용하기 위해선 clone 시 뒤에 .(점)을 붙일 수 있고 또는 원하는 폴더명을 사용할 수 있습니다.

    clone은 변경 이력도 함께 로컬 저장소에 복제되어 오므로 원래 원격 저장소와 똑같이 이력을 참조하고 커밋을 진행할 수 있습니다.


5. Git 저장소 반영

  • 저장소 생성이 완료되면 저장소에 파일을 저장할 수 있게 되는데 이때 git은 세가지 영역이 존재합니다.
  • 현재 작업하고 있는 곳이 working directory가 되며 해당 영역에서 작업한 내용을 $ git add 명령어를 통해 staging area(index)로 보내줄 수 있습니다.
  • 이후 $ git status 명령어를 통해 준비영역의 어떤 파일이 변경되었는지 등의 파일 상태를 확인할 수 있습니다. (-s 옵션을 사용하면 현재 상태를 간단히 볼 수 있습니다.)
  • git_test1.txt 파일을 staging 하였으므로 이제 무엇을 수정하고 추가했는지 메시지를 남겨 저장소에 저장하는 작업을 진행합니다.
  • $ git commit -m <메시지> 명령어를 사용하여 .git 저장소 내에 staging 파일을 저장합니다. 반영한 내용을 추후에 쉽게 알 수 있도록 적절한 메세지를 넣어줍니다.

    앞서 적은 메세지에 오타가 있거나 누락된 내용이 있을 경우 $ git commit --amend 명령어를 사용할 수 있습니다. 해당 명령어를 실행하면 텍스트 편집기가 실행되고 수정하고 싶은 부분을 수정 후 저장하면 그대로 반영됩니다. (편집기에서 i를 입력하면 수정모드로 바뀌고 수정이 끝나면 esc를 누른후 :wq 을 입력하면 현재 내용이 저장되고 편집기가 종료됩니다.)

  • 저장소 반영 내역이 궁금하면 git log 명령어를 통해 확인할 수 있습니다.

    git status : staging file들의 상태 확인
    git log : .git repository에 존재하는 history 확인

git log의 대표적인 옵션들

  • -p, --patch : 각 commit 의 수정 결과를 보여줍니다.
  • -n : 상위 n개의 commit만 보여줍니다.
  • --stat : 어떤 파일이 commit에서 수정되고 변경되었는지 파일 내 라인이 추가되거나 삭제되었는지 확인합니다.
  • --pretty=oneline : 각 commit을 한 줄로 출력합니다.
  • --graph : commit 간의 연결된 관계를 아스키 그래프로 출력합니다.
  • -S <특정 텍스트> : 코드에서 추가되거나 제거된 내용 중 특정 텍스트가 포함되어 있는지 검사합니다.

$ git log -p 와 비슷한 명령어로 $ git diff 명령어가 있습니다.git diff 명령어는 현재 작업중인 Working Directory와 Staging Area 사이의 차이를 확인할 수 있습니다.


6. Git 저장소 공유

pull

  • $ git pull <원격 저장소명> <브랜치> : 원격 저장소에서 데이터 가져오기 + 병합(merge)
  • 원격 저장소에서 데이터를 가져와 로컬 데이터와 병합합니다.

fetch

  • $ git fetch <원격 저장소명> <브랜치> : 원격 저장소에서 데이터 가져오기
  • 원격 저장소에서 데이터를 가져오지만 병합하지 않습니다. 진행중인 작업을 마무리하고 병합해주어야 합니다.

push

  • $ git push <원격 저장소명> <브랜치> : 로컬 저장소에서 데이터 보내기
  • 로컬 저장소에서 작업한 내용을 원격 저장소에 반영합니다. 다른사람이 먼저 push 한 상태에서는 push 할 수 없기때문에 다른 사람이 작업한 것을 merge부터 해줍니다.

7. Git Branch

  • 독립적으로 어떤 작업을 진행하기 위한 개념입니다.
  • 각각의 Branch는 다른 Branch의 영향을 받지 않습니다.

Git Flow

  • Git-flow에는 5가지 종류의 브랜치가 존재합니다. 항상 유지되는 메인 브랜치들(master, develop)과 일정 기간 동안만 유지되는 보조 브랜치들(feature, release, hotfix)이 있습니다.
    • master : 제품으로 출시될 수 있는 브랜치
    • develop : 다음 출시 버전을 개발하는 브랜치
    • feature : 기능을 개발하는 브랜치
    • release : 이번 출시 버전을 준비하는 브랜치
    • hotfix : 출시 버전에서 발생한 버그를 수정 하는 브랜치
  • master 브랜치는 git 저장소를 생성하면 생기는 default 브랜치로 처음에는 master 브랜치에서 develop 브랜치 생성해 두개의 브랜치를 준비합니다.
  • develop 브랜치에서는 상시로 버그를 수정한 커밋들이 추가됩니다. 만약 새로운 기능 추가 작업이 있는 경우 develop 브랜치에서 feature 브랜치를 생성합니다.
  • feature 브랜치는 언제나 develop 브랜치에서부터 시작하며 기능 추가 작업이 완료되면 feature 브랜치는 develop 브랜치로 merge 합니다.
  • develop 브랜치에 이번 버전에 포함되는 모든 기능이 merge 되었다면 QA를 하기 위해 develop 브랜치에서 release 브랜치를 생성합니다.
  • QA를 진행하면서 발생한 버그들은 release 브랜치에서 수정되고 QA를 무사히 통과했다면 release 브랜치를 master와 develop 브랜치로 merge 합니다.
  • 마지막으로 출시된 master 브랜치에서 버전 태그를 추가합니다.
    • 태그란 커밋을 참조하기 쉽도록 알기 쉬운 이름을 붙이는 것을 말합니다. 한번 붙인 태그는 브랜치처럼 위치가 이동하지 않고 고정됩니다.
    • git 에서는 일반적으로 이름 정보만을 갖는 태그와 보다 상세한 정보를 포함하는 주석 태그 이 두가지 태그를 사용할 수 있습니다.
      • 일반 태그 : 이름만 붙일 수 있습니다.
      • 주석 태그 : 이름, 설명, 서명, 만든사람, 이메일, 날짜 정보를 포함시킬 수 있습니다.
    • 보통 릴리스 브랜치에서는 주석태그를 사용하여 설명이나 서명을 넣은 보다 상세한 정보를 포함하는 태그를 사용하고 로컬에서는 일시적으로 사용하는 토픽 브랜치 에서는 이름만 만들어 붙이는 태그를 사용합니다.
  • 처음에 master 브랜치에서 브랜치를 만들때 Not a valid object라는 오류를 확인할 수 있습니다. 이는 아직 한번도 commit 을 하지 않은 repository이기 때문에 생기는 오류로 최소 1번이상 commit 을 진행해주면 됩니다.

여기서 master 브랜치는 default 브랜치라고 설명했는데 git 2.28부터 default branch를 main으로 바꿀 수 있게 되었습니다.(master/slave 같은 차별적인 용어 대신에 main이라는 용어를 사용하게 바뀜)
위에서 git 저장소 생성하는 방법을 설명할때 사용한 사진을 보면 git init을 통해 생성한 저장소의 경우 default 브랜치로 master 브랜치가 생성되는 반면 git clone을 통해 복사한 브랜치의 경우 main으로 생성되어 있는 것을 볼 수 있습니다.
git init 명령어를 통해 로컬에서 저장소를 생성시 생성되는 default 브랜치를 main으로 변경하기 위해서 $ git config --global init.defaultBranch main 명령어를 사용할 수 있습니다.
만약 이미 init 명령어를 통해 저장소를 생성한 후라면 $ git branch -M main 명령어를 통해 변경도 가능합니다.
원격 저장소의 default 브랜치를 변경하는 방법은 저장소 설정을 통해 변경할 수 있습니다.

원격 저장소의 default 브랜치를 변경한 후 만약 해당 저장소를 clone 한 로컬 저장소가 있다면 아래의 명령어를 통해 트래킹 브랜치를 변경해주면 됩니다.

$ git branch -m master main
$ git fetch origin
$ git branch -u origin/main main
$ git remote set-head origin -a

브랜치 생성

  • $ git branch <브랜치명> 명령어를 통해 브랜치를 생성할 수 있고 생성된 브랜치는 $ git branch 명령어를 통해 확인할 수 있습니다.

    $ git branch -r 옵션을 주면 원격 저장소의 branch 리스트를 볼 수 있고, -a 옵션을 주면 로컬, 원격 저장소의 branch 리스트를 볼 수 있습니다.

브랜치 전환

  • $ git checkout <브랜치명>
  • checkout 명령어는 branch를 전환하는데 사용할 수도 있지만 log로 확인한 snapshot을 넘나들때도 사용이 가능합니다.
  • commit 문구 뒤의 해시코드를 앞에서 4자리 이상 복사 한후 브랜치명 대신 sanpshot hash 코드를 넣어줍니다.

    $ git checkout -b <브랜치명> 명령어를 이용하면 브랜치 생성과 함께 브랜치 전환이 함께 됩니다.

stash

  • 커밋하지 않은 변경 내용이나 새롭게 추가한 파일이 인덱스와 작업 트리에 남아 있는 채로 다른 브랜치로 전환하면 그 변경 내용은 기존 브랜치가 아닌 전환된 브랜치에서 커밋할 수 있습니다.
  • 단, 커밋 가능한 변경 내용 중에 전환된 브랜치에서도 한 차례 변경이 되어 있는 경우에는 체크아웃에 실패할 수 있습니다. 이 경우 이전 브랜치에서 커밋하지 않은 변경 내용을 커밋하거나 stash 를 이용해 일시적으로 변경 내용을 다른 곳에 저장하여 충돌을 피하게 한 뒤 체크아웃을 해야 합니다.
  • $ git stash 를 사용하면 작업 트리와 인덱스내에서 아직 커밋하지 않은 변경을 일시적으로 저장해 둘 수 있습니다. 이 stash에 저장된 변경 내용은 나중에 다시 $ git stash pop으로 불러와 원래의 브랜치나 다른 브랜치에 커밋할 수 있습니다.

Git 2.23 버전부터 checkout을 대신할 switch, restore가 도입되었습니다.
checkout : Switch branches or restore working tree files
switch : Switch branches
restore : Restore working tree files
이전에는 checkout으로 브랜치를 변경했다면 이젠 switch로 변경합니다.(명령어만 달라졌습니다.)
또한, 브랜치를 새로 만들면서 브랜치 변경까지 하는 checkout -b 명령어도 -c 옵션으로 똑같이 사용할 수 있습니다. 브랜치를 어디서 만들지 지정하지 않으면 HEAD가 사용되는데, 특정 브랜치나 커밋에서 새로운 브랜치를 만들고 싶으면 브랜치 이름 뒤에 커밋을 지정해 주면 됩니다. $ git switch -c new master
restore는 워킹 트리의 파일을 복원해주는 역할을 합니다. 이전에는 파일의 수정 내용을 복원하려면 $ git checkout -- <파일이름> 명령어를 사용했는데 이젠 명확하지 않은 checkout -- 보다는 restore를 사용합니다. 또한, 아래에서 배울 reset도 restore로 들어왔습니다.

브랜치 삭제

  • $ git branch -d <브랜치명> 명령어를 통해 로컬 브랜치를 삭제할 수 있습니다.
  • 간혹 브랜치 삭제시 정상적으로 merge가 되어있지 않거나 충돌이 해결되지 않은 브랜치의 경우 에러와 함께 브랜치 삭제가 안되는데 이때 강제로 삭제를 원할 경우 -D 옵션을 사용하면 됩니다.

    원격 저장소에 올라가 있는 브랜치를 삭제 할 경우엔 $ git push <원격 저장소명> -d <브랜치명> 명령어를 사용할 수 있습니다.

Upstream 브랜치

  • 최초로 원격 저장소에 push할 경우 위와 같은 메시지를 볼수 있는데 이는 원격저장소에 위치한 브랜치를 추적하기 위한 로컬 저장소의 브랜치가 정해지지 않았다는 의미로 --set-upstream 옵션을 사용하여 pull/fetch, push를 진행할 기본 브랜치를 지정할 수 있습니다.
  • upstream 브랜치를 따로 지정하지 않으면 저장소의 데이터를 공유할때 $ git push origin master 와 같이 원격 저장소명과 브랜치를 같이 명시해줘야합니다.
  • 하지만 upstream 브랜치를 한번 지정해주면 원격 저장소명과 브랜치명을 생략할 수 있습니다.

    트래킹 하는 대상 브랜치를 Upstream 브랜치라고 부릅니다.

브랜치를 새로 만드는 경우

  • $ git branch --track <브랜치명> <upstream할 원격 저장소명/브랜치명>
  • --track 옵션을 사용하여 브랜치를 생성할 수 있습니다.

이미 브랜치가 있는 경우

  • $ git branch --set-upstream-to=<upstream할 원격 저장소명/브랜치명> <브랜치명> or $ git branch -u <upstream할 원격 저장소명/브랜치명> <브랜치명>
  • --set-upstream-to, -u 옵션을 사용하여 upstream 브랜치를 지정해줄 수 있습니다.

push시 지정

  • $ git push --set-upstream <원격 저장소명> <브랜치명> or $ git push -u <원격 저장소명> <브랜치명>
  • --set-upstream, -u 옵션을 사용하여 upstream 브랜치를 지정해줄 수 있습니다.

$ git branch -vv 명령어를 통해 어떤 브랜치가 어떤 upstream 브랜치를 추적하는지 확인할 수 있습니다.


8. Merge

  • 새로운 브랜치(test-branch)를 만들고 파일을 생성한 후 commit을 합니다.
  • 이것을 master 브랜치와 통합하기 위해서 checkout을 통해 master 브랜치로 이동한 후 $ git merge <브랜치명(test-branch)> 명령어를 입력하면 test-branch의 내용이 master 브랜치에 합쳐집니다.

fast-forward

  • 분기한 브랜치의 커밋 히스토리가 기존 브랜치의 커밋 히스토리를 포함하고 있으면 fast-forward 관계라고 합니다.
  • fast-forward 관계일 때는 Merge commit이 만들어 지지 않고 HEAD의 위치만 변하게 됩니다.
  • 위의 사진을 보시면 test-branch가 fast-forward되어 헤드의 위치만 앞으로 변경된걸 보실 수 있습니다.

    HEAD란? 현재 브랜치의 마지막 커밋을 가리키는 snapshot입니다.
    여기서 snapshot은 staging 되어진 데이터들을 commit 명령으로 영구히 저장한 것을 말합니다.

non fast-forward

  • 분기한 브랜치의 커밋 히스토리가 기본 브랜치의 커밋 히스토리를 포함하고 있지 않은 경우이며 이 경우에 병합하게 되면 반드시 Merge commit이 생성됩니다. 또한 HEAD를 따라 가는 것이 아니라 브랜치가 실제로 병합됩니다.
  • 위의 사진을 보시면 test-branch2 브랜치에는 test-branch가 merge된 히스토리를 가지고 있지 않아 Merge commit이 생성되었고 log를 보면 브랜치가 합쳐지는 모습을 보실 수 있습니다.

fast-forward 병합이 가능하더라도 --no-ff 옵션을 주어 non fast-forward 병합을 실행할 수 있습니다.위에서 test-branch가 fast-forward되었었는데 이를 non fast-forward 옵션을 주어 merge하게 되면 위와같이 그래프가 달리지는걸 확인하실 수 있습니다.

merge conflict

  • 만약 각각의 브랜치에서 같은 파일을 변경하고 merge하면 충돌이 발생합니다.
  • git status 명령어로 어느 파일에서 충돌이 발생했는지 확인하고 충돌이 일어난 파일을 열어봅니다.
  • <<<<<< ====== >>>>>> 이렇게 되어 있는 부분이 있는데 해당 부분이 충돌이 일어난 부분으로 ====== 로 구분된 윗 부분이 현재 브랜치, 아랫 부분이 병합한 브랜치 입니다.
  • 수정 완료후 git add, git commit 과정을 거쳐 다시 merge 해줍니다.
  • merge 충돌을 방지하기 위해서 master 브랜치의 변화를 지속적으로 가져와서 충돌이 발생하는 부분을 제거해줍니다.

9. Rebase

  • Merge는 브랜치를 통합하는 것이고, Rebase는 브랜치의 base를 옮긴다는 개념의 차이가 있습니다.
  • 브랜치가 너무 많아져서 history 정리가 필요한 상황에 사용합니다.
  • 새로운 브랜치(test-branch)를 만들고 파일을 생성한 후 commit을 합니다.
  • 이 것을 master로 rebase하기 위해 현재 test-branch에서 $ git rebase master 명령어를 실행합니다.
  • rebase 명령어만 실행하면 위와 같이 test-branch(issue3)는 master 앞쪽으로 위치가 옮겨집니다.
  • master 브랜치에 이를 적용 시켜주기 위해 master 브랜치로 checkout을 한 후 test-branch(issue3)를 merge 해줍니다.
  • rebase를 통해 브랜치가 앞쪽으로 이동했기 때문에 merge는 fast-forward되고 git log를 보면 merge했을 때 커밋들이 여러갈래로 나뉘었던것이 rebase는 한줄로 깔끔하게 정렬된 것을 확인하실 수 있습니다.

    $ git rebase <base 브랜치명> <브랜치명> 명령어를 사용하면 checkout하지 않고 rebase할 수 있습니다.

rebase conflict

  • 마찬가지로 rebase시에 충돌이 일어날 수 있는데 merge conflict 때와 동일하게 처리합니다.
  • 차이점은 이후 git commit 이 아니라 $ git rebase --continue 명령을 사용합니다.
  • 만약 rebase 자체를 취소하려면 --abort 옵션을 지정해주면 됩니다.

10. Squash

  • squash는 여러개의 커밋을 하나로 합치는 기능을 말합니다. 이 기능은 merge할 브랜치의 커밋을 전부 하나의 커밋으로 합친 뒤 타겟 브랜치에 커밋하는 방식입니다.
  • 커밋 메시지는 중요한 정보를 담고 있고 다른사람들이 이 브랜치에서 무엇을 작업했는지 확인할 수 있는 수단인데 이것을 왜 하나로 합치는건지 의아할 수 있습니다.
  • 하지만 브랜치가 많은 상황에서 모든 커밋이 저장되면 오히려 복잡해 히스토리를 추적하는데 힘들 수 있습니다. 예로 커밋 메시지에는 단순히 오타를 고치는 것도 포함되어 있을 수 있기 때문입니다.
  • squash는 커밋이 하나만 남기 때문에 머지가 되었다는 사실을 히스토리 상에서 한번에 알아볼 수 있고 버전 별로 어떤 것이 변경 되었는지 한 눈에 알 수 있다는 것이 장점입니다.

merge squash

  • merge할 때 --squash 옵션을 추가해 줍니다. 이후 git commit -m 명령어를 통해 하나로 통합할 commit 메시지를 작성해줍니다.
  • test-branch2에서 작업했던 squash-test2, squash-test3 commit이 하나로 합쳐져 rebase와 같이 한줄로 표시된 모습을 보실 수 있습니다.

rebase squash

  • rebase 시에 -i 옵션을 줄 수 있습니다.
  • 해당 옵션을 주면 위와 같은 화면을 볼 수 있는데 위에 pick이라 적혀 있는 부분을 s 또는 squash로 변경해줍니다.
  • 저장하고 나오면 아래와 같은 화면이 뜨는데 원하는 commit 메시지가 있으면 변경해줍니다.
  • rebase해준 브랜치를 master에서 merge해준 후 log를 보면 squash merge와 같은 모습인것을 확인하실 수 있습니다.

11. 커밋 변경하기

revert

  • $ git revert <commitID> 명령어를 이용하면 이전 커밋 내역을 그대로 남겨둔 채 새로운 커밋을 생성합니다.
  • 즉 과거로 돌아가겠다는 이력을 남겨두고 원하는 시점으로 돌아갑니다.

reset

  • $ git reset --<option> HEAD~ 명령어를 이용하면 더 이상 필요 없어진 커밋들을 버릴 수 있습니다. 명령어 실행 시 옵션을 지정하여 HEAD의 위치와 인덱스, 작업 트리 내용을 함께 되돌릴지 여부를 선택할 수 있습니다.
  • 즉 아예 현재가 없었던 것 처럼 이전의 이력을 남기지 않고 원하는 시점으로 완전히 되돌아갑니다.
    • --soft : 예전 버전으로 돌아가 commit을 수정할 수 있습니다. 커밋만을 reset하기 때문에 working directory나 staging area까지 변경하지 않습니다.
    • --mixed(default) : working directory를 제외하고 변경한 staging area의 상태만 원래대로 되돌리고 싶을 때 사용합니다.
    • --hard : working directory와 staging area의 작업이 모두 되돌려집니다.
  • origin에 올린 상태에서 reset하고 push하게 되면 에러가 발생합니다. 이럴땐 --force 옵션을 주어 강제로 로컬 commit history를 origin commit history로 덮어쓰면 되지만 만약 해당 repo를 다른 사람들과 공유하고 있다면 절대 하면 안되는 행동입니다.

    reset 전의 커밋은 ORIG_HEAD라는 이름으로 참조할 수 있습니다. 실수로 reset을 한 경우에는 ORIG_HEAD로 reset하여 reset 실행 전의 상태로 되돌릴 수 있습니다.

cherry-pick

  • $ git cherry-pick <commitID> 명령어를 통해 다른 브랜치에 있는 커밋을 선택적으로 복사하여 현재 내 브랜치로 가져올 수 있습니다.
  • cherry-pick에서도 conflict가 발생할 수 있는데 이 경우 코드를 수정하고 git add, git cherry-pick --continue 명령어를 통해 다시 cherry-pick을 진행시킬 수 있습니다.
  • 또는 git cherry-pick --abort 명령어를 사용해 cherry-pick 명령을 중단할 수 있습니다.

    develop브랜치에서 feature브랜치를 따서 작업하는 중에 아직 develop브랜치에 적용되지 않은 다른 feature 브랜치의 내용을 가져와야 할때 유용하게 사용할 것 같습니다.


12. Pull Request (feat, Github)

  • Pull Request(PR)란 깃허브 저장소에 push 된 소스코드 변경 사항을 다른 개발자들에게 알리는 협업의 출발점입니다.
  • PR을 사용하면 코드 충돌을 최소화할 수 있고 push 권한이 없는 오픈 소스 프로젝트에 기여할 수 있습니다. 또, PR을 생성할 때 특정 사용자에게 리뷰를 요청하거나 브랜치 보호 기능 설정으로 승인 프로세스를 적용할 수도 있습니다.
  • 좋은 풀리퀘스트를 만들기 위해서 제목은 내가 한 작업을 간략히 요약해서 적고 내용에는 작업한 내용과 중점적으로 확인 받았으면 하는 부분 등을 상세하게 적습니다.
  • 이렇게 코드 리뷰를 통해 궁금한 부분에 대해 질문을 하거나 버그가 있는 부분을 알려주거나 더 효율적으로 개선할 수 있는 부분을 찾아 피드백을 주며 서로의 코드를 개선해나갈 수 있습니다.
profile
코린이

0개의 댓글