Merge, Squash and merge, Rebase and merge

호밀빵 굽는 쿼카·2022년 8월 23일
1

NHN Cloud

목록 보기
8/16

프로젝트 진행 시, develop 브랜치가 master에 합쳐지지 않고, 변경사항이 제대로 적용되지 않는 오류 발생 > 이유는 develop=>master 으로 squash and merge 로 머지했기 때문 > 관련된 내용을 더 찾아보던 중 좋은 내용의 블로그 발견
해당 블로그의 내용 복사 및 요약 수준입니다.

머지의 종류에는 3가지가 존재합니다.

  • Merge
  • Squash and merge
  • Rebase and merge

3가지 머지 전략 모두 브랜치를 머지한다는 목적은 같지만, 어떤 방식을 선택하냐에 따라 커밋 히스토리가 기록되는 방식이 달라지게 된다.
예를 들어 Git Flow를 사용할 때는 기능 개발을 하는 feature 브랜치가 develop 브랜치로 머지될 때는 Squash and merge를, develop 브랜치가 master 브랜치로 머지될 때는 Merge을 사용하는 등 유연하게 사용하기도 한다.

'커밋 히스토리' 가 중요한 이유

커밋(Commit)이란?

커밋(Commit)은 Git을 구성하는 중요한 요소 중 하나이며, 원칙적으로 하나의 커밋은 의미있는 하나의 변경사항을 의미한다. 그 말인 즉슨, 커밋 메세지만 보고도 어떤 사항이 어떤 이유로 변경되었는지 쉽게 파악할 수 있어야한다는 것이다. 많은 개발자들이 의미 있는 커밋 메세지에 대한 중요성을 언급하는 이유도 짧은 커밋 메세지만 보고도 언제, 어떻게 코드가 변경되었는가를 한번에 알고 싶기 때문이다.

이 커밋들이 모여서 시간 순으로 정렬된 것을 커밋 히스토리(Commit History)라고 부른다.

첫번째 이유, 버그가 언제 터졌는지 파악하기가 쉽다

프로그램의 변경 사항이 많을 수록, 혹은 프로그램의 규모 자체가 큰 경우 협업에 참여하고 있는 개발자들은 사소한 실수로 인해서 버그를 발생시킬 가능성 또한 커지게 된다. 이때 개발자들이 커밋 히스토리를 보고 어떤 이유로 어떤 코드가 수정되었는지 빠르게 파악할 수 있다면 해당 버그의 원인을 찾는 것이 더 빨라진다.

두번째 이유, 레거시 코드를 수정해야할 때 필요하다

레거시 코드를 분석한다는 것이 말이 쉽지, 실제로 거대한 어플리케이션에서 단 하나도 놓치지 않고 모든 의존 관계를 파악한다는 것은 사실 쉬운 일이 아니다. 게다가 이런 분석은 단순히 코드만 본다고 되는 것이 아니라 비즈니스와도 밀접한 관련이 있는 경우가 많기 때문에 해당 기능의 개발 당시 비즈니스 히스토리도 어느 정도 함께 파악하는 것이 좋다.

그나마 팀 내에 해당 기능을 개발하게 된 히스토리를 알고 있는 동료가 있다면 다행이지만, 그 마저도 없을 경우 우리가 의지할 것은 당시의 개발자가 어떤 의도로 코드를 고쳤는지 기록해놓은 커밋 히스토리 밖에 없는 것이다.

물론 정신없이 개발하는 와중에 커밋 메세지에 당시의 비즈니스적인 의도까지 담는 경우는 거의 없기 때문에 비즈니스 히스토리는 파악하기 힘들 수 있지만, 의미있는 단위로 커밋이 되어있다면 적어도 어떤 의도로 이 코드를 수정했는지 정도는 파악할 수 있다. 말 그대로 역사를 읽는 것이다. 하지만 이때 커밋 히스토리가 너무 쓸데 없이 복잡하거나 커밋 메세지가 개판이라면 아무래도 읽어나가는데 어려움이 있을 수 밖에 없다.

결론

그래서 개발자들이 의미 있는 단위의 커밋, 의미 있는 커밋 메세지를 강조하는 것이고 여기에 더해 적절한 머지 전략을 사용하여 가독성이 높고 의미도 있는 커밋 히스토리 그래프를 유지하려고 하는 것이다. 필자는 이 중 깔끔한 히스토리 그래프를 만드는 방법에 대해 설명하려고 하는 것이고, 이때 필요한 것이 적절한 브랜치 머지 전략의 선택인 것이다.

'커밋 히스토리' 를 깔끔하게 만드는 3가지 머지 전략

1. Merge

  • 머지(Merge)는 우리가 알고 있는 일반적인 머지 전략
  • 머지의 장점은 기존 개발자들에게 익숙한 개념이라는 것과 머지된 브랜치가 삭제되어 사라졌다고 해도 히스토리 그래프 상에서는 그대로 다른 가지로 표기되기 때문에 어떤 브랜치에서 어떤 커밋이 진행되어 어떻게 머지가 되었군이라는 자세한 정보를 얻을 수 있음
  • 반면에 단점은 너무 자세하게 히스토리가 남기 때문에 브랜치의 개수가 많아지거나 머지 횟수가 잦아질수록 히스토리 그래프의 가독성이 떨어진다는 것이다.
  • 머지 커밋(Merge commit)은 어느 순간에 어떤 브랜치의 변경사항이 머지되었다라는 소중한 정보를 주는 커밋이지만 개발이 진행되고 있는 브랜치가 많아진 상황에서는 이 머지 커밋들과 해당 브랜치에서 발생한 커밋들이 전부 기록되기 때문에 그래프가 너무 복잡해져서 오히려 히스토리를 추적하기 힘들 수도 있다.

2. Squash and merge

  • Squash and merge에서 Squash는 여러 개의 커밋을 하나로 합치는 기능을 말한다. 즉, 이 기능은 머지할 브랜치의 커밋을 전부 하나의 커밋으로 합친 뒤 타겟 브랜치에 커밋하는 방식으로 머지를 진행한다.
  • Squash and merge에서 발생하는 머지 커밋은 실질적인 머지로 인해서 생성된 머지 커밋이라기보다는 그냥 다른 브랜치의 변경 사항을 하나로 뭉쳐놓은 커밋인 것이다.
  • 머지된 브랜치의 자잘한 커밋 사항이 남지 않기 때문에 머지가 되었다라는 사실 자체에만 집중한 기록이 남게되고, 그로 인해 이 프로그램의 변경 사항을 읽기가 한결 수월해진다
  • 단점은 일반적인 머지 커밋보다는 아무래도 정보력이 떨어진다는 것이다. 일반 머지는 해당 브랜치에서 누가 어떤 커밋을 통해 어떤 라인을 수정 했는지 까지 알려주지만 Squash and merge 전략은 머지 대상 브랜치의 모든 커밋을 하나로 통합해버리기 때문에 그 정도의 자세한 정보는 알 수가 없다.
  • Squash and merge을 사용하여 브랜치를 머지하게 되면 머지된 사실 자체는 알 수 있으나 어떤 상황에서 어떤 코드를 변경 했는지까지는 알 수가 없다.

3. Rebase and merge

  • Rebase and merge 전략은 Git의 리베이스(Rebase) 기능을 사용하여 브랜치를 머지하는 것이다. 이때 리베이스는 말 그대로 브랜치 히스토리들의 베이스를 변경하는 기능이다. 베이스를 변경한다는 의미를 좀 더 쉽게 말하자면 a 브랜치의 변경 사항이 마치 b 브랜치에서 변경된 것처럼 바꿀 수 있다는 것이다.
  • tag 기능을 사용하여 해당 브랜치가 머지된 시점에 태그를 달아주는 것을 추천한다.
  • 리베이스의 치명적인 단점 중 하나는 바로 머지 충돌(Merge Conflict)이 발생했을 경우다. 이건 머지할 브랜치의 히스토리 자체를 그대로 복사해서 대상 브랜치의 히스토리에 박아버리는 방법이기 때문에 충돌이 발생하게 되면 Merge commit이나 Squash and merge처럼 충돌이 한번 발생하는 것이 아니라 각각의 커밋에 하나씩 충돌이 발생한다. 이게 머지할 브랜치의 커밋이 몇개 안되는 상황에서는 할만할지 몰라도 커밋이 몇 백개씩 되는 큰 기능의 브랜치를 리베이스로 머지했다가 충돌이 나면 위험

이 3가지 머지 전략은 각각 장단점이 명확하기 때문에 머지 전략 간의 우위는 없다. 우리는 feature 브랜치가 develop 브랜치로 머지될 때는 Squash and merge를, develop 브랜치가 master 브랜치로 머지될 때는 Merge를 진행한다. 추가로 feature 브랜치에서 기능별로 브랜치를 따서 develop으로 머지함으로써 좀 더 자세한 변경사항 내용을 알 수 있게 하고, 롤백 시 유용하게 하려고 한다.



참고링크

profile
열심히 굽고 있어요🍞

0개의 댓글