git - 브랜치 관리와 고급 기능

Git Branch

여러 개발자가 동시에 서로 다른 작업을 할 수 있게 만들어 주는 기능
또는 기존의 소스 코드를 해치지 않으면서 다른 작업을 시도해보고 싶을 때도 활용 가능

학습 목표

* Git 브랜치의 개념
* Git으로 협업하며 브랜치를 나누는 이유
* Git으로 프로젝트를 관리하며 브랜치를 생성, 전환, 병합

Branch(브랜치)

Branch란?

독립적으로 어떤 작업을 진행하기 위한 개념
코드를 통째로 복사한 후 원래 코드가 변경될 우려 없이 독립적으로 개발 가능
→ 각각의 브랜치는 다른 브랜치의 영향을 받지 않아서, 여러 작업을 동시에 진행 가능

 

브랜치 기능의 장점은 아래와 같다

* 한 소스 코드에서 동시에 다양한 작업 가능

* 소스 코드의 어느 한 시점과 동일한 상태를 만들고, 브랜치를 넘나들며 작업 수행 가능

* 각각의 브랜치에서 생긴 변화가 다른 브랜치에 영향을 주지 않고 독립적으로 코딩 가능

그리고 분리된 작업 영역(브랜치)에서 변경된 내용들을 다른 브랜치와 병합(merge)함으로써 다시 새로운 하나의 브랜치로 모을 수 있다

 

협업 시에는 먼저 통합 브랜치에 자신의 작업 전용 브랜치를 만들고, 각자의 브랜치에서 맡은 작업을 수행 후 작업이 끝난 브랜치는 통합 브랜치에 병합해서 변경 사항을 적용하는 방식으로 진행할 수 있다

브랜치의 종류

통합 브랜치(Integration Branch)

배포될 소스 코드가 기록되는 브랜치
기본적으로 만들어지는 main(또는 master) 브랜치
해당 프로젝트의 모든 기능이 정상적으로 작동하는 상태의 소스 코드가 담김

피처 브랜치(Feature Branch)

기능 추가, 버그 수정과 같이 단위 작업 을 위한 브랜치
통합 브랜치로부터 만들어내고, 피처 브랜치에서 하나의 작업이 완료되면 다시 통합 브랜치에 병합(merge)하는 방식
토픽 브랜치라고도 함

브랜치 명령어 모음

아래는 자주 사용하는 브랜치 명령어들이다

새로운 브랜치 생성

git branch <새로운 브랜치 이름>

새로운 브랜치 생성 후 해당 브랜치로 전환

git switch -c <새로운 브랜치 이름>

## 또는
git checkout -b <새로운 브랜치 이름>

브랜치 목록 확인

git branch

브랜치 목록과 각 브랜치의 최근 커밋 확인

git branch -v

브랜치 삭제

git branch -d <삭제할 브랜치 이름>

## 또는
git branch -D    ## 이 명령어는 병합하지 않은 브랜치를 강제 삭제하는 방법임

브랜치 전환

git switch <브랜치 이름>

## 또는
git checkout <브랜치 이름>

브랜치 병합

  • master 브랜치로 dev 브랜치를 병합할 때를 가정(master ← dev)
## step 1
git checkout master

## step 2
git merge dev

병합을 취소하고 이전 커밋으로 되돌리기

브랜치 병합시 충돌이 발생할 경우 사용할 수 있다


git reset --hard 커밋넘버

로그에 모든 브랜치를 그래프로 표현

git log --branches --graph --decorate

아직 commit 하지 않은 작업을 스택에 임시 저장

git stash

프로젝트 workflow

프로젝트를 위해서 git을 활용 시의 workflow

Fork, clone

프로젝트의 remote repository를 생성 후, fork 해서 각자의 remote repo에 가져온다(origin)
이를 local 에서 작업하기 위해 clone 해서(git clone) 가져 온 상황(local repo)

브랜치를 생성하고 생성한 브랜치로 이동하기

기본적으로 개발을 진행할 때는 main 브랜치가 아닌 dev 브랜치를 만들어서 작업하는 경우가 많다
dev 브랜치를 생성해서 해당 브랜치로 이동하자

git checkout -b dev    ## 새로운 브랜치 이름
## 또는
git switch -c dev

## remote repo에도 해당 생성한 브랜치를 반영하려면
git push origin dev
  • HEAD란 현재 위치의 커밋을 가리킴
    → 즉, 현재 작업 중인 커밋

생성한 브랜치 목록 및 현재 위치한 브랜치 확인

git branch
## 브랜치 목록들이 나오고, 해당 화면에서 나오려면 q

기능 단위 구현을 위한 브랜치 생성

하나의 기능 단위 구현을 위한 브랜치를 만들기로 정하고 feature/기능이름 형식의 브랜치를 만들기로 가정

 

위에서 만드는 방법을 이용해서, feature/login 과 같은 브랜치를 만들고, 또 그에 파생되는 방식으로 브랜치를 만들 수도 있다(기존에 완성된 소스 코드를 건드리지 않기 위함)

 

파생 브랜치로 feature/login-oauth를 만들었다고 하자

브랜치 병합하기

feature/login-oauth 브랜치를 feature/login 브랜치로 병합(merge)한다고 가정

## step 1: 병합이 될 브랜치로 이동(head 이동)
git checkout feature/login    ## 또는 switch 명령어

## step 2: git merge 병합할 브랜치 이름(브랜치 병합하기)
git merge feature/login-oauth

브랜치가 병합되기 전 병할 될 브랜치에 추가적인 커밋이 없었다면 fast-forward 방식으로 병합 됨
→ 브랜치가 분기되지 않고(별도의 커밋을 생성하지 않고), feature/login 브랜치가 가리키는 커밋을 feature/login-oauth가 생성한 커밋으로 바꾸는 작업

  • 병합될 브랜치에 별도의 커밋이 있었다면?
    merge commit 방식으로 병합이 됨
    → 각 브랜치가 줄기처럼 분기한 후, 병합하는 모양새
    → 병합 하기 전의 커밋이 사라지지 않음(병합할 브랜치 feature/login-oauth의 커밋의 분기가 존재)

rebase 와 merge의 차이점

아주 중요한 부분이다(면접 단골 질문)

rebase의 원리가 fast-forward와 같다고 보면 된다

* merge
→ 변경 내역이 모두 그대로 남아 있어서 이력이 복잡해 진다(분기의 가지가 많으므로)

* rebase
→ 말 그대로 branch base를 이동시킨다는 뜻. merge 처럼 브랜치 통합을 목적으로 하지만, 
특정 시점으로 브랜치가 가리키는 곳을 변경하는 기능

feature/login 브랜치에서 git rebase main feature/login 명령시 main의 최신 커밋으로 브랜치 자체가 가리키는 곳(시작점?)이 변경됨(단, main의 다른 커밋에서 충돌이 없을 경우)

remote repo로 업로드

local에서 작업이 다 되었다면, remote repo에 업로드를 하자

git push origin 해당브랜치명

## local의  feature/login 브랜치를 remote에 업로드하는 경우

git push origin feature/login

Pull Request

내가 push한 변경 사항에 대해서 다른 사람들에게 알리는 것
→ 브랜치의 변경 사항을 다른 팀원들과 코드 리뷰하고 dev 브랜치와 같은 브랜치에 반영을 요청하고 싶을 때 사용 가능

compare a pull request → 글 내용 간단 작성 후 create pull request

전체 흐름 workflow

1. Local에서 새로운 브랜치를 생성하고
2. 코드 작업을 한다
3. 작업이 끝나면 Remote Repository 로 Push
4. 
그리고 Project Upstream Repository에 반영(merge)될 수 있도록 Pull Request

* 만약 작업하던 중간에 Remote upstream 에 업데이트가 생긴다면 Local 로 pull 받아주어야 한다!

추가적인 Git 명령어 정리

Git을 사용하면서 문제 상황(실수 등)을 겪을 때 유용한 명령어들을 정리

git의 모든 기록 확인하고 특정 시점으로 되돌리기: git reflog, git reset

git의 모든 브랜치에서 있었던 지금까지의 모든 기록을 볼 수 있다

git reflog
## 각각 HEAD@{index} 형태로 index를 가지고 있으니, 잘못되기 전에 해당하는 index를 찾고

git reset HEAD@{index}
## 해당 index로 리셋하자

실수로 지운 파일 되돌리기, 잘못 수정한 거 되돌리기 등
→ 그냥 잘 작동했던 때로 되돌리고 싶을 때 사용 가능

방금 커밋했는데, 바로 수정하고 싶은 경우: --amend --no-edit

커밋을 했는데, 파일에 등호를 빼먹었다든지, 쉼표를 빼먹었다든지 하는 상황에서 바로 수정하고 새로 커밋하자니 불편하니깐

git add .    ## 또는 각각의 파일을 add 후에

git commit --amend --no-edit
## 마지막 커밋에 바뀐 파일이 등록된다
## 주의: 원격 저장소에 이미 push된 커밋에는 사용하면 안된다!

여러 개의 커밋 로그를 하나로 묶고 싶은 경우: git squash

git squash 를 활용

커밋 메세지를 잘못 써서 수정하고픈 경우: --amend

git commit --amend
## 에디터가 켜지고 커밋 메시지 수정 가능

다른 브랜치에 커밋해야 하는 걸 실수로 master에 커밋한 경우

## step 1: 현재 master의 상태로 새로운 브랜치를 생성
git branch 새로운브랜치이름

## step 2: master 브랜치의 마지막 커밋을 제거
git reset HEAD~ --hard        ## 이러면 잘못 커밋한 부분은 삭제됨
## 여러개의 커밋을 했다면 git reset HEAD@{index} 를 활용하자

## step 3: 브랜치 전환
git checkout 새로운브랜치이름(아까 생성했던)

## 이러면 생성한 브랜치가 커밋한 내용을 가지고 있으니깐 동일 효과!

주의: 이미 원격 저장소에 push 했다면 이 방법도 소용없음

실수로 이상한 브랜치에 커밋을 한 경우

2가지 방법으로 해결할 수 있다

  • stash(임시 stack에 저장)를 이용하는 경우
## 마지막 커밋은 취소하되, 변경사항은 남겨둔다(soft)
git reset HEAD~ --soft
git stash    ## 임시 스택에 저장(변경사항을)

## 올바른 해당 브랜치로 이동
git checkout 해당브랜치
git stash pop    ## 임시 스택에서 꺼낸다
git add .    ## 또는 각각의 파일을 add
git commit -m “메시지작성”

## 이러면 올바른 브랜치에 커밋이 완료됨
  • cherry-pick을 사용하는 경우
## 올바른 해당 브랜치로 이동
git checkout 올바른해당브랜치

git cherry-pick master    ## master의 마지막 커밋을 선택

## master에서 해당 커밋을 제거
git checkout master
git reset HEAD~ --hard

diff를 실행했는데 아무 것도 안 보이는 경우: --staged

분명 뭔가를 바꿨는데, diff의 결과가 나타나지 않는다면, add를 실행해서 파일을 staging 상태로 만들었을 가능성이 높다. 이 때는 특별한 옵션을 줘서 보이게 해야 한다

git diff --staged

커밋의 여러 변경사항들을 되돌리고(취소) 싶은 경우: revert

## 로그에서 되돌려야 할  커밋을 찾는다
git log    ## 원하는 예전 커밋을 찾으면 해당 커밋의 hash를 기억(복사)한다

## 해당 명령어로 git이 해당 커밋을 되돌리고, 새로운 커밋을 생성한다
git revert [복사한해시값]

## 새로운 커밋 메시지를 입력하거나 저장하고 종료

파일 수정한 것을 되돌리기: git checkout [hash값] /path/to/file

## 해당 파일이 수정되기 전의 커밋 해시를 찾는다
git log    ## 커밋을 찾으면 해시를 기록(복사)

git checkout [복사한캐시값] -- 파일이위치할경로
## 주의: checkout 복사한캐시값 (한칸띄우고) --(한칸띄우고) 경로임!
## 이러면 예전 버전 파일로 바껴 있을 것이다

git commit -m “메시지”        ## 다시 커밋하자

완전 꼬여서 그냥 삭제하려는 경우?

물론 cli에서 저장소 삭제 후 다시 클론 받아도 되지만, 아래는 git에서의 공식 방법?이다

  • 주의: 되돌릴 수 없는 명령어임(삭제니깐)
## origin에서 최신 상태를 받아옴
git fetch origin

git checkout master
git reset --hard origin/master    ## 추적되고 있지 않은 파일/폴더를 모두 삭제

git clean -d --force    
## 초기화하고 싶은 각 branch에 대해서 checkout/reset/clean을 반복

추가 보충 내용

컴퓨터 공학(과학) 기초

참고하기 좋을 영상

  • 유튜브 Crash Course: Computer Science

https://www.youtube.com/watch?v=tpIctyqH29Q&list=PLH2l6uzC4UEW0s7-KewFLBC1D0l6XRfye

git branch

이름은 소폭 다르게 사용할 수 있지만, 보통 이런식으로 명명지어 사용한다

* feature: 기능을 추가할 때 마다 따는 브랜치

* dev, develop: 베타 버전, 모든 개발 로그들이 쌓이는 곳. 새로운 기능이 완성되고 나서 병합(merge)되는 곳

* release: 한번 배포 시도해보는 곳

* hotfix: 급하게 수정하는 곳

* master(main): 항상 최신의 안정적인 프로그램이 돌아가는 곳. 안전함을 보장받은 곳

merge는 cli에서도 할 수 있고, 브라우저에서도(github) 할 수 있다

  • cli에서 merge하는 방법: git merge 병합할브랜치명

sourcetree 프로그램을 이용하면 가지치기 되는 걸 수월하게 볼 수 있다?

  • github에서 merge하는 방법:
    (remote repo 에 push 했다는 전제)compare & pull request → create pull request → merge pull request → confirm
    (단, 충돌이 발생하지 않는 상황에만 오토머지된다)

insight 탭에서 workflow를 볼 수 있음

자세한 방법은 아래를 참고
https://www.notion.so/Git-Merge-9312bb14511e4643b0729ac8546d2ee3

 

Git Merge

로컬에서 작업 완료 후, git push origin master 상태를 기준으로 설명드립니다.

www.notion.so

  • git rebase와 merge의 간단한 비교

graph에서 가지치기 한것이 한줄로 쭉 나오느냐(rebase), 가지치기되었다가 합쳐지는 형태(merge)인가가 외형적으로는 큰 차이

 

rebase: 베이스를 다시 설정
→ dev에 있던 내용을 가져오는데 커밋 자체를 다 흡수해서 합쳐버린다
→ 브랜치의 특정 내역(커밋)이 합쳐지면서 사라지는 한계가 존재

 

둘다 통합하는데 사용할 수 있는 명령어인데, 보통은 merge를 많이 쓰는 편(병합용으로는)
→ 커밋 이력 보존을 위해서

cherry-pick

체리피커에서 유래된 용어(체리피커 생각하면 이해가 편함)

## 딱 원하는 부분만 뽑아 활용하고 싶은 경우
cherry-pick 해당 로그값

다음 내용을 참고
https://backlog.com/git-tutorial/kr/stepup/stepup7_4.html

 

누구나 쉽게 이해할 수 있는 Git 입문~버전 관리를 완벽하게 이용해보자~ | Backlog

누구나 쉽게 알 수 있는 Git에 입문하신 것을 환영합니다. Git을 사용해 버전 관리를 할 수 있도록 함께 공부해봅시다!

backlog.com

vs확장앱: git graph

→ vscode에서 해당 확장 앱을 받으면 브랜치 가지치는 모습을 그래프로 볼 수 있다(graphical하게)

복사했습니다!