본문 바로가기
Git

Git - Learn Git Branching 정리

by 엑츄얼리 2022. 4. 25.

Git 커밋 소개

git 커밋

  커밋 : Git 저장소에 디렉토리에 있는 모든 파일에 대한 스냅샷을 기록

git commit

 

 - Git은 가능한 커밋을 가볍게 유지하고자 하기 때문에, 커밋할 때마다 디렉토리 전체를 복사하지 않음

   저장소의 이전 버전 (이전 커밋)과 다음 버전의 변경내역(delta)을 저장

   => 커밋이 그 커밋 위의 부모 커밋(이전 커밋)을 가리킴

 

 - 저장소를 복제(clone)할 때 모든 변경분(delta)를 풀어내도록 하는 명령어

resolving deltas

 

 

 

Git Branch

 브랜치 : 특정 커밋에 대한 참조(Reference)

            하나의 커밋과 그 부모 커밋들을 포함하는 작업 내역

git branch [새로운 브랜치명]

 - 많이 만들어도 메모리나 디스크에 부담을 주지 않음

   => 작업을 브랜치를 통해 작은 단위로 잘게 나누는 것이 좋음

 

* git checkout [존재하는 브랜치]

  새롭게 커밋이 들어올 때, [존재하는 브랜치]가 새로운 커밋을 가리키도록 함

   (사용자의 위치(*)를 존재하는 브랜치로 이동)

git checkout [존재하는 브랜치]

 

 

Branch Merge  

 Git Merge : 두 개의 parent를 가리키는 특별한 commit을 만듦

                => 한 부모의 모든 작업 내역과 나머지 부모의 모든 작업, 

                     그리고 그 두 부모의 모든 부모들의 작업 내역을 포함한다. (= 모든 저장소의 작업내역을 포함)

git merge

 

 - 

위와 같이 2개의 브랜치가 각기 다른 저장소를 가리키는 상황을 해결하기 위해 Git Merge를 사용한다.

위 상황에 아래의 명령어들을 입력해주면

git checkout bugFix;
git merge main;

2개의 브랜치가 Merge 되어 저장소의 모든 작업 내역을 포함하게 된다.

 

** 이해중

   위의 이미지와 아래의 이미지 둘 다 merge 명령을 수행한 이후인데,

   위의 경우 Merge를 통해 branch의 위치가 공유되는 방식이 아닌 사용자의 위치(*)를 포함한 branch가

    이전 2개의 브랜치를 가리키는 새로운 commit을 생성한다.

 

   아래의 경우 사용자 위치(*)를 bugFix로 이동한 후 Merge를 진행하였는데,

    이 경우에는 branch의 위치가 앞서 생성된 commit의 위치로 이동하였다.

 

   내 예상으로는 전자 실행의 경우 c2, c3을 가르키는 commit이 없었으므로 c4를 새로 생성하였고, 

   후자 실행의 경우 c2, c3을 가르키는 commit c4가 있으므로 branch의 위치가 옮겨지는 것으로 보이는 것 같다.

   이를 통해 merge 명령은 '사용자 위치를 포함한 branch가 merge 대상 branch의 영역을 포함하는

   새로운 commit을 생성한다. 만약 사용자 위치를 포함한 branch와 merge 대상 branch의 영역을 포함하는 영역을

   지칭하는 commit이 있으면 해당 commit으로 이동한다.'

   로 이해하면 될 것 같고, 이 명령은 대상 branch의 영역에는 영향을 주지 않는다.

 

git checkout -b bugFix

bugFix branch를 현재 사용자 위치(*)에 생성하며 사용자 위치를 생성된 bugFix로 이동

 

 

Rebase

 커밋들을 모아서 복사한 뒤, 다른 곳에 떨궈 놓는 것

 => 커밋들의 흐름을 보기 좋게 한 줄로 만들 수 있다는 장점 (저장소의 커밋 로그가 깨끗해짐)

 => 실제로는 두 기능을 따로 개발했지만, 마치 순서대로 개발한 것처럼 보임

git rebase [타겟 브랜치]

 

 - 

 

git rebase main

위와 같은 상태에서 rebase 명령어를 입력 시 아래와 같이 C3 commit을 main으로 rebase 하여 C3' commit을 생성

 

git checkout main;
git rebase bugFix;

main을 bugFix로 rebase 시키는 경우 main의 이전 commit인 C2를 C3' commit이 포함하고 있기 때문에

branch의 위치만 bugFix와 같은 C3' commit으로 이동하게 된다.

 

 

Head 분리하기 (여기저기로 옮겨 다니기)

 프로젝트를 표현하는 커밋 트리(commit tree)에서 이동할 수 있는 여러 가지 방법

 

 - HEAD : 현재 체크아웃(checkout)된 커밋, 즉 현재 작업 중인 커밋 (사용자의 위치(*))

             항상 작업 트리의 가장 최근 커밋을 가리킴

             (커밋을 가리킨다고 표현했지만, 실질적으로는 브랜치의 이름을 가리키고 있음)

 

 -

HEAD는 기본적으로 사용자의 위치(*)에 숨어 있으며 별다른 명령어가 없는 상태에서는 드러나지 않는다.

이는 C1 <- main <- HEAD의 상태임을 뜻하며

git checkout C1;

명령어를 통해 사용자의 위치(*)를 인위적으로 HEAD로 옮겨주면

HEAD 자체가 하나의 branch로 인식되어 C1 <- HEAD의 상태로 바뀌게 된다.

당연한 소리겠지만, 이 상태에서

git commit;

명령어를 수행 시 HEAD만 이동하여 아래와 같은 상태가 된다.

 

* 각 커밋은 해당 커밋의 해시값으로 특정 지을 수 있음

 

 

상대 참조

 여기서는 commit을 C*로 표현하고 있지만, 실제로는 해시값을 통해 구분된다.

  ex) fed2da64c0efc5293610bdd892f82a58e8cbc5d8 -> 커밋의 해쉬                                       * git log : 해시 확인

 <git checkout fed2>와 같이 해시의 앞쪽 일부분을 통해 접근할 수 있지만 번거로우므로

   이를 해결하기 위해 상대 참조를 사용

 상대 참조 시 참조된 commit은 HEAD를 통해 가리켜진다.

 

git checkout main^

 - 선택된 브랜치에서 한 커밋 위로 이동

git checkout [존재하는 브랜치]^

 

 - (~) 틸드 연산자 : 선택된 브랜치에서 num 만큼 위로 이동

git checkout [존재하는 브랜치]~[num]

  

 - [존재하는 브랜치명]의 위치를 강제로 [HEAD or 존재하는 브랜치명]~3의 위치로 이동

git branch -f [존재하는 브랜치명] [HEAD or 존재하는 브랜치명]~2

git branch -f main HEAD~2

 

 

작업 되돌리기

 실제 변경이 복구되는 방법 (개별 파일이나 묶음을 스테이징 하는 방식도 가능)

 

 - 로컬 저장소에서 사용 가능

git reset [HEAD or 존재하는 브랜치명]~<num>

git reset HEAD~1

 

C2커밋이 없었던 것과 같은 상태가 됨

 

 

 

 

 

 

 

 

 

 

 

 

 - 원격 저장소에서 사용 시 다른 사람이 작업하는 브랜치를 사용 불가하므로 reset 사용 불가능

git revert [HEAD or 존재하는 브랜치명]

git revert HEAD

 

C2 커밋의 내용과 반대되는 내용의 C2' 커밋이 생성됨 (C1의 상태로 만들기 위해)

 

 

 

 

 

 

 

 

 

 

 

* 작업 되돌리기 명령어들은 사용자 위치를 기준으로 함을 잘 이해하고 생각해야 된다.

 

 

git reset push

 reset 명령어 같은 경우 인자로 받는 branch 사용자 위치(*)에서 인자로 받는 branch까지를 되돌리는 방식이라,

  위와 같이 사용 시 C3 commit은 버려지고 local branch가 pushed branch로 이동된다.

  <git reset push^>를 사용 시 같은 원리로 C1으로 가고 C3는 버려지게 된다.

 

git revert pushed

 같은 원리로 사용자 위치를 기준으로 pushed를 되돌리기 하는 명령이므로, C2'은 C2의 내용과 반대되는 내용을 가진다.

 

 

작업 옮기기 - Git Cherry-Pick

git cherry-pick <Commit1> <Commit2> <...>

 현재 위치(*) 아래에 일련의 커밋들에 대한 복사본을 만듦

git cherry-pick C2 C4

 

 

Interactive Rebase

 원하는 Commit을 모르는 상황에 사용

 리베이스 명령에 <-i> 옵션 추가 시 git이 복사될 커밋들의 해시와 메시지를 UI를 통해 보여줌

  -> 이를 통해 원하는 Commit을 선택하여 rebase가능 (순서 변경도 가능)

 

* Rebase와의 차이점이 있다면, rebase는 중복되는 내용의 commit이 있으면 복사하지 않고 위치만 변경하지만,

 Interactive Rebase는 무조건 선택된 Commit을 선택된 위치에 복사

 

git rebase -i HEAD~4

 왼쪽의 상태에 git rebase HEAD~4를 실행하여도 아무런 일도 생기지 않는다.

 

 

커밋들 가지고 놀기

 - 커밋의 내용 정정

git commit --amend

 

 - Git Tag : 특정 커밋들에 대한 영구적인 Tagging

git tag [새로운 tag 이름] [Commit의 해시 값 or 상대 위치]

 

 - git describe : 현재 위치를 가장 가까운 tag에 대한 상대 위치로 표현

git describe <Commit의 해시 값, 상대 위치>

  <tag>_<numCommits>_g<hash>

  tag : 가장 가까운 부모 tag

  numCommits : tag와의 거리

  hash : describe 하고 있는 대상 Commit의 hash (tag Commit 아님)

 

 - git rebase <대상 Commit> <복사할 Commit> : 복사 후 현재 위치를 복사된 Commit으로 이동

    [대상 Commit]으로 [복사할 Commit]을 복사 (HEAD는 [복사할 Commit]을 가리킨다. 

    (간단하게 git checkout [복사할 Commit] 이후 git rebase [대상 Commit] 인 명령을 하나로 줄인 명령)

git rebase [대상 Commit] [복사할 Commit]

 

원격 저장소

 - 로컬 저장소에 원격 저장소를 복사하여 생성

git clone

 

원격 브랜치

 origin/main : 원격 브랜치 (특정한 목적, 특별한 속성)

 

  - 원격 브랜치로 checkout 시 분리된 HEAD로 사용자 위치가 이동됨 (원격 브랜치에서 직접 작업할 수 없기 때문)

 

  - 원격 브랜치를 통해 pull 했을 때, 원격 저장소에서의 브랜치 위치를 로컬 저장소에 저장하고,

      이 정보를 기반으로 push 했을 때 원격 저장소의 데이터를 갱신

 

  - <remote name>/<branch name> 의 형식으로 원격 브랜치의 이름이 지어진다.

   -> remote name : 원격 저장소의 이름

   -> branch name : 브랜치의 이름

 

 

Git Fetch

 원격 저장소에서 데이터를 가져오는 방식 (동기화) (http://, git://와 같은 프로토콜을 통해 접근)

 

 - 원격 저장소에는 있지만, 로컬에는 없는 커밋들을 다운로드

 

 - 원격 브랜치가 가리키는 곳을 업데이트

 

 * 로컬의 상태는 바뀌지 않는다.

   왜냐하면 commit을 다운로드하지만, branch를 다운로드된 commit들로 이동시켜주지 않기 때문에,

   로컬에서의 파일이나 파일 시스템은 변경되지 않는다.

 

git fetch

 로컬 저장소에서의 o/main은 pull, push 했을 때의 main의 위치를 기억한다.

 이를 기반으로 다음에 pull이나 push시에 작업을 최소화할 수 있으며, 추가적인 기능들도 많이 있을 것 같다.

 즉, 로컬 저장소에서의 main과 원격 저장소의 main은 같지만, 별개로 움직이게 되며,

      Push를 진행할 때, 로컬 저장소의 위치를 기반으로 원격 저장소의 main이 변경된다. (둘의 위치가 같아진다.)

 

 

Git Pull

 git merge + git fetch

 

git fetch;
git merge o/main;

위와 같이 2개의 명령어는

git pull

과 같은 역할을 수행한다.

 

 

협동 가정

 원격 저장소의 main을 한번 commit

git fakeTeamwork

 

 <원격 저장소의 bran이름>을 <num> 번 만큼 commit

git fakeTeamwork <원격저장소의 branch이름> <num>

 

 

Git Push

 작업을 업로드해 공유

 - 로컬 저장소에서의 변경 사항을 원격 저장소에 업로드 + 원격 저장소에 로컬 저장소의 새로운 커밋들을 합치고 갱신

 

git push

* 기본적인 push는 사용자 위치의 branch에 대해서는 push를 진행

 

 

엇갈린 작업

위와 같은 상황에서 <git push>는 실패

=> 원격 저장소는 C2까지 갱신된 상태이고, 로컬 저장소의 C3은 C1을 기반으로 하기 때문

 

아래와 같이 해결 가능

git fetch;
git rebase o/main;
git push;

 

<git pull --rebase> : git pull = git fetch + git merge인데 이 조건을 git fetch + git rebase로 수정

 

 

원격저장소 거부

 일반적으로 원격저장소의 main 브랜치는 잠겨있으므로, 변경사항을 적용하려면 pull request 과정을 거쳐야함

 

 

* main 위치 강제 변경

git checkout -fB main [변경할 위치]

 

* 로컬 원격 브랜치(origin/main)를 로컬 브랜치와 연결

git checkout -b [새로운 브랜치 이름] [로컬의 원격 브랜치 이름]
git branch -u [로컬의 원격 브랜치 이름] [기존 브랜치 이름]
//[새로운 브랜치 이름]이 이미 존재하는 branch고 checkout 되어있다면,
git branch -u o/main

 

*  

git push <remote> <local place>

ex) <git push origin main>

     -> 로컬 저장소의 main 브랜치의 모든 commit을 수집 후,

         원격 저장소(origin)의 main 으로가서 commit을 채워 넣는다.

 

git branch -u o/main new

 를 통해 로컬 저장소의 new와 원격 저장소의 main을 연결

 

git push origin new

 위와 같은 결과를 확인 가능

 

* 로컬 브랜치와 원격 브랜치를 설정하여 푸쉬

git push origin <source>:<destination>

 <source> : local branch

 <destination> : remote branch (새로운 브랜치명 사용 가능)

 

git push origin foo^:main

 

 

* fetch와 pull의 차이점 +1 : fetch는 사용자 위치(*)에 상관 없이 원격 저장소의 모든 branch의 정보를 다운로드

                                      pull은 사용자 위치(*)가 있는 branch에 대해서만 fetch & merge 

 

 

* 원격 저장소의 branch 삭제

git push origin :<삭제할 브랜치 이름>

 

 

* 로컬 저장소에 branch 생성

git fetch origin :<생성할 브랜치 이름>

 

 

* 로컬 브랜치와 원격 브랜치를 설정하여 pull

git pull origin foo

위는 아래와 같다.

git fetch origin foo;
git merge o/foo;

 

다시

git pull origin bar~1:bugFix

위는 아래와 같다.

git fetch origin bar~1:bugFix;
git merge bugFix;

이 부분은 사용자 위치(*)가 어디에 있는지가 중요하다. 그 위치를 기준으로 merge 부분에서 대상 브랜치와 merge됨

 

* 원격 저장소 추가

git remote add origin [github url]

 

* 브랜치 해시 확인

git rev-parse [브랜치 이름]

 

* git main branch가 없을 경우

git checkout -t -b main origin/main

 


* 해당 페이지 명령어

 - sandbox (자유 연습공간)

 - levels (레벨 선택 페이지)

 

Reference

https://learngitbranching.js.org/?locale=ko 

 

Learn Git Branching

An interactive Git visualization tool to educate and challenge!

learngitbranching.js.org

 

'Git' 카테고리의 다른 글

Intellij에서 STS 프로젝트 Git 연동하여 사용  (0) 2022.05.31
Fork Repository 를 Parent Repository와 동기화  (0) 2022.04.27
Git 기초 및 실습  (0) 2022.04.26

댓글