Programing

Git 브랜치는 어디에서 시작하고 길이는 얼마입니까?

lottogame 2020. 11. 21. 08:18
반응형

Git 브랜치는 어디에서 시작하고 길이는 얼마입니까?


때때로 나는 git의 특정 브랜치가 시작되는 커밋 또는 특정 브랜치에서 특정 커밋이 생성되었는지 여부를 묻습니다. 분기의 끝점은 매우 명확합니다. 분기 레이블이있는 곳입니다. 그러나-어디서 시작 되었습니까? 사소한 대답은 다음과 같습니다 . 우리 해당 분기를 만든 커밋에서 . 그러나 그 정보는 내가 지금 아는 한, 그것이 내가 질문하는 이유이며 첫 번째 커밋 후에 잃어버린 것입니다.

우리가 분기 한 커밋을 아는 한 그래프를 그려 명확하게 할 수 있습니다.

A - B - C - - - - J     [master]
     \
      D - E - F - G     [branch-A]
           \
            H - - I     [branch-B]

커밋에서 분기 B를 만들었 E으므로 이것이 "시작"입니다. 나는 그것을했기 때문에 그것을 안다. 그러나 다른 사람들도 같은 방식으로 인식 할 수 있습니까? 다음과 같이 동일한 그래프를 그릴 수 있습니다.

A - B - C - - - - J     [master]
     \
      \       F - G     [branch-A]
       \     /
        D - E
             \
              H - I     [branch-B]

이제 그래프를 보면 어떤 분기가에서 시작했고 어느 분기 EB? 커밋 D이 두 브랜치의 멤버입니까 아니면 브랜치 -A 또는 브랜치 -B에 속하는지 명확하게 결정할 수 있습니까?

이것은 다소 철학적으로 들리지만 실제로는 그렇지 않습니다. 감독자는 때때로 분기가 시작된시기 (일반적으로 작업의 시작을 표시 함)와 일부 변경 사항이 속한 분기 (일부 변경의 목적을 얻기 위해-작업에 필요한지 여부)를 알고 싶어합니다. git이 정보 (도구, 명령) 또는 정의를 제공하여 이러한 질문에 올바르게 대답하는지 알고 싶습니다.


Git에서는 모든 브랜치가 루트 커밋에서 시작한다고 말할 수 있으며 이는 말 그대로 사실입니다. 하지만 그것은 당신에게별로 도움이되지 않는 것 같습니다. 대신 할 수있는 것은 다른 분기와 관련하여 "분기의 시작"을 정의하는 것입니다. 이렇게 할 수있는 한 가지 방법은

git show-branch branch1 branch2 ... branchN

그러면 출력 하단에 지정된 모든 분기 간의 공통 커밋이 표시됩니다 (실제로 공통 커밋이있는 경우).

다음 은 Linux Kernel Git 문서의 예입니다.show-branch

$ git show-branch master fixes mhf
* [master] Add 'git show-branch'.
 ! [fixes] Introduce "reset type" flag to "git reset"
  ! [mhf] Allow "+remote:local" refspec to cause --force when fetching.
---
  + [mhf] Allow "+remote:local" refspec to cause --force when fetching.
  + [mhf~1] Use git-octopus when pulling more than one heads.
 +  [fixes] Introduce "reset type" flag to "git reset"
  + [mhf~2] "git fetch --force".
  + [mhf~3] Use .git/remote/origin, not .git/branches/origin.
  + [mhf~4] Make "git pull" and "git fetch" default to origin
  + [mhf~5] Infamous 'octopus merge'
  + [mhf~6] Retire git-parse-remote.
  + [mhf~7] Multi-head fetch.
  + [mhf~8] Start adding the $GIT_DIR/remotes/ support.
*++ [master] Add 'git show-branch'.

이 예에서는 분기 master와 비교됩니다 . 이 출력을 테이블로 생각하면 각 분기가 자체 열로 표시되고 각 커밋은 자체 행을 가져옵니다. 커밋을 포함하는 분기는 해당 커밋 행의 열에 또는 표시됩니다.fixesmhf+-

출력 맨 아래에서 3 개의 브랜치가 모두 공통 조상 커밋을 공유하고 실제로 다음의 head커밋임을 알 수 있습니다 master.

*++ [master] Add 'git show-branch'.

이것은 fixes둘 다 mhf에서 해당 커밋에서 분기 되었음을 의미합니다 master.

대체 솔루션

물론 이것은 Git에서 공통 기본 커밋을 결정하는 유일한 방법입니다. 다른 방법으로는 git merge-base공통 조상을 찾 git log --all --decorate --graph --oneline거나 gitk --all분기를 시각화하고 분기 위치를 확인하는 방법이 있습니다 (매우 빠르게 어려워지는 커밋이 많더라도).

원본 포스터의 다른 질문

이러한 질문에 대해서는

커밋가 D두 가지의 구성원 또는 우리가 명확하게이 속한 여부를 결정할 수 있습니다 branch-A또는 branch-B?

D 두 브랜치의 구성원이며 두 브랜치 모두에 대한 조상 커밋입니다.

감독자는 때때로 분기가 시작된시기 를 알고 싶어합니다 (보통 작업의 시작을 표시 함) ...

Git에서 전체 커밋 트리와 분기의 기록을 다시 작성할 수 있으므로 분기가 "시작" 할 때 TFS 또는 SVN과 같은 설정이 아닙니다. rebaseGit 트리의 어느 시점 으로든 분기 할 수 있으며 심지어 루트 커밋 이전에 배치 할 수도 있습니다! 따라서 원하는 트리의 어느 시점에서든 작업을 "시작"하는 데 사용할 수 있습니다.

이것은 git rebase브랜치를 업스트림 브랜치의 최신 변경 사항과 동기화하고, 브랜치에서 작업을 "방금 시작"한 것처럼 커밋 그래프를 따라 시간을 "앞으로"푸시 하는 일반적인 사용 사례입니다 . 실제로 한동안 작업 해 왔습니다. 원한다면 커밋 그래프를 따라 브랜치를 제 시간에 되돌릴 수도 있습니다 (브랜치 내용에 따라 많은 충돌을 해결해야 할 수도 있고 ... 아니면 그렇지 않을 수도 있습니다). 개발 히스토리의 중간에서 브랜치를 삽입하거나 삭제할 수도 있습니다 (그렇게하면 많은 커밋의 커밋 shas가 변경 될 수 있습니다). 재 작성 기록은 강력하고 유연하게 만드는 Git의 주요 기능 중 하나입니다.

이것이 커밋에 작성된 날짜 (커밋이 원래 작성되었을 때)와 커밋 된 날짜 (커밋이 커밋 트리에 마지막으로 커밋 된시기)가 모두있는 이유입니다. 시간-날짜 및 마지막으로 수정 된 시간-날짜를 생성하는 것과 유사하다고 생각할 수 있습니다.

감독자는 때때로 어떤 변화가 어느 지점에 속 하는지 알고 싶어 합니다 (일부 변화의 목적을 얻기 위해-작업에 필요 했는가).

다시 말하지만, Git을 사용하면 히스토리를 다시 작성할 수 있으므로 원하는 커밋 그래프의 거의 모든 브랜치 / 커밋에 대한 일련의 변경 사항을 (재)베이스 할 수 있습니다. git rebase문자 그대로 전체 분기를 자유롭게 이동할 수 있습니다 (분기를 이동하는 위치와 포함 내용에 따라 갈 때 갈등을 해결해야 할 수도 있음).

즉, Git에서 변경 사항 집합이 포함 된 분기 또는 태그를 확인하는 데 사용할 수있는 도구 중 하나는 다음과 --contains같습니다.

# Which branches contains commit X?
git branch --all --contains X

# Which tags contains commit X?
git tag --contains X

이 질문에 대한 현상금 통지는 다음과 같이 묻습니다.

Git 브랜치에 대해 루트 커밋 이외의 정의 된 "시작"커밋이있는 것으로 생각하는지 여부를 알고 싶습니다.

다음을 제외하고는 다음과 같습니다.

그것의 의미는:

  • 첫 번째 정의는 고정 된 커밋을 제공합니다 (대량의 경우를 제외하고는 절대 변경되지 않을 수 있음 filter-branch).
  • 두 번째 정의는 언제든지 변경 될 수 있는 상대 커밋 ( 다른 분기에 상대적)을 제공합니다 (다른 분기는 삭제할 수 있음).

두 번째는 브랜치 간의 병합 및 리베이스에 관한 모든 것입니다.

감독자는 때때로 분기가 시작된시기 (일반적으로 작업의 시작을 표시 함) 및 일부 변경 사항이 속한 분기 (일부 변경의 목적을 얻기 위해-작업에 필요한지 여부)를 알고 싶어합니다.

브랜치는 그에 대한 잘못된 표시 일뿐입니다. 브랜치의 일시적인 특성 (이름 변경 / 이동 / 재 기반 / 삭제 / ... 가능)으로 인해 브랜치가있는 "변경 세트"또는 "활동"을 모방 할 수 없습니다. "작업"을 나타냅니다.

이것이 XY 문제입니다 . OP는 실제 문제 (Git에서 작업으로 간주 될 수있는 것)가 아닌 시도 된 솔루션 (분기가 시작되는 위치 )을 요구합니다.

이를 수행하려면 (작업을 나타냄) 다음을 사용할 수 있습니다.

  • 태그 : 그것들은 불변이며 (한 번 커밋에 연결되면 해당 커밋은 더 이상 이동 / 재 기반되지 않아야 함) 이름이 잘 지정된 두 태그 사이의 커밋은 활동을 나타낼 수 있습니다.
  • 일부 git notes는 어떤 "작업 항목"이 커밋이 생성되었는지 기억하기 위해 커밋합니다 (태그와 달리 커밋이 수정되거나 다시 기반하면 메모를 다시 작성할 수 있음).
  • 후크 (커밋 메시지를 기반으로 "작업 항목"과 같은 일부 "외부"개체에 커밋을 연결) 이것이 바로 브릿지 Git-RTC (IBM Rational Team Concert)가 사전 수신 후크 로 수행하는 작업입니다. 요점은 분기의 시작이 항상 작업의 시작을 반영하는 것이 아니라 단순히 역사의 연속을 반영한다는 것입니다. 변경 될 수있는 순서와 논리적 변경 집합을 나타내는 순서입니다.

아마도 당신은 잘못된 질문을하고있을 것입니다. IMO, 그것은 분기 시작이 주어진 지점은 모든 파일의 모든 변경 사항이 포함되어 있기 때문에 위치를 물어 이해가되지 않습니다 (즉, 초기 커밋 이후를).

반면에 두 가지가 갈라진 곳을 묻는 것은 확실히 유효한 질문입니다. 사실, 이것이 바로 여러분이 알고 싶은 것 같습니다. 즉, 단일 브랜치에 대한 정보를 정말로 알고 싶지 않습니다. 대신 두 가지를 비교하는 방법에 대한 정보를 알고 싶습니다.

약간의 연구 를 통해 특정 커밋과 커밋 범위를 참조하는 세부 사항을 설명하는 gitrevisions man 페이지 가 나타 났습니다 . 특히,

커밋에서 도달 할 수있는 커밋을 제외하기 위해 접두사 ^ 표기법이 사용됩니다. 예를 들어 ^ r1 r2는 r2에서 도달 할 수있는 커밋을 의미하지만 r1에서 도달 할 수있는 것은 제외합니다.

이 집합 연산은 너무 자주 나타나서 그것에 대한 속기가 있습니다. 두 개의 커밋 r1 및 r2가있는 경우 (위의 수정 사항 지정에 설명 된 구문에 따라 이름이 지정됨), ^ r1 r2에 의해 r1에서 도달 할 수있는 것을 제외하고 r2에서 도달 할 수있는 커밋을 요청할 수 있으며 r1로 쓸 수 있습니다. .r2.

따라서, 귀하의 질문에서 예를 사용하여, 당신은 커밋을 얻을 수있는 곳 branch-A에서 발산 master

git log master..branch-A

나는 이것이 아마도 교육을위한 좋은 기회라고 생각합니다. git실제로 분기의 시작점을 기록하지 않습니다. 해당 브랜치에 대한 리플 로그에 생성 레코드가 여전히 포함되어 있지 않으면 시작 위치를 명확하게 결정할 수있는 방법이 없으며 브랜치가 어디에서나 병합 된 경우 실제로는 루트 커밋이 두 개 이상있을 수 있으며 가능한 여러 포인트가있을 수 있습니다. 생성되었을 수 있으며 원래 소스에서 분기되기 시작했습니다.

그러한 경우에 반대 질문을하는 것이 좋습니다. 왜 분기 된 위치를 알아야합니까, 아니면 분기 된 위치에서 유용한 방식으로 중요합니까? 이것이 중요한 이유가있을 수도 있고 없을 수도 있습니다. 대부분의 이유는 팀에서 채택하고 시행하려는 특정 워크 플로에 묶여있을 수 있으며 어떤 방식 으로든 워크 플로가 개선 될 수있는 영역을 나타낼 수 있습니다. 아마도 한 가지 개선 사항은 "올바른"질문이 무엇인지 파악하는 것입니다. 예를 들어, "어디에서 branch-B분기 했습니까?"보다는 " 분기가 branch-B"...에서 도입 한 수정 사항 / 새 기능을 포함하거나 포함하지 않는 것 "이있을 수 있습니다 .

이 질문에 대한 완전히 만족스러운 답변이 실제로 존재하는지 확신하지 못합니다 ...


여기에는 두 가지 별도의 문제가 있습니다. 귀하의 예에서 시작하여

A - B - C - - - - J     [master]
     \
      \       F - G     [branch-A]
       \     /
        D - E
             \
              H - I     [branch-B]

[...] 감독자는 분기가 시작된시기 (보통 작업의 시작을 표시 함)와 일부 변경 사항이 속한 분기 (일부 변경의 목적을 얻기 위해-작업에 필요한지 여부)를 알고 싶어합니다.

우리가 고기를 먹기 전에 두 가지 사실적 관찰 :

첫 번째 관찰 : 감독자가 알고 싶어하는 것은 커밋과 일부 외부 작업 주문 레코드 간의 매핑입니다. 버그 -43289 또는 featureB를 해결하는 커밋은 무엇입니까? strcat에서 사용량을 변경하는 이유는 무엇 longmsg.c입니까? 이전 푸시와이 푸시 사이의 20 시간 동안 누가 지불할까요? 지점 이름 자체는 여기서 중요하지 않습니다. 중요한 것은 커밋과 외부 관리 레코드의 관계입니다.

두 번째 관찰 : branch-A또는 branch-B먼저 게시 되는지 여부 (예 : 병합 또는 리베이스 또는 푸시를 통해), 커밋 D 및 E의 작업은 즉시 수행되어야하며 후속 작업에 의해 복제되지 않아야합니다. 그 커밋이 만들어 졌을 때 현재의 것이 무엇인지 전혀 차이가 없습니다. 여기서도 지점 이름은 중요하지 않습니다. 중요한 것은 조상 그래프를 통한 커밋의 관계입니다.


그래서 내 대답은 역사에 관한 한 지점 이름은 전혀 중요하지 않다는 것입니다. 해당 리포지토리에 특정한 목적으로 현재 어떤 커밋이 있는지 보여주는 편리한 태그입니다. 기본 병합 커밋 메시지 제목 줄에 유용한 모니 커를 원한다면 병합 git branch some-useful-name전 팁을 병합하고 병합하십시오. 어느 쪽이든 동일한 커밋입니다.

개발자가 커밋 할 때 체크 아웃 한 브랜치 이름을 외부 레코드 또는 모든 것과 함께 묶는 것은 "모든 것이 작동하는 한 괜찮은"영역에 깊이 들어가 있습니다. 하지 마십시오. 대부분의 VCS에서 일반적으로 사용되는 제한된 사용에도 불구하고 D-E-{F-G,H-I}나중에 발생하는 것이 아니라 더 빨리 발생하고 브랜치 명명 규칙을 조정하여이를 처리해야합니다. . .

왜 귀찮게? 커밋 메시지 하단의 태그 라인에 작업을 요청하는 보고서 번호를 입력하고 완료하십시오. git log --grep(그리고 일반적으로 git)은 타당한 이유로 엄청나게 빠릅니다.

다음과 같이 태그 라인을 삽입하는 매우 유연한 준비 후크조차도 간단합니다.

branch=`git symbolic-ref -q --short HEAD`                     # branch name if any
workorder=`git config branch.${branch:+$branch.}x-workorder`  # specific or default config
tagline="Acme-workorder-id: ${workorder:-***no workorder supplied***}"
sed -i "/^ *Acme-workorder-id:/d; \$a$tagline" "$1"

다음은 모든 커밋을 검사해야 할 때를위한 기본 사전 수신 후크 루프입니다.

while read old new ref; do              # for each pushed ref
        while read commit junk; do      # check for bad commits

                # test here, e.g. 
                git show -s $commit | grep -q '^ *Acme-workorder-id: ' \
                || { rc=1; echo commit $commit has no workorder associated; }
                # end of this test

        done <<EOD
        $(git rev-list $old..$new)
EOD
done
exit $rc

커널 프로젝트는 저작권 사인 오프 및 코드 검토 기록을 위해 이와 같은 태그 라인을 사용합니다. 정말 더 간단하거나 더 강력해질 수는 없습니다.

실제 스크립트의 전문화를 해제하기 위해 c & p 후에 손으로 직접 조작했습니다. 키보드 간 경고


철학적 관점에서 지부의 역사에 대한 질문은 세계적인 의미에서 답할 수 없습니다. 그러나은 특정 저장소에서reflog 각 지점의 기록 추적 합니다 .

따라서 모든 사람이 푸시하는 단일 중앙 저장소가있는 경우 해당 저장소를 사용 reflog하여이 정보를 추적 할 수 있습니다 ( 이 질문 에서 더 자세한 내용은 ). 먼저 중앙 저장소에서 reflog가 기록되고 정리되지 않도록합니다.

$ git config core.logAllRefUpdates true
$ git config gc.reflogExpire never

그런 다음 실행 git reflog <branchname>하여 지점의 기록을 검사 할 수 있습니다 .

테스트 저장소에 몇 번 푸시하여 샘플 커밋 그래프를 재현했습니다. 이제 이런 일을 할 수 있습니다.

$ git log --graph --all --oneline --decorate
* 64c393b (branch-b) commit I
* feebd2f commit H
| * 3b9dbb6 (branch-a) commit G
| * 18835df commit F
|/  
* d3840ca commit E
* b25fd0b commit D
| * 8648b54 (master) commit J
| * 676e263 commit C
|/  
* 17af0d2 commit B
* bdbfd6a commit A

$ git reflog --date=local master branch-a branch-b
64c393b branch-b@{Sun Oct 11 21:45:03 2015}: push
3b9dbb6 branch-a@{Sun Oct 11 21:45:17 2015}: push
18835df branch-a@{Sun Oct 11 21:43:32 2015}: push
8648b54 master@{Sun Oct 11 21:42:09 2015}: push
17af0d2 master@{Sun Oct 11 21:41:29 2015}: push
bdbfd6a master@{Sun Oct 11 21:40:58 2015}: push

따라서 내 예에서 branch-a처음 존재 했을 때 commit을 가리키고 F중앙 서버에 대한 두 번째 푸시가 커밋으로 이동했음을 알 수 있습니다 G. branch-b처음 등장 했을 때 commit을 가리키고 I아직 업데이트를 보지 못했습니다.

주의 사항

이것은 중앙 저장소에 푸시 된 기록 만 표시합니다 . 예를 들어 동료 branch-A가 commit A에서 시작 했지만 B푸시하기 전에 커밋에 다시 기반을 두면 해당 정보가 중앙 저장소의 reflog에 반영되지 않습니다.

이것은 또한 분기가 시작된 위치에 대한 명확한 기록을 제공하지 않습니다 . 우리는 정말 "소유"확인 지점 커밋에 대해 말할 수 DE그 처음에 마스터의 오프 갈래. 에서 생성 된 branch-a다음에 의해 선택 되었습니까 branch-b, 아니면 다른 방법으로 선택 되었습니까?

두 브랜치는 처음에 해당 커밋을 포함하는 중앙 저장소에 나타 났으며, 중앙 저장소에 먼저 나타난 브랜치를 reflog알려줍니다. 그러나 이러한 커밋은 format-patch등을 통해 여러 최종 사용자 저장소 사이에서 "전달"되었을 수 있습니다 . 따라서 어떤 분기 포인터가이를 중앙 서버로 먼저 전달했는지 알고 있지만 최종 출처는 알 수 없습니다 .


@cupcake가 설명했듯이 분기의 시작점이 없습니다. 한 지점이 다른 지점에 처음 닿은 지점 만 확인할 수 있습니다. 이것은 아마도 대부분의 경우 당신이 원하는 것입니다. @ code-guru는 이미 커밋 범위를 참조하는 구문을 설명했습니다.

모두 함께 퍼팅 :이 명령은 표시가 먼저가 있었다 커밋 전에 첫 번째 커밋 branch-A아니라에서 master:

git show `git rev-list branch-A ^master --topo-order | tail -n 1`~1


일부 건축 세부 사항

Git은 개정을 일련의 커밋으로 저장소에 저장합니다. 이러한 커밋에는 마지막 커밋 이후 파일의 변경 사항에 대한 정보에 대한 링크와 중요한 것은 이전 커밋에 대한 링크가 포함되어 있습니다. 넓은 의미에서 브랜치의 커밋 내역은 가장 최근 개정에서 저장소의 루트까지 단일 연결 목록입니다. 모든 커밋에서 리포지토리의 상태는 커밋 이전의 모든 커밋과 결합되어 루트로 돌아갑니다.

그래서 HEAD는 무엇입니까? 그리고 브랜치는 무엇입니까?

HEAD는 현재 활성 분기의 최신 커밋에 대한 특수 포인터입니다. master 1을 포함하여 각 분기 는 역사의 최신 개정판에 대한 포인터이기도합니다.

진흙처럼 깨끗합니까? Pro Git 책 의 이미지를 사용하는 예제를 살펴보면 다소 명확 해집니다. 2

간단한 Git 트리

이 다이어그램에는 4 개의 커밋이있는 비교적 간단한 저장소가 있습니다. 98ca9뿌리입니다. 마스터와 테스트의 두 가지 분기가 있습니다. 마스터 브랜치는 커밋 f30ab상태이고 테스트 브랜치는 87ab2. 현재 마스터 브랜치에서 작업 중이므로 HEAD는 마스터 브랜치를 가리 킵니다. 샘플 리포지토리의 브랜치 기록은 다음과 같습니다 (최신에서 오래된 것까지)

testing:  87ab2 -> f30ab -> 34ac2 -> 98ca9
 master:           f30ab -> 34ac2 -> 98ca9

이것에서 우리는 두 브랜치가에서 시작하여 동일 f30ab하다는 것을 알 수 있으므로 테스트가 해당 커밋에서 브랜치라고 말할 수도 있습니다.

Pro Git 책은 훨씬 더 자세히 설명되어 있으며 읽을만한 가치가 있습니다.

이제 우리는 해결할 수 있습니다.

구체적인 질문

Fancifying the diagram we get:

차를 마시는 새끼 손가락처럼 멋지다.

Is commit D a member of both branches or can we clearly decide whether it belongs to branch-A or branch-B?

Knowing what we now know, we can see that commit D is a member of both chains leading from the branch pointers to the root. Therefore we can say that D is a member of both branches.

Which branch started at E, which one at B?

Both branch-A and branch-B originate from the master branch at B, and diverge from each other at E. Git itself doesn't distinguish which branch owns E. At their core, the branches are just the chain of commits from newest to oldest ending up at the root.


1Fun Fact: The master branch is just an ordinary branch. It is no different from any other branch.

2 Pro Git 책은 Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License로 사용이 허가되었습니다.

참고 URL : https://stackoverflow.com/questions/17581026/where-does-a-git-branch-start-and-what-is-its-length

반응형