100===Dev Ops/Git

Git Rebase 가이드 - 커밋 히스토리 정리부터 안전하게 푸시하기까지

블로글러 2025. 4. 22. 18:27

1. Git Rebase 개념

git rebase는 특정 브랜치의 커밋 시퀀스를 다른 베이스 커밋 위에 재조정하는 Git의 핵심 작업입니다. 이 과정에서 기존 커밋들은 새로운 커밋으로 재생성되어 선형적 히스토리를 구성합니다.

핵심 기능:

  • 히스토리 최적화: 세분화된 커밋들을 의미론적 단위로 통합(squash, fixup), 커밋 메시지 수정(reword), 커밋 순서 재조정 등을 통해 로컬 커밋 이력을 체계화합니다.
  • 브랜치 동기화: 작업 브랜치를 베이스 브랜치(예: main, develop)의 최신 상태에 재배치함으로써 코드 베이스를 최신으로 유지하고 향후 병합 충돌의 복잡성을 최소화합니다.

2. Rebase 사용의 기술적 이점

  • 선형적 히스토리 구조: git merge가 생성하는 병합 커밋 없이 커밋들을 순차적으로 적용하여 직관적이고 추적 가능한 이력을 구성합니다.
  • 코드 리뷰 효율성 향상: 논리적 단위로 재구성된 커밋 셋은 리뷰어가 변경사항의 의도와 영향을 명확히 파악할 수 있게 합니다.
  • 점진적 충돌 해소: 베이스 브랜치와의 정기적 동기화를 통해 대규모 충돌 리스크를 분산시키고 관리 가능한 단위로 해결할 수 있습니다.

3. 실무 적용 시나리오 및 구현 방법

A. 브랜치 최신화 (git rebase <base_branch>)

# 1. 작업 브랜치로 전환
git checkout feature

# 2. 베이스 브랜치 최신 상태 획득
git fetch origin main

# 3. 작업 브랜치의 커밋을 origin/main 위에 재배치
git rebase origin/main

# 충돌 발생 시 해결 후 계속 진행
# git rebase --continue

B. 커밋 이력 정제 (git rebase -i <base>)

대화형 리베이스를 통해 <base> 커밋 이후의 이력을 편집할 수 있습니다.

  • 베이스 지정 방식:

    • HEAD~N: 현재 위치에서 N개 이전 커밋부터
    • <commit-id>: 특정 커밋 ID 이후부터
    • <branch_name>: 특정 브랜치 분기점 이후부터
  • 대화형 인터페이스 주요 명령어:

      pick a1b2c3d feat: 초기 기능 구현
      pick e4f5g6h fix: 사소한 버그 수정 (WIP)
      pick i7j8k9l feat: 추가 기능 구현
    
      # 명령어 옵션:
      # p, pick <commit> = 커밋 유지
      # r, reword <commit> = 커밋 사용, 메시지 수정
      # e, edit <commit> = 커밋 사용, 수정을 위해 중지
      # s, squash <commit> = 이전 커밋과 통합, 메시지 병합
      # f, fixup <commit> = 이전 커밋과 통합, 메시지 폐기
      # d, drop <commit> = 커밋 제거
      # 기타 옵션들...
  • 고급 리베이스 예시: 최근 커밋 3개 통합

    1. 명령 실행: git rebase -i HEAD~3

    2. 편집기 수정:

       pick a1b2c3d 주요 기능 구현squash e4f5g6h 부분 수정 1fixup i7j8k9l 부분 수정 2
    3. 저장 후 통합 커밋 메시지 최종 편집

    4. 결과: 세 커밋이 단일 의미론적 커밋으로 최적화됨

4. 리베이스 충돌 해결 프로세스

  1. 충돌 발생 시점 식별: Git이 리베이스 중단 지점과 충돌 파일을 표시합니다.
  2. 충돌 해결: 마커(<<<<<<<, =======, >>>>>>>)가 표시된 파일 내 영역을 수정합니다.
  3. 변경사항 스테이징: git add <resolved_file>로 해결된 파일을 등록합니다.
  4. 리베이스 계속 진행: git rebase --continue로 프로세스를 재개합니다.

대안 옵션:

  • git rebase --skip: 현재 충돌 커밋의 변경사항을 무시하고 다음으로 진행
  • git rebase --abort: 리베이스 전체를 취소하고 원래 상태로 복귀

5. Rebase의 핵심 원칙

절대 규칙: 공유 저장소에 이미 푸시되어 다른 개발자와 공유된 커밋은 리베이스하지 않는다.

  • 위험성: 리베이스는 기존 커밋을 폐기하고 신규 커밋을 생성합니다. 타 개발자가 원본 커밋을 기반으로 작업 중이라면, 리베이스된 이력을 강제 푸시할 경우 해당 개발자의 작업 기반이 소실되어 심각한 이력 불일치와 작업 손실이 발생합니다.

  • 안전한 적용 범위:

    • 원격 저장소에 미푸시된 로컬 커밋
    • 개인 전용 브랜치 (타 개발자가 기반으로 사용하지 않는 것이 확실한 경우)

6. 리베이스 후 안전한 푸시 전략 (--force-with-lease)

리베이스로 히스토리가 재구성된 경우, 일반 git push는 거부됩니다. 이는 원격 저장소 이력 보호 메커니즘입니다.

  • 일반적 오류: git pull 실행 시 원격의 이전 이력과 로컬의 재구성된 이력이 병합되어 불필요한 병합 커밋이 생성됩니다.

  • 최적 해결책: git push --force-with-lease <remote> <branch>

    • --force보다 안전한 방식으로, 원격 브랜치가 마지막 동기화 상태와 동일한지 검증합니다.

    • 다른 개발자의 커밋이 추가된 경우 푸시를 중단하여 작업 손실을 방지합니다.

    • 원격 상태가 예상과 일치할 경우에만 이력을 갱신합니다.

      git push --force-with-lease origin feature-branch
  • 주의사항: 공유 브랜치에 적용 시에는 팀 내 사전 커뮤니케이션이 필수적이며, 이후 다른 개발자들은 git fetch origingit reset --hard origin/feature-branch로 로컬을 동기화해야 합니다.

7. 복구 메커니즘 (git reflog)

리베이스 오류나 의도치 않은 결과 발생 시 git reflog를 통해 이전 상태로 복구할 수 있습니다.

  • reflog는 HEAD 위치 변경 이력을 보존합니다.
  • git reflog로 리베이스 이전 커밋 해시를 식별합니다.
  • git reset --hard <commit-hash>로 이전 상태로 복구합니다 (현재 작업은 소실되므로 주의).

8. Rebase와 Merge 비교 분석

특성 git rebase git merge
이력 구조 선형적, 정제된 이력 (커밋 재생성) 브랜치 분기/병합 명시적 표현 (병합 커밋 생성)
커밋 식별자 신규 해시값 생성 원본 커밋 해시값 유지
공유 브랜치 적용 엄격히 금지됨 안전하게 적용 가능
충돌 관리 커밋별 발생 가능 (다중 해결 필요) 단일 병합 시점에 통합 해결
주요 활용 영역 로컬 이력 최적화, 작업 브랜치 동기화 완료된 기능의 메인 브랜치 통합

팀의 워크플로우와 프로젝트 특성에 따라 최적의 전략이 달라질 수 있습니다. 선형적 이력 관리를 선호하면 rebase, 분기 이력의 명시적 보존이 중요하면 merge가 적합합니다. 일반적으로 작업 브랜치 내에서는 rebase로 이력을 정제하고, 메인 브랜치 통합 시에는 --no-ff 옵션과 함께 merge를 적용하는 하이브리드 접근법이 효과적입니다.

결론

git rebase는 이력 관리와 코드베이스 동기화를 위한 강력한 도구이지만, 부적절한 적용 시 심각한 협업 문제를 야기할 수 있습니다. 공유된 이력의 리베이스 금지 원칙을 준수하고, 리베이스된 브랜치 푸시 시 --force-with-lease를 통한 안전장치 활용이 필수적입니다. 이러한 원칙과 모범 사례를 준수하면 리베이스를 통해 깔끔하고 추적 가능한 이력 관리가 가능합니다.

728x90