28P by neo 5달전 | favorite | 댓글 1개
  • 많은 사람들이 Git branch의 작동 방식을 직관적이지 않다고 생각함
  • Git 브랜치에 대한 일반적인 직관적 모델과 실제 Git 내부에서 브랜치가 어떻게 표현되는지에 대한 차이점을 설명함
  • 직관적 모델과 실제 Git의 작동 방식이 실제로 매우 밀접하게 관련되어 있음을 보여줌
  • 직관적 모델의 한계와 문제를 일으킬 수 있는 이유를 논의함

직관적인 브랜치 모델

  • 많은 사람들이 브랜치를 '사과나무의 가지'에 비유하여 생각함.
  • Git에서 브랜치는 '부모' 개념이 없으며, 'main'에서 분기된 것으로 생각하는 것과 다름.

Git에서 브랜치는 전체 히스토리

  • Git에서 브랜치는 단순히 분기된 커밋이 아니라 모든 이전 커밋의 전체 히스토리를 포함함.
  • 예제 저장소를 통해 mainmybranch 모두 4개의 커밋을 가지고 있음을 보여줌.

브랜치는 커밋 ID로 저장됨

  • Git 내부에서 브랜치는 커밋 ID를 포함한 작은 텍스트 파일로 저장됨.
  • 각 브랜치의 최신 커밋이 해당 파일에 기록되어 있음.
  • 커밋 간의 부모-자식 관계가 없기 때문에, 브랜치 간의 관계를 Git은 알지 못함.

사람들의 직관은 보통 그렇게 틀리지 않음

  • Git에 대한 사람들의 직관이 '틀렸다'고 말하는 것은 다소 어리석음.
  • '틀린' 모델도 실제로 유용할 수 있음.

리베이스는 '직관적' 브랜치 개념을 사용함

  • 리베이스는 '직관적' 브랜치의 커밋만을 main에 재적용함.
  • 리베이스 결과는 직관적 모델과 일치함.

머지도 '직관적' 브랜치 개념을 사용함

  • 머지는 커밋을 복사하지 않지만, 공유된 기반 커밋을 필요로 함.
  • 머지 베이스는 직관적 모델에 기반한 브랜치가 분기된 커밋을 찾아줌.

GitHub 풀 리퀘스트도 직관적 아이디어를 사용함

  • GitHub에서 mybranchmain에 머지하려는 풀 리퀘스트를 생성하면, 직관적 브랜치의 커밋만 보여줌.

직관은 좋지만 한계가 있음

  • 직관적 브랜치 정의는 실제 Git 작업과 잘 맞지만, Git은 main과 분기된 브랜치를 다르게 인식하지 못함.

트렁크와 분기된 브랜치

  • 사람들은 mainmybranch를 다르게 인식하며, 이는 Git 사용 방식에 영향을 줌.
  • Git은 브랜치가 다른 브랜치의 '분기'인지 구분하지 않음.

Git은 리베이스를 '역방향'으로 할 수 있음

  • Git은 브랜치가 다른 브랜치의 '분기'인지 알려주지 않으므로, 언제 어떤 브랜치를 리베이스해야 하는지 사용자가 알아야 함.
  • git rebase main 과 역방향 리베이스인 git rebase mybranch가 모두 가능. 머지도 마찬가지

Git 브랜치 간의 계층 구조 부재는 다소 이상함

  • main 브랜치가 특별하지 않다는 말은 Git이 브랜치 간의 관계를 인식하지 못하기 때문에 나온 것임.
  • 브랜치 사이에는 관계가 있지만, git은 아무것도 모름

Git 브랜치 UI도 이상함

  • '분기된' 커밋만 보고 싶을 때 git loggit diff를 사용하는 방법이 다름.

GitHub에서 기본 브랜치는 특별함

  • GitHub는 '기본 브랜치'를 가지며, 이는 특별한 역할을 함.

GN⁺의 의견

이 글에서 가장 중요한 것은 Git 브랜치에 대한 사람들의 직관적 이해와 실제 Git의 작동 방식 간의 차이를 이해하는 것임. 이 글은 초급 소프트웨어 엔지니어들이 Git 브랜치의 개념을 더 잘 이해하고 효과적으로 사용할 수 있도록 도와줄 것임. Git 브랜치의 직관적 모델이 실제 작업과 어떻게 일치하는지, 그리고 Git이 브랜치 간의 관계를 어떻게 처리하지 않는지를 알아보는 것은 흥미롭고 유익함.

Hacker News 의견
  • 브랜치는 커밋을 가리키는 포인터이며, 새로운 커밋이 생성될 때마다 이 포인터가 갱신됨. 브랜치는 태그처럼 떠돌아다니는 이름이라고 볼 수 있음. 커밋 자체가 부모 커밋을 가리키기 때문에 브랜치라는 것은 관련 커밋들의 연쇄로, 명명된 진입점을 가짐. 브랜치를 삭제하면 더 이상 명명된 레이블이 없어져서, 단순히 관련 커밋들의 연쇄로만 남게 됨.
  • 커밋의 계보를 '앞으로'가 아닌 '뒤로' 가리키는 포인터로 생각하면 이해하기 쉬움. 브랜치는 커밋 ID이므로 부모 링크를 거슬러 올라가면 해당 브랜치의 전체 역사를 찾을 수 있음. '브랜치 포인트'는 두 커밋 체인이 만나는 지점이며, 병합 커밋은 특별한데, 이는 두 역사가 하나로 합쳐졌음을 나타냄.
  • 개인적인 프로젝트에서 git reset --hardgit stash를 사용하여 변경 사항과 브랜치 포인터를 조작하는 것을 친구들이 보면 화를 내곤 함. 잘못된 병합을 취소하려면 git reset --hard <병합 전 마지막 커밋>을 사용하고, 로컬 브랜치의 작은 수정 사항을 메인 브랜치에 적용하려면 git stash를 사용한 후 메인 브랜치로 체크아웃하여 git stash apply를 사용함.
  • Git에는 'main이 특별하다'는 개념이 없지만, Gitlab과 같은 도구들은 보호된 브랜치 기능을 제공하여 실수를 줄일 수 있음. '부모'와 '자식' 브랜치 개념이 실제로 흥미로울 수 있으며, 장기 지원 브랜치를 위해 여러 '부모' 브랜치를 지원해야 함.
  • 병합, 리베이스, 풀 리퀘스트를 할 때 다른 브랜치를 명시적으로 지정해야 함. Git은 사용자가 기반으로 생각하는 브랜치를 알지 못하기 때문임. 때로는 기능 브랜치를 다른 기능 브랜치에 병합하고 싶을 수 있으므로, 어떤 브랜치를 다른 브랜치에 병합할지 명확히 지정해야 함.
  • 사람들이 가지고 있는 직관이 기술적으로 부분적으로 틀릴 수 있어도, 그들이 그런 직관을 가진 데는 타당한 이유가 있음.
  • git addgit commit 사용법을 아는 사람들을 대상으로 한 동적인 튜토리얼이 있음. 이 튜토리얼은 브랜치를 시각화하며 읽을 수 있도록 도와줌.
  • Git 명령어를 수행할 때 '항상' 현재 브랜치를 수정한다는 것을 기억하면 Git의 문법을 '쉽게' 이해할 수 있음. 예를 들어, git merge my-branch는 현재 브랜치에 my-branch를 병합하고, git rebase my-branch는 현재 브랜치를 my-branch 위에 리베이스함.
  • 브랜치(헤드)가 해당 브랜치가 시작된 기저 커밋을 가리키는 '꼬리'를 가지는 것이 좋을 것 같음. 브랜치가 자주 리베이스되기 때문에 어디서 시작하는지 생각해야 할 때가 있음. Git이 기저 커밋이 main에 속한다고 알려주면 더 편리함.
  • 메일링 리스트에 '패치'를 보낼 때 기저 커밋을 선택적으로 포함할 수 있음. 이는 변경 사항이 최신 릴리스, 메인 개발 브랜치, 또는 통합 브랜치 중 어디에 기반하는지 명확하지 않을 수 있기 때문임. git range-diff를 사용할 때도 기저를 염두에 두어야 함. 이 도구는 main..previousmain..current와 같은 두 범위를 비교함.
  • 브랜치에 대한 개인적인 견해를 다시 읽고 잊었던 몇 가지를 다시 배움.