Branching and Merging

Branching and Merging은 Git 과 같은 분산 버전 관리 시스템에서 핵심적인 기능이다. 브랜칭은 독립적인 작업 공간을 생성하여 여러 개발자가 동시에 작업할 수 있게 하며, 머징은 이러한 작업 결과를 하나의 코드베이스로 통합한다. 이를 통해 병렬 개발, 기능 분리, 코드 안정성 유지 등이 가능해진다. Git, SVN, Mercurial 등 다양한 버전 관리 시스템에서 지원되며, 현대 소프트웨어 개발의 필수 요소이다.

핵심 개념

  • Branching (브랜칭): 코드베이스의 복사본 생성으로 기능 개발/버그 수정을 격리한다.
  • Merging (머징): 분리된 변경 사항을 메인 코드베이스에 통합하는 과정이다.
  • HEAD: 현재 작업 중인 브랜치의 최신 커밋을 가리키는 포인터이다.
graph TD
    main[main] -->|분기| feature[feature/login]
    feature -->|머지| main
    main -->|배포| Production

목적

  • 병렬 개발 환경 제공
  • 코드 충돌 최소화
  • 기능별/작업별 독립적 개발 지원
  • 안정적인 배포 프로세스 구축

필요성

  • 다수의 개발자가 동시에 작업하는 환경에서 협업 효율성 증대
  • 프로덕션 코드의 안정성 보장
  • 실험적 기능 개발과 버그 수정의 분리
  • 코드 리뷰와 품질 관리 용이성

주요 기능

  • 브랜치 생성/삭제
  • 브랜치 전환 (Checkout)
  • 코드 병합 (Merge)
  • 리베이스 (Rebase)
  • 충돌 해결 (Conflict Resolution)

특징

  • 분산형 개발 지원
  • 비선형적 개발 이력 관리
  • 원격 저장소와의 동기화
  • 다양한 병합 전략 제공

장점과 단점

구분항목설명
✅ 장점병렬 개발여러 기능을 동시에 개발 가능
안정성메인 브랜치의 안정성 유지
실험 용이성실험적 기능을 안전하게 테스트
롤백 용이성문제 발생 시 쉽게 이전 상태로 복구
⚠ 단점복잡성브랜치가 많아지면 관리가 복잡
충돌 발생병합 시 코드 충돌 가능성
학습 곡선초보자에게 어려운 개념
리소스 사용브랜치별 리소스 사용량 증가

주요 원리

구성 요소기능역할
HEAD현재 브랜치 참조현재 작업 중인 브랜치를 가리킴
Branch Pointer커밋 참조특정 커밋을 가리키는 포인터
Commit Object변경사항 저장코드 변경 내용과 메타데이터 저장
Tree Object디렉토리 구조파일과 디렉토리 구조 표현
Merge Base공통 조상 커밋브랜치 분기점 식별

Branching

브랜칭의 주요 원리는 포인터 기반의 참조 시스템이다:

1
2
3
main: A → B → C
feature:        D → E

각 브랜치는 특정 커밋을 가리키는 포인터로, 새로운 커밋이 추가되면 해당 브랜치 포인터가 이동한다.
개발을 위해 다양한 브랜치 전략과 브랜치를 활용한다.

Branch 생성

새로 만든 브랜치는 지금 작업하고 있던 마지막 커밋을 가리킨다.

1
git branch testing

현재 작업 중인 브랜치를 가리키는 HEAD
Source: https://git-scm.com/book/ko/v2/Git-%eb%b8%8c%eb%9e%9c%ec%b9%98-%eb%b8%8c%eb%9e%9c%ec%b9%98%eb%9e%80-%eb%ac%b4%ec%97%87%ec%9d%b8%ea%b0%80

  • 파일이 3 개 있는 디렉토리가 하나 있고 이 파일을 Staging Area 에 저장하고 커밋하는 예제
    git commit 으로 커밋하면 먼저 루트 디렉토리와 각 하위 디렉토리의 트리 개체를 체크섬과 함께 저장소에 저장한다.
1
2
git add README test.rb LICENSE  
git commit -m 'The initial commit of my project'

그 다음에 커밋 개체를 만들고 메타데이터와 루트 디렉토리 트리 개체를 가리키는 포인터 정보를 커밋 개체에 넣어 저장한다.
Git 저장소에는 다섯 개의 데이터 개체가 생긴다.

파일을 수정하고 커밋하면 이전 커밋이 무엇인지도 저장한다.

커밋과 이전 커밋
https://git-scm.com/book/ko/v2/Git-%eb%b8%8c%eb%9e%9c%ec%b9%98-%eb%b8%8c%eb%9e%9c%ec%b9%98%eb%9e%80-%eb%ac%b4%ec%97%87%ec%9d%b8%ea%b0%80

Branch 전환하기

git checkout 명령으로 다른 브랜치로 이동할 수 있다
HEAD 는 testing 브랜치를 가리킨다.

1
git checkout testing

HEAD는 testijng 브랜치를 가리킴
Source: https://git-scm.com/book/ko/v2/Git-%eb%b8%8c%eb%9e%9c%ec%b9%98-%eb%b8%8c%eb%9e%9c%ec%b9%98%eb%9e%80-%eb%ac%b4%ec%97%87%ec%9d%b8%ea%b0%80

브랜치를 만들면서 Checkout 까지 한 번에 하려면 git checkout 명령에 -b 라는 옵션을 추가한다.

1
2
$ git checkout -b testing
Switched to a new branch "testing"
변경사항 커밋

파일을 수정하고 새롭게 커밋을 하면,

1
2
vim test.rb
git commit -a -m 'made a change'

HEAD가 가리키는 testing 브랜치가 새 커밋을 가리킴
Source: https://git-scm.com/book/ko/v2/Git-%eb%b8%8c%eb%9e%9c%ec%b9%98-%eb%b8%8c%eb%9e%9c%ec%b9%98%eb%9e%80-%eb%ac%b4%ec%97%87%ec%9d%b8%ea%b0%80

1
git checkout master

다시, master 브랜치로 되돌아가서,

HEAD가 Checkout한 브랜치로 이동함
Source: https://git-scm.com/book/ko/v2/Git-%eb%b8%8c%eb%9e%9c%ec%b9%98-%eb%b8%8c%eb%9e%9c%ec%b9%98%eb%9e%80-%eb%ac%b4%ec%97%87%ec%9d%b8%ea%b0%80

파일을 수정하고, 다시 커밋을 하면,

1
2
vim test.rb
git commit -a -m 'made other changes'

갈라지는 브랜치
Source: https://git-scm.com/book/ko/v2/Git-%eb%b8%8c%eb%9e%9c%ec%b9%98-%eb%b8%8c%eb%9e%9c%ec%b9%98%eb%9e%80-%eb%ac%b4%ec%97%87%ec%9d%b8%ea%b0%80

두 작업 내용은 서로 독립적으로 각 브랜치에 존재한다.

git log 명령으로 현재 브랜치가 가리키고 있는 히스토리가 무엇이고 어떻게 갈라져 나왔는지 확인할 수 있다.
git log --oneline --decorate --graph --all 이라고 실행하면 히스토리를 출력한다.

1
2
3
4
5
6
7
$ git log --oneline --decorate --graph --all
* c2b9e (HEAD, master) made other changes
| * 87ab2 (testing) made a change
|/
* f30ab add feature #32 - ability to add new formats to the
* 34ac2 fixed bug #1328 - stack overflow under certain conditions
* 98ca9 initial commit of my project

Merging

브랜치 병합 (merging) 과 관련해 가장 많이 사용되는 방식은 아래와 같다.

Fast-forward Merge

두 브랜치가 완전히 일직선상에 있을 때 사용할 수 있는 가장 단순한 병합 방식이다.
main(또는 master) 브랜치가 feature 브랜치의 조상 (ancestor) 일 때, main 브랜치의 HEAD 를 feature 브랜치의 최신 커밋으로 " 앞으로 이동 " 시키는 방식이다.
별도의 머지 커밋이 생성되지 않고, 히스토리가 깔끔하게 유지된다.

장점

  • 커밋 히스토리가 단순하고 선형적.
  • 불필요한 머지 커밋이 생기지 않는다.

단점

  • 브랜치가 분기된 뒤 main 에도 커밋이 추가된 경우에는 사용할 수 없다.
  • 특정 기능을 되돌릴 때 관련 커밋을 개별적으로 찾아야 한다.
1
2
3
A---B---C (main)
         \
          D---E (feature)

main 브랜치에 새로운 커밋이 없고, feature 브랜치에서 작업한 경우, mainfeature 를 병합하면:

1
A---B---C---D---E (main, feature)

main 브랜치가 feature 브랜치의 최신 커밋을 가리키게 된다.

Fast-forward Merged 의 시나리오를 정리해보면:

1
2
3
4
5
$ git checkout -b iss53
Switched to a new branch "iss53"

$ git branch iss53
$ git checkout iss53

브랜치 포인터를 새로 만듦
https://git-scm.com/book/ko/v2/Git-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%99%80-Merge-%EC%9D%98-%EA%B8%B0%EC%B4%88

  1. 새로 만든 Branch 에서 작업을 진행함.
    iss53 브랜치를 Checkout 했기 때문에 (즉, HEAD 는 iss53 브랜치를 가리킨다) 작업을 하고 커밋을 하면 iss53 브랜치가 앞으로 나아간다.

    진행 중인\u00a0<code>iss53</code>\u00a0브랜치
    https://git-scm.com/book/ko/v2/Git-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%99%80-Merge-%EC%9D%98-%EA%B8%B0%EC%B4%88

    이 상황에서 문제를 해결해야할 상황이 생겨 Hotfix 를 해야할 상황이라고 가정해보자.

  2. 새로운 이슈를 처리하기 이전의 운영 (Production) 브랜치로 이동한다.

    1
    2
    
    git checkout master
    Switched to branch 'master'
    
  3. Hotfix 브랜치를 새로 하나 생성한다.

    1
    2
    3
    4
    5
    6
    
    git checkout -b hotfix
    Switched to a new branch 'hotfix'
    vim index.html
    git commit -a -m 'fixed the broken email address'
    [hotfix 1fb7853] fixed the broken email address
    1 file changed, 2 insertions(+)
    

    master 브랜치에서 갈라져 나온 hotfix 브랜치
    https://git-scm.com/book/ko/v2/Git-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%99%80-Merge-%EC%9D%98-%EA%B8%B0%EC%B4%88

  4. 수정한 Hotfix 테스트를 마치고 운영 브랜치로 Merge 한다.

    1
    2
    3
    4
    5
    6
    
    git checkout master
    git merge hotfix
    Updating f42c576.a0874c
    Fast-forward
    index.html | 2 ++
    1 file changed, 2 insertions(+)
    

    Merge 메시지에서 “Fast-forward”
    hotfix 브랜치가 가리키는 C4 커밋이 C2 커밋에 기반한 브랜치이기 때문에 브랜치 포인터는 Merge 과정 없이 최신 커밋으로 이동한다.
    A 브랜치에서 다른 B 브랜치를 Merge 할 때 B 브랜치가 A 브랜치 이후의 커밋을 가리키고 있으면 그저 A 브랜치가 B 브랜치와 동일한 커밋을 가리키도록 이동시킬 뿐이다.

    Merge후 hotfix와 같은 것을 가리키는 master 브랜치

    문제를 해결하고  master 브랜치에 적용하고 나면 다시 일하던 브랜치로 돌아가야 한다. 이제 더 이상 필요없는 hotfix 브랜치는 삭제한다.

    1
    2
    
    git branch -d hotfix
    Deleted branch hotfix (3a0874c).
    
  5. 다시 작업하던 브랜치로 옮겨가서 하던 일을 진행한다.

    1
    2
    3
    4
    5
    6
    
    git checkout iss53
    Switched to branch "iss53"
    vim index.html
    git commit -a -m 'finished the new footer [issue 53]'
    [iss53 ad82d7a] finished the new footer [issue 53]
    1 file changed, 1 insertion(+)
    

    master와 별개로 진행하는 iss53 브랜치
    Source: https://git-scm.com/book/ko/v2/Git-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%99%80-Merge-%EC%9D%98-%EA%B8%B0%EC%B4%88

3-way Merge

브랜치가 분기된 후 각각 별도의 커밋이 존재할 때 사용하는 일반적인 병합 방식이다.
main 과 feature 브랜치의 공통 조상 (Base), main 의 최신 커밋, feature 의 최신 커밋, 이렇게 3 개의 커밋을 비교하여 병합한다.
병합 시 새로운 머지 커밋 (merge commit) 이 생성되어, 두 브랜치의 변경 사항을 모두 반영한다.

장점

  • 병렬 개발 시 충돌 관리와 이력 추적이 용이하다.
  • 브랜치가 분기된 뒤에도 자유롭게 병합이 가능한다.

단점

  • 머지 커밋이 추가되어 히스토리가 복잡해질 수 있다.
  • 병합 충돌이 발생할 가능성이 높아진다.
1
2
3
4

       D---E (feature)
      /
A---B---C (main)

mainfeature 브랜치가 각각 커밋을 추가한 경우, 병합하면:

1
2
3
4

       D---E
      /     \
A---B---C-----F (main)

F 는 병합 커밋으로, C, E 의 변경 사항을 통합한다.

53 번 이슈를 다 구현하고 master 브랜치에 Merge 를 한다고 가정해보자.
git merge 명령으로 합칠 브랜치에서 합쳐질 브랜치를 Merge 하면 된다.

1
2
3
4
5
6
$ git checkout master
Switched to branch 'master'
$ git merge iss53
Merge made by the 'recursive' strategy.
index.html |    1 +
1 file changed, 1 insertion(+)

현재 브랜치가 가리키는 커밋이 Merge 할 브랜치의 조상이 아니므로 Git 은 ‘Fast-forward’ 로 Merge 하지 않는다.

커밋 3개를 Merge
https://git-scm.com/book/ko/v2/Git-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%99%80-Merge-%EC%9D%98-%EA%B8%B0%EC%B4%88

3-way Merge 의 결과를 별도의 커밋으로 만들고 나서 해당 브랜치가 그 커밋을 가리키도록 이동시킨다. 그래서 이런 커밋은 부모가 여러 개고 Merge 커밋이라고 부른다.

Merge 커밋
https://git-scm.com/book/ko/v2/Git-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%99%80-Merge-%EC%9D%98-%EA%B8%B0%EC%B4%88

iss53 브랜치를 master 에 Merge 하고 나면 더는 iss53 브랜치가 필요 없다. 다음 명령으로 브랜치를 삭제하고 이슈의 상태를 처리 완료로 표시한다.

1
git branch -d iss53
Rebase

feature 브랜치의 커밋을 main 브랜치의 최신 커밋 위로 " 재배치 " 하는 방식이다.
마치 feature 브랜치가 main 브랜치에서 바로 분기된 것처럼 히스토리가 선형으로 정리된다.
실제로는 feature 브랜치의 각 커밋을 새로 생성하여 main 브랜치 뒤에 붙인다.

장점

  • 커밋 히스토리가 선형으로 정리되어, 이력 파악이 쉽다.
  • 불필요한 머지 커밋이 생기지 않는다.

단점

  • 이미 공유된 브랜치에서 rebase 를 사용할 경우, 히스토리 변경으로 인해 충돌이나 혼란이 발생할 수 있다.
  • 리베이스 중 충돌이 발생하면 수동으로 해결해야 한다.
  • 협업 중에는 주의해서 사용해야 한다.

리베이스 전:

1
2
3
       D---E (feature)
      /
A---B---C (main)

리베이스 후 (feature 브랜치를 main 위로 재적용):

1
A---B---C---D'---E' (feature)

D', E'D, E 의 변경 사항을 C 이후에 재적용한 새로운 커밋이다.

요약 비교

항목Fast-forward Merge3-way MergeRebase
병합 커밋 생성아니오아니오
히스토리 형태선형비선형선형
충돌 가능성낮음높음중간
사용 시기브랜치가 선형일 때브랜치가 분기되었을 때히스토리를 깔끔하게 유지할 때
주의 사항기능 경계 파악이 어려움히스토리가 복잡해질 수 있음공개 브랜치에서 사용 주의
3-way Merge vs. Fast-Forward
특성3-way MergeFast-Forward
조건브랜치가 분기된 경우분기가 없는 선형 관계
병합 커밋생성됨생성되지 않음
이력병합 이력 보존단순 포인터 이동
명령어git merge --no-ffgit merge (기본값)
Merge vs. Rebase
비교 항목MergeRebase
통합 방식두 브랜치의 최종 결과물을 하나로 합침한 브랜치의 커밋들을 다른 브랜치 끝에 재배열
히스토리 관리병렬적 히스토리 유지 (분기와 병합 지점 모두 보존)선형적 히스토리 생성 (마치 처음부터 한 브랜치에서 작업한 것처럼)
커밋 처리하나의 새로운 Merge 커밋 생성재배열된 모든 커밋에 대해 새로운 커밋 생성
원본 이력원본 브랜치 히스토리가 그대로 유지됨원본 커밋은 남지만 새로운 커밋 해시값 생성
작업 흐름브랜치 작업 내역을 확실히 구분하여 파악 가능마치 하나의 브랜치에서 작업한 것처럼 깔끔한 히스토리
충돌 발생 시점병합 시점리베이스 시점
충돌 해결한 번에 모든 충돌 해결커밋별로 순차적 충돌 해결 필요
사용 용도브랜치 간 변경 사항 통합깔끔한 히스토리 유지

실무에서 효과적으로 적용하기 위한 고려사항 및 주의할 점

항목고려사항주의점
브랜치 명명 규칙일관된 명명 규칙 사용 (feature/, bugfix/, hotfix/)너무 긴 브랜치 이름 피하기
브랜치 수명 관리작업 완료 후 즉시 브랜치 삭제오래된 브랜치 방치하지 않기
병합 전략 선택프로젝트 특성에 맞는 전략 선택일관성 없는 전략 혼용 피하기
충돌 해결정기적인 main 브랜치 동기화대규모 충돌 발생 방지

충돌 해결

Git merge 시 충돌이 발생하는 상황과 해결 과정

시나리오: 동일한 파일의 동일한 부분을 수정하는 상황

  • 초기 상태: main 브랜치에 calculator.js 파일이 있음
  • 개발자 A: feature/add-multiply 브랜치를 생성하여 곱셈 기능 추가
  • 개발자 B: feature/add-divide 브랜치를 생성하여 나눗셈 기능 추가
  • 두 개발자 모두 동일한 위치에 코드를 추가하여 충돌 발생

예시 코드 - 원본 파일 (calculator.js):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// Calculator Module
class Calculator {
    constructor() {
        this.result = 0;
    }

    add(a, b) {
        return a + b;
    }

    subtract(a, b) {
        return a - b;
    }

    // More operations will be added here
}

module.exports = Calculator;

개발자 A 의 변경사항 (feature/add-multiply):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Calculator Module
class Calculator {
    constructor() {
        this.result = 0;
    }

    add(a, b) {
        return a + b;
    }

    subtract(a, b) {
        return a - b;
    }

    // More operations will be added here
    multiply(a, b) {
        return a * b;
    }

    power(a, b) {
        return Math.pow(a, b);
    }
}

module.exports = Calculator;

개발자 B 의 변경사항 (feature/add-divide):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Calculator Module
class Calculator {
    constructor() {
        this.result = 0;
    }

    add(a, b) {
        return a + b;
    }

    subtract(a, b) {
        return a - b;
    }

    // More operations will be added here
    divide(a, b) {
        if (b === 0) {
            throw new Error("Division by zero is not allowed");
        }
        return a / b;
    }

    modulo(a, b) {
        return a % b;
    }
}

module.exports = Calculator;
충돌 발생 및 해결 과정
  1. 첫 번째 브랜치 병합 (성공)
1
2
3
4
# main 브랜치에서 개발자 A 작업 병합
git checkout main
git merge feature/add-multiply
# 성공적으로 병합됨 (Fast-forward 또는 3-way merge)
  1. 두 번째 브랜치 병합 (충돌 발생)
1
2
3
4
5
6
7
# 개발자 B 작업을 병합하려고 시도
git merge feature/add-divide

# 충돌 발생 메시지
Auto-merging calculator.js
CONFLICT (content): Merge conflict in calculator.js
Automatic merge failed; fix conflicts and then commit the result.
  1. 충돌 상태 확인
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
git status

# 출력 결과
On branch main
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>…" to mark resolution)
        both modified:   calculator.js
  1. 충돌 파일 내용 확인
    충돌이 발생한 calculator.js 파일을 열어보면:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// Calculator Module
class Calculator {
    constructor() {
        this.result = 0;
    }

    add(a, b) {
        return a + b;
    }

    subtract(a, b) {
        return a - b;
    }

<<<<<<< HEAD
    // More operations will be added here
    multiply(a, b) {
        return a * b;
    }

    power(a, b) {
        return Math.pow(a, b);
    }
=======
    // More operations will be added here
    divide(a, b) {
        if (b === 0) {
            throw new Error("Division by zero is not allowed");
        }
        return a / b;
    }

    modulo(a, b) {
        return a % b;
    }


   >>>>>>>  feature/add-divide

}

module.exports = Calculator;
  1. 충돌 마커 이해하기
  • <<<<<<< HEAD: 현재 브랜치 시작을 의미하며 현재 체크아웃된 브랜치 (HEAD) 의 변경사항 시작점이다.
  • =======: 구분선으로, 현재 브랜치와 병합하려는 브랜치의 변경사항을 구분한다.
  • >>>>>>> [branch-name]: 병합 브랜치 끝을 의미하며, 병합하려는 브랜치의 변경사항 끝점 (브랜치명 포함) 이다.
  1. 충돌 해결하기: 세 가지 해결 방법이 있다:
  2. 두 변경사항 모두 유지
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Calculator Module
class Calculator {
    constructor() {
        this.result = 0;
    }

    add(a, b) {
        return a + b;
    }

    subtract(a, b) {
        return a - b;
    }

    // More operations will be added here
    multiply(a, b) {
        return a * b;
    }

    power(a, b) {
        return Math.pow(a, b);
    }

    divide(a, b) {
        if (b === 0) {
            throw new Error("Division by zero is not allowed");
        }
        return a / b;
    }

    modulo(a, b) {
        return a % b;
    }
}

module.exports = Calculator;
  1. 한쪽 변경사항만 선택
1
2
3
4
5
# Alice의 변경사항만 유지
git checkout --ours calculator.js

# 또는 Bob의 변경사항만 유지
git checkout --theirs calculator.js
  1. 대화형 도구 사용
1
2
3
4
5
# VS Code의 병합 도구 사용
code calculator.js

# 또는 git mergetool 사용
git mergetool
  1. 해결 완료 후 커밋
1
2
3
4
5
6
7
8
# 충돌을 해결한 파일을 스테이징
git add calculator.js

# 충돌 해결 상태 확인
git status

# 병합 커밋 생성
git commit -m "Merge feature/add-divide into main - Added both multiply and divide operations"
충돌 해결 시 주의사항
  1. 코드 리뷰: 충돌 해결 후 코드가 정상적으로 동작하는지 확인
  2. 테스트 실행: 단위 테스트와 통합 테스트 실행
  3. 팀원과 소통: 중요한 변경사항은 팀원과 논의
  4. 일관성 유지: 코딩 스타일과 프로젝트 규칙 준수
충돌 예방 방법
  1. 자주 풀 (Pull) 하기: 원격 저장소의 최신 변경사항을 자주 가져오기
  2. 작은 단위로 커밋: 큰 변경사항을 작은 단위로 나누어 커밋
  3. 브랜치 전략: 명확한 브랜치 전략 수립 (예: Git Flow)
  4. 커뮤니케이션: 팀원들과 작업 내용 공유
고급 충돌 해결 기법
3-way Diff 도구 사용
1
2
3
4
5
6
# 설정
git config --global merge.tool vimdiff
git config --global mergetool.prompt false

# 사용
git mergetool
충돌 발생 시 중단하고 다시 시도
1
2
3
4
5
6
# 병합 중단
git merge --abort

# 브랜치 업데이트 후 재시도
git pull origin main
git merge feature/add-divide
충돌 기록 확인
1
2
3
4
5
# 충돌 발생 파일 목록
git diff --name-only --diff-filter=U

# 충돌 내용 상세 보기
git diff --check

Interactive Rebase 활용

여러 커밋을 한 번에 수정, 합치기, 순서 변경, 삭제, 분할 등 다양한 조작을 할 수 있는 강력한 도구이다.
주로 개인 브랜치 정리, 커밋 히스토리 정제, 커밋 메시지 수정 등에 활용한다.

1
2
3
git rebase -i <base-commit>
# 또는
git rebase -i HEAD~n   # 최근 n개 커밋을 대상으로
  • 에디터가 열리며, 커밋별로 pick/squash/edit/reword 등 원하는 동작을 선택
  • 주요 명령어/옵션
    • pick : 해당 커밋을 그대로 사용
    • reword : 커밋 메시지만 수정
    • edit : 커밋 내용 및 메시지 모두 수정
    • squash : 이전 커밋과 합치기 (메시지 합침)
    • fixup : 이전 커밋과 합치기 (메시지 버림)
    • drop : 해당 커밋 삭제

Remote 브랜치

  • 리모트 Refs: 원격 저장소에 있는 포인터인 레퍼런스로 원격 저장소에 있는 브랜치, 태그 등등을 의미.
  • 리모트 트래킹 브랜치: 리모트 트래킹 브랜치는 리모트 브랜치를 추적하는 레퍼런스이며 브랜치이다.
    • 로컬에 있지만 임의로 움직일 수 없다.
    • 리모트 서버에 연결할 때마다 리모트의 브랜치 업데이트 내용에 따라서 자동으로 갱신만 된다.
    • 리모트 저장소에 마지막으로 연결했던 순간에 브랜치가 무슨 커밋을 가리키고 있었는지를 나타낸다.
    • <remote>/<branch> 형식으로 원격 저장소 origin 의 master 브랜치를 보고 싶다면 origin/master 라는 이름으로 브랜치를 확인하면 된다.

git ls-remote [remote] 명령으로 모든 리모트 Refs 를 조회할 수 있다.
git remote show [remote] 명령은 모든 리모트 브랜치와 그 정보를 보여준다.

 git.ourcompany.com 이라는 Git 서버가 있고 이 서버의 저장소를 하나 Clone 하면 Git 은 자동으로 origin 이라는 이름을 붙인다. origin 으로부터 저장소 데이터를 모두 내려받고 master 브랜치를 가리키는 포인터를 만든다. 이 포인터는 origin/master 라고 부르고 멋대로 조종할 수 없다. 그리고 Git 은 로컬의 master 브랜치가 origin/master 를 가리키게 한다. 이제 이 master 브랜치에서 작업을 시작할 수 있다.

Clone 이후 서버와 로컬의 master 브랜치
https://git-scm.com/book/ko/v2/Git-%eb%b8%8c%eb%9e%9c%ec%b9%98-%eb%a6%ac%eb%aa%a8%ed%8a%b8-%eb%b8%8c%eb%9e%9c%ec%b9%98

로컬 저장소에 작업중 동시에 다른 팀원이 git.ourcompany.com 서버에 push 하고 master 브랜치를 업데이트하면 팀원 간의 히스토리는 달라진다. 로컬과 원격 서버의 커밋 히스토리는 독립적으로 이뤄지며 원격 서버로부터 저장소 정보를 동기화하려면 git fetch origin 명령을 사용한다.
“origin” 서버의 주소 정보를 찾아서, 현재 로컬 저장소가 갖고 있지 않은 새로운 정보가 있으면 모두 내려받고, 받은 데이터를 로컬 저장소에 업데이트하고 나서, origin/master 포인터의 위치를 최신 커밋으로 이동시킨다.

<code>git fetch</code> 명령은 리모트 브랜치 정보를 업데이트
https://git-scm.com/book/ko/v2/Git-%eb%b8%8c%eb%9e%9c%ec%b9%98-%eb%a6%ac%eb%aa%a8%ed%8a%b8-%eb%b8%8c%eb%9e%9c%ec%b9%98

원격 저장소를 여러 개 운영하는 상황을 가정해서 확인해보면, git remote add 명령을 통해 새로운 원격 저장소를 추가한다. 그리고, git fetch teamone 명령을 실행해도 teamone 서버의 데이터는 모두 origin 서버에도 있는 것들이라서 아무것도 내려받지 않는다. 리모트 트래킹 브랜치 teamone/master 가 teamone 서버의 master 브랜치가 가리키는 커밋을 가리키게 한다.

<code>teamone/master</code>의 리모트 트래킹 브랜치

브랜치 추적

리모트 트래킹 브랜치를 로컬 브랜치로 Checkout 하면 자동으로 " 트래킹 (Tracking) 브랜치 " 가 만들어진다 (트래킹 하는 대상 브랜치를 “Upstream 브랜치 " 라고 부른다).
트래킹 브랜치는 리모트 브랜치와 직접적인 연결고리가 있는 로컬 브랜치이다.
트래킹 브랜치에서 git pull 명령을 내리면 리모트 저장소로부터 데이터를 내려받아 연결된 리모트 브랜치와 자동으로 Merge 한다.

서버로부터 저장소를 Clone 을 하면 Git 은 자동으로 master 브랜치를 origin/master 브랜치의 트래킹 브랜치로 만든다.
트래킹 브랜치를 직접 만들 수 있는데 리모트를 origin 이 아닌 다른 리모트로 할 수도 있고, 브랜치도 master 가 아닌 다른 브랜치로 추적하게 할 수 있다.
git checkout -b <branch> <remote>/<branch> 명령으로 간단히 트래킹 브랜치를 만들 수 있다.
--track 옵션을 사용하여 로컬 브랜치 이름을 자동으로 생성할 수 있다.

1
2
3
$ git checkout --track origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'

이 명령은 매우 자주 쓰여서 더 생략할 수 있다. 입력한 브랜치가 있는 (a) 리모트가 딱 하나 있고 (b) 로컬에는 없으면 Git 은 트래킹 브랜치를 만들어 준다.

1
2
3
$ git checkout serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'

리모트 브랜치와 다른 이름으로 브랜치를 만들려면 로컬 브랜치의 이름을 아래와 같이 다르게 지정한다.

1
2
3
$ git checkout -b sf origin/serverfix
Branch sf set up to track remote branch serverfix from origin.
Switched to a new branch 'sf'

이제 sf 브랜치에서 Push 나 Pull 하면 자동으로 origin/serverfix 로 데이터를 보내거나 가져온다.

로컬 브랜치가 특정 Remote 브랜치를 추적하게 하기

로컬에 존재하는 브랜치가 리모트의 특정 브랜치를 추적하게 하려면 git branch 명령에 -u 나 --set-upstream-to 옵션을 붙여서 아래와 같이 설정한다.

1
2
$ git branch -u origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.

추적 브랜치가 현재 어떻게 설정되어 있는지 확인하려면 git branch 명령에 -vv 옵션을 더한다.
이 명령을 실행하면 로컬 브랜치 목록과 로컬 브랜치가 추적하고 있는 리모트 브랜치도 함께 보여준다.
게다가, 로컬 브랜치가 앞서가는지 뒤쳐지는지에 대한 내용도 보여준다.

1
2
3
4
5
$ git branch -vv
  iss53     7e424c3 [origin/iss53: ahead 2] forgot the brackets
  master    1ae2a45 [origin/master] deploying index fix
* serverfix f8674d9 [teamone/server-fix-good: ahead 3, behind 1] this should do it
  testing   5ea463a trying something new
  • iss53 브랜치:
    • origin/iss53 리모트 브랜치를 추적
    • “ahead” 표시를 통해 로컬 브랜치가 커밋 2 개 앞서 있다 (리모트 브랜치에는 없는 커밋이 로컬에는 존재)
  • master 브랜치:
    • origin/master 브랜치를 추적
    • 두 브랜치가 가리키는 커밋 내용이 같은 상태.
  • serverfix 브랜치:
    • server-fix-good 이라는 teamone 리모트 서버의 브랜치를 추적
    • 커밋 3 개 앞서 있으며 동시에 커밋 1 개로 뒤쳐져 있다.
      • serverfix 브랜치에 서버로 보내지 않은 커밋이 3 개, 서버의 브랜치에서 아직 로컬 브랜치로 머지하지 않은 커밋이 1 개 있다
  • testing 브랜치: 추적하는 브랜치가 없는 상태.

여기서 중요한 점은 명령을 실행했을 때 나타나는 결과는 모두 마지막으로 서버에서 데이터를 가져온 (fetch) 시점을 바탕으로 계산한다.
서버의 최신 데이터를 반영하지는 않으며 로컬에 저장된 서버의 캐시 데이터를 사용한다.
현재 시점에서 진짜 최신 데이터로 추적 상황을 알아보려면 먼저 서버로부터 최신 데이터를 받아온 후에 추적 상황을 확인해야 한다.

1
$ git fetch --all; git branch -vv

원격 브랜치 삭제

git push 명령에 --delete 옵션을 사용하여 리모트 브랜치를 삭제할 수 있다.
serverfix 라는 리모트 브랜치를 삭제하려면 아래와 같이 실행한다.

1
2
3
$ git push origin --delete serverfix
To https://github.com/schacon/simplegit
 - [deleted]         serverfix

위 명령을 실행하면 서버에서 브랜치 (즉 커밋을 가리키는 포인터) 하나가 사라진다.
서버에서 가비지 컬렉터가 동작하지 않는 한 데이터는 사라지지 않기 때문에 종종 의도치 않게 삭제한 경우에도 커밋한 데이터를 살릴 수 있다.

최적화하기 위한 고려사항 및 주의할 점

최적화 영역방법효과
브랜치 정리사용하지 않는 브랜치 주기적 삭제저장소 크기 최적화
병합 주기자주 작은 단위로 병합충돌 최소화, 리뷰 용이성
커밋 크기원자적 커밋 유지병합 시 롤백 용이성
히스토리 관리불필요한 병합 커밋 제거 (squash)깔끔한 이력 유지

분류에 따른 종류 및 유형

분류 기준종류설명
브랜치 전략Git Flow기능 개발, 릴리스, 핫픽스 등을 위한 브랜치 전략
GitHub Flow메인 브랜치 중심의 간단한 브랜치 전략
GitLab Flow환경별 브랜치를 포함한 전략
Trunk-Based Development메인 브랜치에 자주 병합하는 전략
수명장기 브랜치main, develop 등 지속적으로 유지되는 브랜치
단기 브랜치feature, hotfix 등 작업 완료 후 삭제되는 브랜치
목적Feature Branch새로운 기능 개발용
Release Branch릴리즈 준비용
Hotfix Branch긴급 버그 수정용
병합 전략Merge Commit병합 커밋을 생성하는 방식
Fast-Forward단순히 포인터를 이동하는 방식
Rebase커밋 이력을 재정렬하는 방식

최신 동향

주제항목설명
브랜치 전략Trunk-Based Development빠른 배포와 지속적인 통합을 위한 전략으로 주목받음
협업 도구GitHub Codespaces클라우드 기반 개발 환경으로 브랜치 작업 효율성 향상
자동화GitHub Actions병합 및 배포 자동화를 위한 워크플로우 도구로 활용 증가
CI/CD 강화머지 시 테스트/배포 자동화
AI 통합자동 충돌 해결머신러닝 기반 충돌 예측
모듈화컴포저블 아키텍처마이크로서비스 기반 브랜치 관리
보안 강화 브랜칭Secure Branch Policies취약점 스캔과 연동된 브랜치 보호 규칙
자동화된 브랜치 관리Semantic Branch Management커밋 메시지와 코드 변경을 분석하여 자동 브랜치 분류

주목해야 할 기술

주제항목설명
GitOpsFlux/ArgoCD Branch Sync브랜치 변경사항을 자동으로 쿠버네티스 클러스터에 배포
Branch AnalyticsCode Velocity Metrics브랜치별 개발 속도와 품질 메트릭 분석
Zero-Conflict MergingConflict-Free Replicated Data TypesCRDT 를 활용한 충돌 없는 병합 알고리즘
Branch Visualization3D Branch Graph복잡한 브랜치 구조를 3D 로 시각화하여 이해도 향상

앞으로의 전망

주제항목설명
브랜치 전략단순화복잡한 브랜치 전략보다 단순한 전략 선호 경향 증가
완전 자동화Autonomous BranchingAI 가 개발 패턴을 학습하여 브랜치 전략 자동 최적화
코드 품질병합 전 테스트 강화브랜치 병합 전에 실행되는 정적 분석 및 테스트 강화 추세
협업 방식단축된 리뷰 주기Pull Request 리뷰 자동화 및 사전 검증 시스템 도입 확대
분산형 협업 강화Decentralized Git블록체인 기반의 완전 분산형 버전 관리 시스템
실시간 병합Real-time Merge코드 작성 중 실시간으로 병합 가능성 검증
컨텍스트 인식 병합Context-aware Merging비즈니스 로직과 의존성을 이해하는 지능형 병합

하위 주제 및 추가 학습 필요 항목

카테고리주제설명
병합 전략Merge vs Rebase vs Squash각 전략의 용도, 히스토리 구조, 협업 관점 비교
충돌 해결Manual Merge Conflict Resolution실무에서 자주 발생하는 충돌 사례와 해결법 학습
Git 명령어Interactive Rebase커밋 재정렬, 스쿼시, 수정 등 고급 Git 커맨드 활용
Cherry-pick특정 커밋만 선택적으로 적용고급 병합 기법
Stash 활용임시 변경사항 저장 및 복원브랜치 전환 기법
Submodule 브랜칭서브모듈의 브랜치 관리 전략복합 프로젝트 관리
Monorepo 브랜칭대규모 단일 저장소의 브랜치 전략대규모 프로젝트

관련 분야 추가 학습 내용

관련 분야주제설명
DevOpsCI/CD 파이프라인병합 이벤트를 기반으로 한 자동 빌드 및 배포 설계
품질 보증 (QA)병합 전 테스트 자동화PR 생성 시 테스트 케이스 자동 실행 및 실패 처리 설계
협업 시스템리뷰 프로세스 설계머지 조건, 리뷰어 승인 정책, 자동 리뷰 트리거 정의
보안변경 사항 감사 (Git Audit)브랜치 기반 변경 로그 추적 및 감시 체계 구축
소프트웨어 아키텍처마이크로서비스와 브랜치 전략시스템 설계

용어 정리

용어설명
HEAD현재 체크아웃된 커밋 포인터
CI/CD지속적 통합/배포 (Continuous Integration & Delivery)
PR(Pull Request)코드 병합 요청 및 리뷰 프로세스
BranchGit 에서 독립적인 작업 공간을 생성하는 분기점
Merge두 브랜치를 통합하는 작업으로, 커밋을 결합함
Rebase브랜치의 기반을 다른 브랜치의 최신 커밋으로 변경하여 히스토리를 재작성하는 작업
Fast-forward브랜치가 다른 브랜치의 선행 커밋을 모두 포함하고 있을 때, 포인터만 이동하는 병합 방식
3-way Merge공통 조상 커밋을 기준으로 병합하는 표준 방식으로 충돌 발생 가능성이 있음
Interactive Rebase커밋 히스토리를 수정하거나 정리하기 위해 Git 에서 제공하는 대화형 rebase 기능
Feature Branch새로운 기능 또는 수정 사항을 작업하기 위해 생성된 브랜치
Merge Conflict동일 파일의 동일 위치가 다른 브랜치에서 변경되어 충돌이 발생하는 현상
Detached HEAD특정 커밋을 직접 체크아웃한 상태
Upstream원격 저장소의 원본 브랜치
Cherry-pick특정 커밋만 선택하여 현재 브랜치에 적용
Stash작업 중인 변경사항을 임시로 저장하는 기능

참고 및 출처