GitHub Pull Request가 자동으로 close되는 경우는?

LINE 개발 팀에서 서버 쪽 개발을 담당하고 있는 Ohara(@kory1202)입니다. 저희 팀은 PR(Pull Request)을 master 브랜치(branch)로 생성하는 방식으로 Git을 운영하고 있습니다. 이런 방식으로 Git을 운영하면 PR을 merge했을 때 다른 PR이 ‘자동으로 close되는 상황’이 발생하곤 했는데요. 정확히 어떤 조건에서 자동 close되는 것인지 확실히 알 수 없었습니다. GitHub Help > Closing a pull request를 읽어 보아도 원하는 답이 나오지 않더군요. 그래서 아예 직접 정리를 해보았습니다.

자동 close란?

자동 close란 GitHub에 올라와 있는 PR이 UI를 통한 일련의 작업으로 close되는 것이 아니라 다른 요인에 영향을 받아 저절로 close되는 것을 말합니다. 구체적인 예를 통해 살펴보겠습니다. 다음 그림과 같이 커밋(commit)이 진행되었다고 가정해보겠습니다.

알지 못했던 점

앞서 말씀드렸지만, 자동 close가 되는 현상 자체는 인지했지만 세부적으로는 모르는 부분이 많았습니다.

  • 베이스 브랜치에 따라 자동으로 PR이 close되는 상황과 그렇지 않은 상황의 조건은 무엇인가?
  • master ←PR- develop ←PR- feature와 같은 상황, 즉 feature 브랜치에서 develop 브랜치로 PR을 생성하고, develop 브랜치에서 master 브랜치로 PR을 생성한 상황에서, feature에서 생성한 PR을 develop에 merge하기 전에 develop 브랜치를 master 브랜치로 merge하면, feature 브랜치에서 생성한 PR은 자동 close되는지?
  • master 브랜치에 hotfix가 반영되었다면 develop 브랜치 이하 개발 중인 모든 브랜치에 master 브랜치를 새로 반영할 필요가 있는지?
  • 어떤 동작을 수행하느냐에 따라 리뷰 전의 PR이 자신도 모르는 사이에 merge & close되는 큰 사고가 일어나는지?
  • 아직 리뷰하지 않은 PR이 자동으로 close되는 상황이 발생하는지? 있다면 어떤 조건에서 발생하는지?

위 다섯 가지 질문에 대한 답은 글 하단 요약 및 소감(#요약 및 소감) 섹션에 있으니 답을 먼저 알고 싶으신 분은 아래 이어지는 내용을 건너뛰고 ‘요약 및 소감’부터 보시면 될 것 같습니다.

그럼 PR이 어떻게 처리되는지 자세히 확인해 보겠습니다.

조사를 위한 브랜치 설정

어느 브랜치를 베이스로 두고 PR을 생성할지, 언제 merge할 것인지 등 각자 수립한 정책이나 운영 방식은 다를 수 있습니다. 하지만 주로 서비스 운영용과 QA용, 기타, 이렇게 3가지 용도로 브랜치를 나누어 사용하는 패턴이 보입니다. 이 패턴에 기초하여 브랜치가 다음과 같이 3개 있다고 가정하겠습니다. 그리고 PR의 베이스 브랜치와 merge, 자동 close를 조사 대상으로 두고 차근차근 살펴보겠습니다.

  • master 브랜치: 서비스 운영
  • develop 브랜치: QA 
  • feature 브랜치: 기타

특히 feature 브랜치 PR의 베이스를 master 브랜치로 할지, develop 브랜치로 할지에 따라 발생하는 차이에 포커스를 두고 있습니다.

조사 1. PR의 베이스 브랜치와 자동 close의 관계

첫 번째로, PR의 베이스 브랜치와 자동 close의 관계를 살펴보겠습니다. 첫 번째 상황은 feature 브랜치가 하나만 있는 상황입니다. 두 번째 상황은 두 개 혹은 그 이상의 feature 브랜치가 서로 의존 관계에 있는 상황입니다.

feature 브랜치가 하나일 때 

feature 브랜치가 하나만 존재하는 상황에서 다음 두 PR을 살펴봅시다.

  • master ←PR- develop: develop 브랜치에서 PR을 생성하여 master 브랜치로 merge
  • master ←PR- feature: feature 브랜치에서 PR을 생성하여 master 브랜치로 merge

master ←PR- feature

먼저, feature 브랜치에서 master 브랜치로 merge하려는 PR이 있는 상황입니다. 그리고 아래 그림처럼 또 하나의 PR이 있습니다. develop 브랜치에서 master 브랜치로 향하는 PR입니다.

위와 같이 두 개의 PR이 존재하는 상황에서 다음과 같은 작업을 수행해 보겠습니다.

  1. 로컬 환경에서 feature 브랜치를 develop 브랜치에 merge한 후 develop 브랜치 리모트에 push함
  2. GitHub에서 develop 브랜치를 master 브랜치에 merge함(자동 close 발생!)

2번 단계에서 feature 브랜치에서 생성한 PR이 자동으로 close됩니다.

develop ←PR- feature

앞서 살펴본 사례와 마찬가지로 feature 브랜치는 하나고, 생성된 PR은 두 개입니다. 위 사례에선 생성된 PR이 모두 master 브랜치를 향하고 있던 반면, 이번 사례에선 PR 하나는 feature 브랜치에서 develop 브랜치로 향하고 또 다른 PR은 develop 브랜치에서 master 브랜치로 향합니다.

위와 같이 PR이 두 개 생성되어 있는 상황에서 두 가지 시나리오를 생각해 볼까요?

  • 첫 번째 시나리오
  1. GitHub에서 feature 브랜치를 develop 브랜치로 merge함
  2. GitHub에서 develop 브랜치를 master 브랜치로 merge함
  • 두 번째 시나리오
  1. 로컬 환경에서 feature 브랜치를 develop 브랜치에 merge한 후 develop 브랜치 리모트에 push함(자동 close 발생!)
  2. GitHub에서 develop 브랜치를 master 브랜치에 merge함

두 번째 시나리오는 위 사례(master ←PR- feature)에서 살펴본 시나리오였는데요. 이번 사례(develop ←PR- feature)에서도 자동 close가 발생했는데 2번 단계가 아닌 1번 단계에서 feature 브랜치에서 생성한 PR이 자동 close되었습니다. 만약 feature 브랜치를 develop 브랜치에 merge하지 않은 채로 develop 브랜치를 바로 master 브랜치에 merge하면 어떻게 될까요? 역시 feature 브랜치에서 생성한 PR(베이스 브랜치가 develop 브랜치인 PR)은 자동 close될까요? 시도해 본 결과는 다음과 같았습니다.

  • feature 브랜치에서 생성한 PR은 close되지 않음
  • feature 브랜치에서 생성한 PR을 develop 브랜치에 merge하면, develop 브랜치의 PR을 다시 작성할 수 있게 됨
  • develop 브랜치는 feature 브랜치를 삭제하거나, feature 브랜치에서 생성한 PR의 베이스를 다른 브랜치로 바꾸는 등의 조치를 취하지 않으면 GitHub에서 삭제할 수 없게 됨 (단, 강제로 git push --delete origin develop 명령어로 develop 브랜치를 삭제하면 feature 브랜치에서 생성한 PR은 close돼버림)

의존 관계가 있는 feature 브랜치가 두 개 이상일 때

먼저 서로 의존 관계가 있는 두 개의 브랜치를 살펴보겠습니다. 아래 그림처럼 ‘hoge’라는 기능을 신규로 추가한 ‘add_hoge’ 브랜치와 더욱 기능을 개선한 ‘update_hoge’ 브랜치가 있다고 가정해봅시다.

add_hoge 브랜치에서 생성된 PR이 리뷰를 거치는 와중에 누군가 update_hoge 브랜치에서 PR을 생성할 수 있습니다. 혹은 새로운 기능의 개발 규모가 꽤 커서 좀 더 원활한 리뷰를 위해 PR을 나누고 싶을 수도 있습니다.

update_hoge 브랜치는 add_hoge 브랜치를 베이스로 두고 있기 때문에 update_hoge 브랜치에서 생성한 PR은 add_hoge로 보내게 될 것입니다. 이때 자동 close가 발생하는지 확인해보겠습니다.

master ←PR- add_hoge

총 3개의 PR이 아래 그림과 같이 발생합니다. 여기서 PR 3개를 실행하는 과정, 즉 develop 브랜치에 add_hoge와 update_hoge 브랜치를 반영하고, master 브랜치에 develop 브랜치를 반영하는 방법은 3가지가 있습니다.

  • 방법1
    1. GitHub에서 update_hoge 브랜치를 add_hoge 브랜치에 merge
    2. 로컬에서 develop 브랜치에 add_hoge 브랜치를 merge
    3. GitHub에서 develop 브랜치를 master 브랜치에 merge(add_hoge 브랜치가 자동 close됨)
  • 방법2
    1. 로컬에서 develop 브랜치에 add_hoge 브랜치와 update_hoge 브랜치를 merge
    2. GitHub에서 update_hoge 브랜치에서 생성한 PR의 베이스를 master로 변경
    3. GitHub에서 develop 브랜치를 master 브랜치에 merge(add_hoge 브랜치와 update_hoge 브랜치에서 생성한 PR 모두 자동 close됨)
  • 방법3
    1. 로컬에서 develop 브랜치에 add_hoge 브랜치와 update_hoge 브랜치를 merge
    2. GitHub에서 develop 브랜치를 master 브랜치에 merge(add_hoge 브랜치에서 생성한 PR은 자동 close되지만 update_hoge 브랜치에서 생성한 PR은 자동 close되지 않음) 
    3. update_hoge 브랜치에서 생성한 PR은 수동으로 close함

방법 2와 3은 add_hoge 브랜치의 리뷰가 끝나지 않았으니 add_hoge 브랜치에 update_hoge 브랜치를 merge하고 싶진 않지만, QA를 위해 develop 브랜치에는 merge하고 싶을 때 발생할 만한 사례입니다(리뷰가 끝나기도 전에 develop 브랜치에 merge하는 현실적이지 않은 사례지만, 자동 close 작동을 확인하기 위한 것임을 감안해주시기 바랍니다).

방법 2와 3의 사례에서 update_hoge 브랜치에서 생성한 PR의 베이스가 master 브랜치가 아니기 때문에 master 브랜치에 merge해도 자동 close되지 않는다는 것을 알게 되었습니다. 

develop ←PR- add_hoge

이번에도 앞선 사례와 동일하게 3개의 PR이 있지만, 아래 그림처럼 조금 다른 부분이 있습니다.

add_hoge 브랜치와 update_hoge 브랜치를 모두 develop 브랜치에 merge한 뒤, develop 브랜치를 master 브랜치에 merge하는 방법은 3가지가 있습니다.

  • 방법1
    1. GitHub에서 update_hoge 브랜치를 add_hoge 브랜치에 merge
    2. GitHub에서 add_hoge 브랜치를 develop 브랜치에 merge
    3. GitHub에서 develop 브랜치를 master 브랜치에 merge
  • 방법2
    1. GitHub에서 add_hoge 브랜치를 develop 브랜치에 merge
    2. GitHub에서 update_hoge 브랜치에서 생성한 PR의 베이스를 develop 브랜치로 변경
    3. GitHub에서 update_hoge 브랜치를 develop 브랜치에 merge
    4. GitHub에서 develop 브랜치를 master 브랜치에 merge
  • 방법3
    1. 로컬에서 update_hoge 브랜치를 develop 브랜치에 merge하여 push
      • add_hoge 브랜치에서 생성한 PR은 자동 close되지만, update_hoge 브랜치에서 생성한 PR은 자동 close되지 않음
      • update_hoge 브랜치에서 생성한 PR의 베이스는 develop 브랜치로 변경할 수 없게 됨. There are no new commits between base branch 'develop' and head branch 'update_hoge' 라는 에러가 발생
    2. update_hoge 브랜치에서 생성한 PR은 수동으로 close함

조사2. Hotfix가 master에 반영되었을 때의 대응

결론부터 말씀드리자면 master 브랜치에 hotfix가 merge되었을 때, develop 브랜치와 feature 브랜치에 master 브랜치를 다시 merge할 필요는 없습니다. 만약 베이스 브랜치에 변경이 발생했다고 해도 충돌(conflict)이 없다면 베이스를 향한 PR은 자동으로 close 됩니다.

단, master 브랜치를 다시 merge할 필요가 없는 것은 자동 close되는 점에 한정된 이야기입니다. 실제로는 master 브랜치에 반영된 코드가 정상 작동하는지 확인하고 싶어서 develop 브랜치와 feature 브랜치에 master 브랜치를 다시 반영하게 될 것입니다. 

아래 조사에서는 어떻게 베이스 브랜치(master 브랜치나 develop 브랜치)가 feature 브랜치에서 생성된 PR에 영향을 주는지 알아보겠습니다.

master ←PR- feature

아래 그림은 feature 브랜치를 master 브랜치에 merge하는 경우를 나타냅니다.

Master 브랜치를 feature 브랜치에 merge한 경우, feature 브랜치를 develop 브랜치에 다시 한번 merge할 필요가 있습니다. GitHub에서 develop 브랜치에서 생성한 PR을 master 브랜치에 merge했을 때, feature 브랜치에서 생성한 PR을 자동으로 close하기 때문입니다. 

GitHub에서 자동 close된 PR을 찾는 명령어는 git branch --merged입니다. Master 브랜치에서 feature 브랜치의 첫 commit을 찾을 수 없다면 자동 close되지 않습니다. 관련하여 실제 구현에 관해서 GitHub Community Forum에 문의했고 맞다는 답변을 받았습니다(완전히 엉뚱한 곳에다 질문했지만 친절하게 답변해주셨습니다).

develop ←PR- feature

Hotfix가 발생했을 때의 그림은 아래와 같습니다.

이번 경우는 간단합니다. Feature 브랜치와 master 브랜치가 충돌한다면, master 브랜치를 feature 브랜치에 merge하여 충돌을 없앱니다. 이후 feature 브랜치가 정상적으로 작동한다면 GitHub에서 develop 브랜치에 merge할 수 있습니다. 로컬에서 merge하면 feature 브랜치에서 생성한 PR은 자동 close됩니다.

요약 및 소감

마지막으로 ‘알지 못했던 점‘에서 제기했던 의문에 답변하는 방식으로 이번 포스팅을 마무리하겠습니다. 

Q. 어떤 경우에 PR이 자동으로 close되어 버릴까요? 

A. PR이 베이스 설정 및 조작에 따라 자동으로 close되는 경우는 아래와 같습니다.

  • 베이스 브랜치에 변경이 반영될 때 자동 close됨
    • 단, 이때 PR이 생성된 브랜치의 선두(head)가 베이스에 merge되어 있어야 함
    • 로컬에서 merge해서 push하더라도 변경을 반영하면 close됨
  • 베이스 브랜치가 없어지면 자동 close됨

자동으로 close되지 않는 경우는 아래와 같습니다.

  • 베이스 브랜치에서 생성된 PR이 close되어도 자동 close되지는 않음
  • 베이스가 master가 아닌 PR은 master 브랜치에 반영해도 자동 close되지 않음

Q. master ←PR- develop ←PR- feature 일 때 feature 브랜치 merge 전에 develop 브랜치를 merge하면 feature 브랜치에서 생성한 PR도 close되나요?

A. close되지 않습니다.

Q. master 브랜치에 hotfix가 반영된 경우 develop 브랜치 이하 개발 중인 모든 브랜치에 master 브랜치를 새로 반영할 필요가 있나요?

A. 충돌이 발생했을 때만 필요합니다. master 브랜치를 feature 브랜치에 merge한 경우에는 develop 브랜치에 다시 feature 브랜치를 merge할 필요가 있습니다. 

Q. 조작에 따라서 리뷰를 하기도 전에, 자신도 모르는 사이에 PR이 merge & close되어 버리는 것 같은 큰 사고가 일어날까요?

A. 발생하지 않습니다. 그런 일은 발생하지 않아서 안심하게 되었습니다. 

Q. 리뷰 완료 전에 PR이 close되는 경우가 있나요? 있다면 어떤 상황일까요?

A. 있습니다. develop ←PR- feature의 경우, 리뷰 완료 전 feature 브랜치의 QA를 진행하기 위해 로컬에서 feature 브랜치를 develop 브랜치에 merge해버리면 feature 브랜치의 PR은 close되어 버립니다. master ←PR- feature의 경우에는 같은 조작을 해도 feature 브랜치의 PR이 close되지 않습니다. 

마치며

이번 포스팅에서는 GitHub의 자동 close 동작을 확인했습니다. 어떤 조건일 때 자동으로 close되는지, 혹은 그렇지 않은지 잘 모르셨던 분들께 도움이 된다면 좋겠습니다. GitHub PR이 의도치 않게 merge되는 큰 사고에 관해서도 정리하려고 했지만, 최근엔 그런 경우는 발생하지 않아서 의도치 않은 순간에 close되는 정도만 정리되었네요. 혹시 GitHub 조작 실수 등으로 큰 사고가 발생하는 패턴을 알고 있다면 여기로 알려주시면 감사하겠습니다.

Related Post