Art of Clean Pull Requests - 클린한 Git PR의 기술

직장 동료인 소프트웨어 엔지니어 Alex의 프리젠테이션을 한국어로 번역한 포스트입니다.
Git을 실무에서 사용함에 있어 공감하고 배울 내용이 많아 번역해보았습니다.
세팅이나 기초적인 사용법에 대해서가 아닌, 효율적인 실무를 위한 Branch 관리 및 PR 방법에 대하여 전반적으로 다루고 있습니다.

그 외에도 이해를 돕기 위한 추가적인 내용이 들어가 있습니다.

Art of Clean Pull Requests - Presentation
Alex Yeung

목차

간략한 개요

Git은 어떻게 동작하는가?

Local Machine

모든 코드 변경사항이 로컬환경에 실시간으로 저장되는 곳입니다.

Origin

당신의 코드가 존재하는 '원격(remote)' Github 저장소입니다.
당신이 모든 제어를 가지며, 일반적으로 원본 소스에 대한 Fork 저장소 경로입니다.
https://github.com//GitUser/test.git 같은 주소를 사용하며, origin이라는 remote 이름을 사용합니다.

Upstream

또 다른 '원격(remote)' 저장소입니다.
master 혹은 develop branch의 코드를 가져올 저장소입니다.
Fork의 대상이 되는 실제 원본 소스를 관리하는 저장소입니다.
https://github.com/Organization/test.git 같은 주소를 사용하며, upstream이라는 remote 이름을 사용합니다.

EXXPEN (EXXPEN은 Alex가 사용하는 User ID)

origin이나 upstream이라는 remote 이름은 당신을 위해 입력된 임의의 단어입니다. (원하는 데로 변경해서 사용할 수 있지만, origin과 upstream이라는 명칭을 사용하길 권장합니다)
origin은 당신의 Fork 저장소, upstream은 원본 저장소입니다

그리고 각각의 remote는 URL과 연결되어있습니다.

원격 저장소별로 분리된 Branch

각 "원격저장소(remote)" 별로 고유의 Branch를 가지고 있습니다.
따라서 아래와 같이 수행해야 합니다.

# upstream의 현재 데이터를 가져옵니다. git fetch upstream # upstream 저장소의 reelase-2.67 Branch를 로컬환경에 생성 후 checkout git checkout -b upstream/release-2.67 # origin(나의 fork 저장소)에 release-2.67 Branch를 push 합니다. git push origin release-2.67

팁과 트릭

당신의 작업 사항을 나중을 위해 임시 저장하고 싶다면?

예를 들면 현재 로컬에서 작업 중에 긴급한 변경사항이 생겼을 시, 긴급 작업용 branch로 checkout을 시도합니다.
이때 현재 작업 중인 코드에 대한 경고를 발생시킵니다.
현재 작업 사항을 임시저장 하고 싶다면 아래와 같은 옵션이 있습니다.

Option 1

# 현재 수정된 사항을 add후 메시지와 함께 commit git commit -am "brb" # 긴급한 작업 Branch로 checkout, 작업 완료 후 다시 원래 Branch로 checkout # commit 한 사항을 reset 명령어로 복구 git reset HEAD~1

Option 2

# stash 명령어로 현재 변경된 코드를 임시 위치로 이동 git stash # 긴급한 작업 Branch로 checkout, 작업 완료 후 다시 원래 Branch로 checkout # git stash apply로 임시 이동된 코드를 복구
# git stash pop으로도 사용 가능합니다. git stash apply

역주) 개인적으로 Option 2를 권장합니다.

당신의 Commit을 다른 브런치로 재설정 시키기

예를 들어 당신의 branch와 원본 branch의 git log가 꼬였을 경우 사용하는 방법입니다.

# 로컬 환경에 develop Branch로 checkout git checkout develop # 로컬 환경의 코드들을 원본 소스의 develop Branch와 같이 hard reset 시킵니다. git reset upstream/develop --hard # 자신이 fork 한 저장소로 강제 push 합니다. # force 옵션이 없으면 당신의 현재 branch와 충돌이 일어납니다. git push origin develop -f

당신의 최근 커밋에 약간의 변경사항이 추가될 경우

예를 들어 당신이 작업 사항을 commit 후 추가로 약간의 변경사항이 생겼다고 합시다.
이때 새로 commit 메시지를 추가해서 혼란스럽게 만드는 게 옳은 방법일까요?
그럴 경우 아래와 같은 두 가지 옵션이 있습니다.

option 1

# 이전 commit으로 소프트 리셋 git reset -soft HEAD~1 # 해당 commit 메시지를 변경합니다 git commit --amend

option 2

# commit 생성 git commit -am "Final change" # rebase를 이용한 commit 사항 수정 git rebase -i HEAD~1 (Then either use fixup or squash)

역주) rebase에 대해서 주제에서 더 자세히 다룹니다.

Feature Branches!

위 workflow는 master/develop Branch가 아닌, 새로운 기능(feature) 개발을 위해 분리한 Branch에 대해 초점을 맞춥니다.

Feature라고 쓰여 있지만 실제로 Feature Branch에 대한 Branch 이름은 해당 기능에 연관되게 그리고 식별할 수 있게 지어주시면 됩니다.

왜 기능별로 Branch를 나눠야 하는가?

develop Branch에서 작업하는 게 아닌 각 새로운 기능(Feature)별로 Branch로 만들어서 작업하는 방식은 아래와 같은 이점이 있습니다.

  • 적은 merge commits! (이후에 자세히 설명합니다)
  • 코드 캡슐화
  • Branch를 더 체계적으로 유지
  • 불변적인 upstream branch들
  • Pull Request!
  • 개별적이고 쉬운 테스트

Merge commit

여러 Branch에 대한 merge commit에 대한 내용입니다.

Merge commit? 무엇을? 언제? 어떻게?

merge commit을 통해 서로 다른 Branch가 Commit 된 시점을 기록 할 수 있습니다.
두 개의 Head(가장 최근의 Commit)를 하나의 Commit(일반적으로 Feature 및 Develop Branch)로 통합해야 합니다.

아래와 같은 명령어를 사용할 수 있습니다.

git pull

git pull은 git fetch && git merge에 대한 별칭이며, 자동으로 실행시킵니다. (대게 사용자가 모르게 수행됩니다)

Rebase

Rebase? 무엇을? 언제? 어떻게?

merge commit을 하는 대신, 당신의 기능(Feature) Branch를 최신 master branch에 재배치(rebase) 할 수 있습니다.

rebase를 통해 선형 히스토리관리가 가능해집니다.
위의 이미지를 보세요! Git 히스토리에 분기가 없어져서 깔끔합니다.

Rebase는 Git 히스토리를 다시 재배치합니다.

git rebase Origin/develop

무엇이 다르고 무엇이 나은가요?

역주)
두 가지 방식은 각각의 장단점이 있기 때문에, 프로젝트 성격이나 팀 상황 등 각종 외부요소를 생각하여 판단하시면 될 것 같습니다.
일반적으로 Merge 방식을 많이 사용하기 때문에 Rebase에 대한 아주 좋은 예제인 ESLint의 Github 저장소 경로도 첨부했습니다.

Feature Branch 병합하기 (Merge)

  • 히스토리를 다시 작성할 필요 없음
  • 쉽고 간단한 변경사항 Push
  • Branch가 많아질수록 선형 Git 히스토리보다 이전 작업 사항 추적이 어렵고 복잡함.

Feature Branch 재배치하기 (Rebase)

  • 잦은 Git 히스토리 재작성
  • 현재 변경사항을 알고 있어야 함.
  • 깔끔한 선형 Git 히스토리

Rebase를 통한 Git 히스토리 관리 예
ESLint - Commit List
ESLint - PR

정리: 깔끔하게 PR을 생성하는 방법

git checkout -b feature

당신의 개발 Branch를 upstream/develop으로 재배치한 후 새로운 기능에 대한 Branch를 만드세요.
이렇게 하면 불필요한 Merge commit이 필요하지 않고 보다 깔끔하게 저장소를 관리할 수 있습니다.

Rebase/Merge Upstream

당신의 Feature Branch에서 작업하는 동안 당신이 커밋한 사항이 다른 Branch와 동기화되지 않습니다.
따라서 PR(Pull Request)시 Git 충돌을 방지하기 위해서 Merge/Rebase 하는 것이 중요합니다.

Github desktop App 다운로드!!

이것은 Slack, Atom, VS Code와 같은 Electron으로 만들어진 App입니다.
그렇기 때문에 200mb 이상의 램을 기본적으로 소모하지만 이 애플리케이션을 통해 보다 쉽게 코드의 차이를 확인하고 각 커밋에 대해 특정 줄을 추가할 수 있습니다.

유용한 상세정보 기입

신중한 PR 제목과 설명은 리뷰어가 이해하는 데 큰 도움이 됩니다.

보다 나은 Git Message 작성에 관련해서 아래 링크를 참고해주세요.

How to Write a Git Commit Message

마치며

Rebase를 통한 선형 히스토리관리에 대해서 알고 있었지만 왜? 사용하는가에 대한 의문이 있었는데, 이 발표를 보니 바로 와 닿네요.

그 외에도 직접 말한 발표내용이 아니고 프리젠테이션만 번역하다 보니 이해를 돕기위한 추가적인 설명이 많이 들어가게 되네요. 글 새로 쓰는 기분.

참고

Art of Clean Pull Requests - Presentation
How to Write a Git Commit Message
ESLint - Commit List
ESLint - PR

A successful Git branching model