git 발전편

shinyeongwoon·2022년 12월 21일
0

git

목록 보기
3/4

Branch

소프트웨어를 개발할 때에 개발자들은 동일한 소스코드를 함께 공유하고 다루게 된다.
동일한 소스코드 위에서 어떤 개발자는 버그를 수정하기도 하고 또 다른 개발자는 새로운 기능을 만들어 낸다.
이와 같이 여러 사람이 동일한 소스코드를 기반으로 서로 다른 작업을 할 때에는 각각 서로 다른 버전의 코드가 만들어 질 수 밖에 없다.

이럴때, 여러 개발자들이 동시에 다양한 작업을 할 수 있게 만들어 주는 기능이 '브랜치'.
각자 독립적인 작업 영역(저장소) 안에서 마음대로 소스코드를 변경할 수 있다.
이렇게 분리된 작업 영역에서 변경된 내용은 나중에 원래의 버전과 비교해서 하나의 새로운 버전으로 만들어 낼 수 있다.

또한, 이렇게 만들어진 브랜치는 다른 브랜치와 Merge 함으로써, 작업한 내용을 다시 새로운 하나의 브랜치로 모을 수 있다.

여러명이서 동시에 작업할 때는 다른 사람의 작업에 영향을 주거나 받지 않도록, 먼저 메인 브랜치에 자신의 작업 전용 브랜치를 만든다.
그리고 각자 작업을 진행한 후, 작업이 끝난 사람은 메인 브랜치에 자신의 브랜치의 변경 사항을 적용한다.
이렇게 함으로써 다른 사람의 작업에 영향을 받지 않고 독립적으로 특정 작업을 수행하고 그 결과를 하나로 모아 나가게 된다.
이러한 방식으로 작업할 경우 '작업단위', 즉 브랜치로 그 작업의 기록을 중간 중간에 남기게 되므로 문제가 발생했을 경우 원인이 되는 작업을 찾아내거나 그에 따른 대책을 세우기 쉬워짐.

master branch

저장소를 처음 만들면, Git은 바로 'master'라는 이름의 브랜치를 만듦.
이 새로운 저장소에 새로운 파일을 추가 한다거나 추가한 파일의 내용을 변경하여 그 내용을 커밋하는것은 모두 'master' 이름의 브랜치를 통해 처리할 수 있는 일이 됨.
'master'가 아닌 또 다른 새로운 브랜치를 만들어서 chekout을 하지 않는 이상 모든 작업은 master에서 이뤄짐

브랜치 만들기

브랜치를 효과적으로 관리하려면, 먼저 작업할 팀원들과 어떠한 방식으로 브랜치를 만들고 통합할 것인지 미리 정해 두는 것이 좋다.
브랜치 이름은 어떤 규칙으로 지을 것 인지, 어떤 상황에서 브랜치를 만들지, 어느 시점에 통합할 것인지 등등

통합 브랜치 (Integration Branch)

언제든지 배포할 수 있는 버전을 만들 수 있어야 하는 브랜치.
늘 안정적인 상태를 유지하는것이 중요.
'안정적인 상태' : 모든 기능이 정상적으로 동작하는 상태
만약 문제가 발견되어 버그 수정 및 새로운 기능을 추가해야 할때 '토픽 브랜치'를 만들 수 있다.
처음에는 보통 통합 브랜치에서 토픽 브랜치를 만들어 낸다.
일반적으로 저장소를 처음 만들었을때에 생기는 'master' 브랜치를 통합 브랜치로 사용

토픽 브랜치 (Topic Branch)

기능 추가나 버그 수정과 같은 단위 작업을 위한 브랜치.
여러 개의 작업을 동시에 진행할 때, 그 수 만큼 토픽 브랜치를 생성할 수 있음.
토픽 브랜치는 보통 통합 브랜치로부터 만들어 내며, 토픽 브랜치에서 특정 작업이 완료되면 다시 통합 브랜치에 병합하는 방식으로 진행.
이러한 토픽 브랜치는 '피처 브랜치(Feature branch)' 라고 부르기도 함

브랜치 전환하기

Git에서는 항상 작업할 브랜치를 미리 선택해야 합니다.
처음에 Git을 설치하게 되면 'master' 브랜치가 선택 되어 있다.
현재 선택된 브랜치가 아닌 다른 브랜치에서 작업하고 싶을때는 checkout 명령어를 실행하여 원하는 브랜치로 전환 가능.
체크아웃을 실행하면, 우선 브랜치 안에 있는 마지막 커밋 내용이 작업 트리에 펼쳐진다.
브랜치가 전환 되었으므로 이 후에 실행한 커밋은 전환한 브랜치에 추가됨.

'HEAD' 란 현재 사용 중인 브랜치의 선두 부분을 나타내는 이름.
기본적으로는 'master'의 선두 부분을 나타낸다.
'HEAD' 를 이동하면, 사용하는 브랜치가 변경됨.

stash

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

stash 란, 파일의 변경 내용을 일시적으로 기록해두는 영역이다.
stash를 사용하여 작업 트리와 인덱스내에서 아직 커밋하지 않은 변경을 일시적으로 저장해 둘 수 있다.
이 stash 에 저장된 변경 내용은 나중에 다시 불러와 원래의 브랜치나 다른 브랜치에 커밋할 수 있다.

브랜치 통합하기

작업이 완료된 토픽 브랜치는 최종적으로 통합 브랜치에 병합됨.
브랜치 통합에는 merge 를 사용하는 방법과 'rebase'를 사용하는 2가지 방법이 있다.
어느 쪽을 사용하냐에 따라 통합 후의 브랜치의 이력이 크게 달라짐.

merge

merge 를 사용하면, 여러 개의 브랜치를 하나로 모을 수 있다.
예를 들어, 'master' 브랜치에서 분기하는 'bugfix'라는 브랜치가 있다고 가정하자

이 'bugfix' 브랜치를 'master' 브랜치로 병합할때, 'master' 브랜치의 상태가 이전부터 변경되어 있지만 않으면 매우 쉽게 병합할 수 있다.
'bugfix' 브랜치의 이력은 'master' 브랜치의 이력을 모두 포함하고 있기 때문에, 'master' 브랜치는 단순히 이동하기만 해도 'bugfix' 브랜치의 내용을 적용할 수 있다.
또한 이 같은 병합은 'fast-forward(빨리감기) 병합'이라고 부름

하지만 'bugfix' 브랜치를 분기한 이후에 'master' 브랜치에 여러 가지 변경 사항이 적용되는 경우도 있다.
이 경우 'master' 브랜치 내의 변경 내용과 'bugfix' 브랜치 내의 변경 내용을 하나로 통합할 필요 있다.

따라서 양쪽의 변경을 가져온 'merge commit(병합 커밋)'을 실행하게 된다.
병합 완료 후 , 통합 브랜치인 'master' 브랜치로 통합된 이력이 생기게 된다.

rebase

'master' 브랜치에서 분기하는 'bugfix' 브랜치가 있다고 가정

이제 rebase 를 이용해 어떻게 브랜치를 통합할 수 있는지 알아볼 차례.
'non fast-forward 병합' 방식으로 진행되는 시나리오를 만들어 보자.

우선 'bugfix' 브랜치를 'master' 브랜치에 rebase 하면, 'bugfix' 브랜치의 이력이 'master' 브랜치 뒤로 이동하게 된다. 그 때문에 이력이 하나의 줄기로 이어지게 된다.
이때 이동하는 커밋 X와 Y내에 포함되는 내용이 'master'의 커밋된 버전들과 충돌하는 부분이 생길 수 있다. 그 때는 각각의 커밋에서 발생한 충돌 내용을 수정할 필요가 있다.

'rebase'만 하면, 'master'의 위치는 그대로 유지됨.
'master' 브랜치의 위치를 변경하기 위해서는 'master' 브랜치에서 'bugfix' 브랜치를 fast-foward 병합하면 됨.

토픽 브랜치와 통합 브랜치에서의 작업 흐름 파악하기

토픽 브랜치와 통합 브랜치를 사용한 작업은 어떠한 순서로 진행되는 지, 간단한 예를 통해 알아보자

토픽 브랜치에서 새로운 기능 추가 작업과 버그 수정 작업을 동시에 진행 하는 경우
일단 통합 브랜치로 부터 새롭게 버그 수정용/새로운기능 추가용 토픽 브랜치를 만들어, 별개로 작업이 가능.
버그 수정을 완료한 후 통합 브랜치와 버그 수정용 토픽 브랜치를 병합하여 수정된 버전을 만들어 낼 수 있다.
버그도 수정 했으니, 다시 원래 브랜치로 돌아와서 새로운 기능 추가 작업을 계속 진행하려 한다.
그러나, 작업을 진행하려고 봤더니 앞서 적용한 버그수정 커밋이 새로운 기능 추가에도 적용이 되야 한다는 것을 알게 됨.
여기서 버그 수정 내용을 적용하려면 직접 merge하는 방법과 버그수정 커밋을 적용한 통합 브랜치에 rebase하는 방법이 있다.

컬럼

「A successful Git branching model」- 성공적인 Git 브랜칭 모델

Git의 브랜치 운용 모델로서, "A successful Git branching model" 이란 컬럼을 소개합니다.

원문:
http://nvie.com/posts/a-successful-git-branching-model/

이 운용 모델에서는 크게 나눠 4가지 종류의 브랜치를 이용하여 개발을 진행합니다.

메인 브랜치(Main branch)
피처 브랜치(Feature branch) 또는 토픽 브랜치(Topic branch)
릴리스 브랜치(Release branch)
핫픽스 브랜치(Hotfix branch)

메인 브랜치(Main branch)
'master' 브랜치와 'develop' 브랜치, 이 두 종류의 브랜치를 보통 메인 브랜치로 사용합니다.

master
'master' 브랜치에서는, 배포 가능한 상태만을 관리합니다. 커밋할 때에는 태그를 사용하여 배포 번호를 기록합니다.
develop
'develop' 브랜치는 앞서 설명한 통합 브랜치의 역할을 하며, 평소에는 이 브랜치를 기반으로 개발을 진행합니다.
피처 브랜치(Feature branch)
피처 브랜치는, 앞서 설명한 토픽 브랜치 역할을 담당합니다.

이 브랜치는 새로운 기능 개발 및 버그 수정이 필요할 때에 'develop' 브랜치로부터 분기합니다. 피처 브랜치에서의 작업은 기본적으로 공유할 필요가 없기 때문에, 원격으로는 관리하지 않습니다. 개발이 완료되면 'develop' 브랜치로 병합하여 다른 사람들과 공유합니다.

릴리즈 브랜치(Release branch)
릴리즈 브랜치에서는 버그를 수정하거나 새로운 기능을 포함한 상태로 모든 기능이 정상적으로 동작하는지 확인합니다. 릴리즈 브랜치의 이름은 관례적으로 브랜치 이름 앞에 'release-' 를 붙입니다. 이 때, 다음 번 릴리즈를 위한 개발 작업은 'develop' 브랜치 에서 계속 진행해 나가면 됩니다.

릴리즈 브랜치에서는 릴리즈를 위한 최종적인 버그 수정 등의 개발을 수행합니다. 모든 준비를 마치고 배포 가능한 상태가 되면 'master' 브랜치로 병합시키고, 병합한 커밋에 릴리즈 번호 태그를 추가합니다.

릴리즈 브랜치에서 기능을 점검하며 발견한 버그 수정 사항은 'develop' 브랜치에도 적용해 주어야 합니다. 그러므로 배포 완료 후 'develop' 브랜치에 대해서도 병합 작업을 수행합니다.

핫픽스 브랜치(Hotfix branch)
배포한 버전에 긴급하게 수정을 해야 할 필요가 있을 경우, 'master' 브랜치에서 분기하는 브랜치입니다. 관례적으로 브랜치 이름 앞에 'hotfix-'를 붙입니다.

예를 들어 'develop' 브랜치에서 개발을 한창 진행하고 있는 도중에 이전에 배포한 소스코드에 아주 큰 버그가 발견되는 경우를 생각해 보세요. 문제가 되는 부분을 빠르게 수정해서 안정적으로 다시 배포해야 하는 상황입니다. 'develop' 브랜치에서 문제가 되는 부분을 수정하여 배포 가능한 버전을 만들기에는 시간도 많이 소요되고 안정성을 보장하기도 어렵습니다. 그렇기 때문에 바로 배포가 가능한 'master' 브랜치에서 직접 브랜치를 만들어 필요한 부분 만을 수정한 후 다시 'master'브랜치에 병합하여 이를 배포하려고 하는 것이죠.

이 때 만든 핫픽스 브랜치에서의 변경 사항은 'develop' 브랜치에도 병합하여 문제가 되는 부분을 처리해 주어야 합니다.

branch 사용해 보기

  1. git 저장소 만들기
$ mkdir tutorial
$ cd tutorial
$ git init
  1. tutorial folder에 myfile.txt 만들고 커밋하기
$ vi tutorial
$ git add myfile.txt
$ git commit -m "first commit"

이력)

  1. 브랜치 만들기
    issue1 이라는 이름으로 새로운 브랜치 만들기
$ git branch issue1
$ git branch

*master
issue1

*은 현재 사용중인 branch를 표시

  1. 브랜치 전환하기
$ git checkout issue1

$ git checkout -b <branch>

-b 옵션을 넣으면 브랜치 작성과 체크아웃을 한번에 실행 할 수 있다.

myfile.txt 에 한행을 더 추가 후 커밋하기

$ git add myfile.txt
$ git commit -m "add 설명 추가"

  1. issue1 브랜치의 변경 사항을 'master' 브랜치에 병합
$ git checkout master
$ cat myfile.txt
$ git merge issue1
$ cat myfile.txt

issue1의 커밋내용이 master로 병합되게 된다.

이러한 방식을 fast-foward merge (빨리감기 병합) 이라고 한다.

  1. 브랜치 삭제하기
    issue1 브랜치 내용이 모두 master에 통합 되었기 때문에 이제 더이상 issue1 브랜치가 필요없게 되었다.
    브랜치를 삭제하려면 branch 명령에 -d옵션을 지정하여 실행
$ git branch -d issue1

동시에 여러 작업하기

  1. issue2 와 issue3 브랜치 만들고 issue2 branch로 전환하기
$ git branch issue2
$ git branch issue3
$ git checkout issue2

  1. issue2 branch에서 myfile.txt 변경 및 commit 하기

branch test
issue1 branch add
issue2 branch add

$ git add myfile.txt
$ git commit -m "issue2 branch add"

  1. issue3 brach로 전환 및 file 내용 변경하기

branch test
issue1 branch add
issue3 branch add

$ git add myfile.txt
$ git commit -m "issue3 branch add"

이 처럼 각각의 브랜치에서는 독립적으로 서로 다른 작업을 처리할 수 있다.

병합할 때 발생하는 충돌 해결하기

master branch에 issue2 와 issue3를 병합해보기

  1. master branch checkout 후 issue2 merge 하기
$ git checkout master
git merge issue2

fast-forward 병합이 실행

  1. issue3 병합해보기
$ git merge issue3

충돌 에러 발생

실제로 myfile.txt 를 확인해 보면 위와 같이 변경 되어 있음
이 충돌 부분을 일일이 확인해서 수정해 주어야 한다.

  1. 파일 수정하기

branch test
issue1 branch add
issue2 branch add
issue3 branch add

$ git add myfile.txt
$ git commit -m "issue3 branch merge"

이 시점까지의 이력을 보면 이번 병합은 충돌 부분을 수정했기 때문에 그 변화를 기록하는 병합 커밋이 새로 생성됨. 그리고 'master' 브랜치의 시작('HEAD')이 그 위치로 이동해 있는것을 확인 할 수 있다. 아래와 같은 방식을 'non fast-forward 병합' 이라고 함

rebase로 병합하기

앞서 두개의 브랜치를 'master' 브랜치로 모두 병합시킴.
그로 인해 두개의 줄기로 브랜치가 분기 되었다가 다시 하나로 합쳐지는 것을 확인함.
issue3 브랜치를 병합 할 때에 rebase를 먼저 실행 한 후 병합을 시도한다면 그 이력을 하나의 줄기로만들 수 있다.

  1. 위에서 진행한 마지막 병합 명령 취소하기
$ git reset --hard HEAD~

  1. issue3 브랜치로 전환하여 master 브랜치에 rebase 실행
$ git checkout issue3
$ git rebase master

merge 때와 마찬가지로 myfile.txt 파일 내용에 충돌이 있기 때문에 그 부분을 이전의 튜토이얼에서와 동일하게 처리

  1. 충돌난 파일의 내용을 적절히 변경 할 것
$ git add myfile.txt
$ git rebase --continue

rebase 의 경우 충돌 부분을 수정한 수 에 commit 이 아니라 rebase 명령에 --continue 옵션을 지정하여 실행 해야 한다.
rebase 자체를 취소하려면 --abort 옵션을 지정하면 된다.

rebase 만 실행한 경우 issue3 브랜치가 두 브랜치의 앞쪽으로 위치가 옮겨 졌을 뿐 'master' 브랜치는 아직 issue3의 변경 사항이 적용되지 못한 상태로 뒤에 남겨져 있다.

  1. master branch 로 checkout 한 뒤 변경사항을 모두 병합하기
$ git chekcout master
$ git merge issue3

myfile.txt의 최종적인 내용은 merge 했을 경우와 동일하지만, 그 이력은 아래 그림과 같이 달라짐

pull, 원격 저장소의 데이터를 로컬 저장소에 가져와 병합하기

pull을 실행하면 원격 저장소의 변경된 데이터를 가져올 수 있다,
경우에 따라 로컬 저장소에 pull 한 데이터가 어떻게 반영되는지 확인 해 본다.

  1. 로컬 저장소의 모든 변경 사항이 반영되어 있는 상태에서 새로운 변경 사항이 있는 원격 저장소의 커밋 Y를 로컬로 가져오는 경우를 살펴보자

이런 경우, 단순히 'fast-forward 병합'이 이루어짐.
'master'는 로컬 저장소의 'master' 브랜치
'origin/master'는 원격 저장소 'origin'의 'master' 브랜치를 나타냄

그러나 로컬 저장소의 'master' 브랜치에서도 변경 사항이 생긴 경우, 양 쪽의 변경을 통합할 필요가 있다.

이때 pull을 실행하여 소스를 병합할 수 있다.
충돌하는 변경이 없을 경우 자동적으로 병합 커밋이 만들어 지지만,
충돌이 있을 경우에는 충돌난 부분을 수동으로 해결한 다음 직접 commit을 해주어야 한다.

fetch, 원격 저장소의 데이터를 로컬에 가져오기만 하기

pull을 실행하면, 원격 저장소의 내용을 가져와 자동으로 병합 작업을 실행하게 된다.
그러나 단순히 원격 저장소의 내용을 확인만 하고 로컬 데이터와 병합은 하고 싶지 않은 경우 fetch 명령어를 사용할 수 있다.
fetch를 실행하면, 원격 저장소의 최신 이력을 확인 할 수 있다.
이때 가져온 최신 커밋 이력은 이름 없는 브랜치로 로컬에 가져오게 된다.
이 브랜치는 'FETCH_HEAD'의 이름으로 체크아웃 할 수도 있다.
예를 들어, 로컬 저장소와 원격 저장소에 B에서 진행된 커밋이 있는 상태에서 fetch를 수행하면 아래 그림과 같이 이력이 남겨짐

이 상태에서 원격 저장소의 내용을 로컬 저장소의 'master'에 통합하고 싶은 경우에는, 'FETCH_HEAD' 브랜치를 merge 하거나 다시 pull을 실행하면 됨

fetch 후 merge 를 수행하면, pull 명령을 실행했을 때와 같은 이력이 만들어 짐
사실 pull 이 내부적으로 보면 fetch + merge 이기 때문

Tag

커밋을 참조하기 쉽도록 알기 쉬운 이름을 붙이는 것을 말함
한 번 붙인 태그는 브랜치처럼 위치가 이동하지 않고 고정됨

Git 에서는 일반적으로 이름 정보만을 갖는 '태그(Lightweight tag)'와 보다 상세한 정보를 포함하는 '주석 태그(Annotated tag)' 이 두 가지 태그를 사용할 수 있다.

일반 태그 (Lightweight tag)

  • 이름만 붙일 수 있다

주석 태그 (Annotated tag)

  • 이름을 붙일 수 있다

  • 태그에 대한 설명도 포함할 수 있다

  • 서명도 넣을 수 있다

  • 이 태그를 만든 사람의 이름, 이메일과 태그를 만든 날짜 정보도 포함할 수 있다

    보통 '릴리스 브랜치(Release branch)'에서는 주석 태그를 사용하여 설명이나 서명을 넣은 보다 상세한 정보를 포함하는 tag를 사용
    로컬에서 일시적으로 사용하는 '토픽 브랜치(Topic branch)'에서는 이름만 만들어 붙이는 태그를 사용

    태그 이름을 지정하여 checkout 하거나 reset 함으로써, 간단하게 과거의 특정 상태로 되돌릴 수 있다.

Tag 사용해보기

$ mkdir tutorial
$ cd tutorial
$ git init

tutorial folder 안에 myfile.txt 라는 이름으로 파일을 만든 후 커밋

$ git add myfile.txt
$ git commit -m "first commit"

  1. 태그 추가하기
$ git tag apple

log 명령어에 --decorate 옵션을 붙여 실행하면, 태그 정보를 포함한 이력을 확인할 수 있다.

$ git log --decorate

  1. 주석이 달린 태그 추가하기
    주석이 달린 태그를 추가하려면 tag 명령어에 -a 옵션을 지정하여 실행하면 됨.
    엔터키를 눌러 실행한 후 태그에 대한 주석 내용을 바로 입력한다.
    -m 옵션을 지정하여 명령어를 실행할 때에 바로 내용을 입력 할 수도 있다.
$ git tag -a <tagname>

현재 'HEAD'가 가리키고 있는 커밋에 'banana'라는 주석이 달린 태그를 달려면 다음과 같이 명령어를 실행

$ git tag -am "tagtest" banana

-n 옵션을 지정하여 tag 명령어를 실행하면 태그 목록과 주석 내용을 확인 할 수 있다.

$ git tag -n

  1. 태그 삭제하기
    태그를 삭제하려면, tag 명령어에 -d 옵션을 지정하여 실행
$ git tag -d <tagname>

0개의 댓글