때로는 실제로 불가능하며 (추가 데이터가있는 경우가있는 경우를 제외하고) 여기에서 해결책이 작동하지 않습니다.
힘내는 참조 기록 (지점 포함)을 유지하지 않습니다. 각 분기 (헤드)의 현재 위치 만 저장합니다. 이것은 시간이 지남에 따라 git에서 일부 분기 기록을 잃을 수 있음을 의미합니다. 예를 들어 지점을 만들 때마다 어느 지점이 원래 지점인지 즉시 손실됩니다. 모든 지점은 다음과 같습니다.
git checkout branch1 # refs/branch1 -> commit1
git checkout -b branch2 # branch2 -> commit1
첫 번째 커밋이 브랜치라고 가정 할 수 있습니다. 이것은 사실이지만 항상 그런 것은 아닙니다. 위의 작업 후에 먼저 분기 중 하나에 커밋하는 것을 막을 것은 없습니다. 또한 git 타임 스탬프가 신뢰할 수 있다고 보장하지는 않습니다. 당신이 진정으로 구조적으로 가지가되기로 약속하기 전까지는 아닙니다.
다이어그램에서는 커밋을 개념적으로 지정하는 경향이 있지만 커밋 트리가 분기 될 때 git에는 안정적인 시퀀스 개념이 없습니다. 이 경우 숫자 (표시 순서)가 타임 스탬프에 의해 결정된다고 가정 할 수 있습니다 (모든 타임 스탬프를 동일하게 설정할 때 git UI가 작업을 처리하는 방법을 보는 것이 재미있을 수 있습니다).
이것은 인간이 개념적으로 기대하는 것입니다.
After branch:
C1 (B1)
/
-
\
C1 (B2)
After first commit:
C1 (B1)
/
-
\
C1 - C2 (B2)
이것이 실제로 얻는 것입니다.
After branch:
- C1 (B1) (B2)
After first commit (human):
- C1 (B1)
\
C2 (B2)
After first commit (real):
- C1 (B1) - C2 (B2)
B1을 원래 브랜치로 가정하지만 실제로는 단순히 죽은 브랜치 일 수 있습니다 (누군가 -b를 체크 아웃했지만 결코 커밋하지 않았습니다). git 내에서 합법적 인 브랜치 구조를 얻기 위해 커밋 할 때까지는 아닙니다.
Either:
/ - C2 (B1)
-- C1
\ - C3 (B2)
Or:
/ - C3 (B1)
-- C1
\ - C2 (B2)
C1은 C2와 C3보다 먼저 왔음을 항상 알고 있지만 C2가 C3보다 먼저 왔거나 C3이 C2보다 먼저 왔는지 확실하게 알 수는 없습니다 (예를 들어 워크 스테이션에서 시간을 설정할 수 있기 때문). 어느 지점이 먼저 왔는지 알 수 없으므로 B1과 B2도 오해의 소지가 있습니다. 많은 경우에 아주 좋고 일반적으로 정확한 추측을 할 수 있습니다. 레이스 트랙과 비슷합니다. 모든 것이 일반적으로 자동차와 동일하면 무릎 뒤에서 오는 자동차가 무릎 뒤에서 시작했다고 가정 할 수 있습니다. 우리는 또한 매우 신뢰할만한 협약을 가지고 있습니다. 예를 들어, 마스터는 거의 항상 가장 오래 살았던 지점을 대표하지만 슬프게도 이것이 사실이 아닌 경우를 보았습니다.
여기에 주어진 예는 기록 보존 예입니다.
Human:
- X - A - B - C - D - F (B1)
\ / \ /
G - H ----- I - J (B2)
Real:
B ----- C - D - F (B1)
/ / \ /
- X - A / \ /
\ / \ /
G - H ----- I - J (B2)
우리가 인간으로서 왼쪽에서 오른쪽으로, 루트에서 잎까지 (판독) 읽었 기 때문에 여기서도 실제 오해의 소지가 있습니다. 힘내 그렇게하지 않습니다. 우리가 머리에서하는 곳 (A-> B) git does (A <-B or B-> A). ref에서 root까지 읽습니다. 심판은 어느 곳에 나있을 수 있지만 적어도 활동적인 가지에서는 잎사귀 경향이 있습니다. 심판은 커밋을 가리키고 커밋은 자녀가 아닌 부모와 같은 것을 포함합니다. 커밋이 병합 커밋 인 경우 둘 이상의 부모가 있습니다. 첫 번째 부모는 항상 병합 된 원래 커밋입니다. 다른 부모는 항상 원래 커밋으로 병합 된 커밋입니다.
Paths:
F->(D->(C->(B->(A->X)),(H->(G->(A->X))))),(I->(H->(G->(A->X))),(C->(B->(A->X)),(H->(G->(A->X)))))
J->(I->(H->(G->(A->X))),(C->(B->(A->X)),(H->(G->(A->X)))))
이것은 매우 효율적인 표현이 아니라 git이 각 참조 (B1 및 B2)에서 취할 수있는 모든 경로의 표현입니다.
Git의 내부 저장소는 다음과 같습니다 (부모로서 A가 두 번 나타나는 것은 아님).
F->D,I | D->C | C->B,H | B->A | A->X | J->I | I->H,C | H->G | G->A
원시 자식 커밋을 덤프하면 0 개 이상의 부모 필드가 표시됩니다. 0이 없으면 부모가없고 커밋이 루트임을 의미합니다 (실제로 여러 루트를 가질 수 있음). 있는 경우 병합이없고 루트 커밋이 아니라는 의미입니다. 둘 이상이 있으면 커밋이 병합의 결과이고 첫 번째 이후의 모든 부모가 병합 커밋임을 의미합니다.
Paths simplified:
F->(D->C),I | J->I | I->H,C | C->(B->A),H | H->(G->A) | A->X
Paths first parents only:
F->(D->(C->(B->(A->X)))) | F->D->C->B->A->X
J->(I->(H->(G->(A->X))) | J->I->H->G->A->X
Or:
F->D->C | J->I | I->H | C->B->A | H->G->A | A->X
Paths first parents only simplified:
F->D->C->B->A | J->I->->G->A | A->X
Topological:
- X - A - B - C - D - F (B1)
\
G - H - I - J (B2)
둘 다 A를 칠 때 그들의 사슬은 같을 것이고, 그들의 사슬이 완전히 다를 것입니다. 또 다른 두 가지 커밋이 공통적으로 갖는 첫 번째 커밋은 공통 조상이며 어디서 시작했는지입니다. commit, branch, ref라는 용어가 혼동 될 수 있습니다. 실제로 커밋을 병합 할 수 있습니다. 이것이 병합이 실제로하는 일입니다. 참조는 단순히 커밋을 가리키고 분기는 폴더 .git / refs / heads의 참조에 지나지 않으며 폴더 위치는 참조가 태그와 같은 것이 아니라 분기라는 것을 결정합니다.
역사를 잃어버린 곳에서 병합은 상황에 따라 두 가지 중 하나를 수행합니다.
치다:
/ - B (B1)
- A
\ - C (B2)
이 경우 어느 방향 으로든 병합하면 현재 체크 아웃 된 분기에서 지정한 커밋으로 첫 번째 부모와 현재 분기에 병합 한 분기의 끝에서 커밋으로 두 번째 부모를 사용하여 새 커밋을 만듭니다. 공통된 조상 이후 결합되어야하는 두 가지 브랜치에 변경 사항이 있으므로 새 커밋을 만들어야합니다.
/ - B - D (B1)
- A /
\ --- C (B2)
이 시점에서 D (B1)는 이제 두 가지 (자체와 B2)의 변경 세트를 모두 갖습니다. 그러나 두 번째 지점에는 B1의 변경 사항이 없습니다. B1에서 B2로 변경 사항을 병합하여 동기화되도록하면 다음과 같은 것을 기대할 수 있습니다 (git 병합을 강제로 --no-ff로 수행 할 수 있음).
Expected:
/ - B - D (B1)
- A / \
\ --- C - E (B2)
Reality:
/ - B - D (B1) (B2)
- A /
\ --- C
B1에 추가 커밋이 있어도 얻을 수 있습니다. B1에없는 변경 사항이 B2에없는 한 두 분기가 병합됩니다. 하나의 브랜치 만 변경 세트를 가지고 있기 때문에 rebase와 달리 하나의 브랜치에서 다른 브랜치의 변경 세트를 적용 할 필요가 없다는 점을 제외하고는 rebase와 같은 빨리 감기를 수행합니다 (리베이스도 먹거나 선형화 기록).
From:
/ - B - D - E (B1)
- A /
\ --- C (B2)
To:
/ - B - D - E (B1) (B2)
- A /
\ --- C
B1에 대한 작업을 중단하면 장기적으로 역사를 보존 할 수 있습니다. B1 (마스터 일 수 있음) 만 일반적으로 진행되므로 B2 기록에서 B2의 위치는 B1에 병합 된 지점을 성공적으로 나타냅니다. 이것은 git이 A에서 B로 분기하기를 기대 한 다음 변경 사항이 누적되는만큼 원하는만큼 A를 B로 병합 할 수 있지만 B를 A로 다시 병합 할 때 B에서 더 이상 작업하지 않을 것으로 예상됩니다 . 지점으로 빨리 병합 한 후 지점에서 계속 작업하는 경우 작업중인 지점에서 매번 B의 이전 기록을 지 웁니다. 소스에 빨리 감기 커밋 한 다음 분기에 커밋 한 후에 매번 새 분기를 만들고 있습니다.
0 1 2 3 4 (B1)
/-\ /-\ /-\ /-\ /
---- - - - -
\-/ \-/ \-/ \-/ \
5 6 7 8 9 (B2)
1 ~ 3 및 5 ~ 8은 4 또는 9의 히스토리를 따르는 경우 표시되는 구조 분기입니다. git에서 명명되지 않은 참조되지 않은 구조 분기 중 명명되지 않은 참조 분기와 참조 분기 중 어느 것이 속하는지 알 수있는 방법은 없습니다. 구조의 끝. 이 그림에서 0 ~ 4는 B1에 속하고 4 ~ 9는 B2에 속하지만 4와 9를 제외하고 어느 브랜치에 어떤 브랜치가 속하는지 알 수 없었습니다. 그 환상. 0은 B2에 속하고 5는 B1에 속할 수 있습니다. 이 경우 16 개의 서로 다른 가능성이 있으며,이 경우 각각의 구조 분기가 속하는 분기를 명명 할 수 있습니다.
이 문제를 해결하는 여러 가지 git 전략이 있습니다. git merge가 빨리 감기되지 않도록하고 항상 merge branch를 만들 수 있습니다. 브랜치 히스토리를 유지하는 끔찍한 방법은 선택한 규칙에 따라 태그 및 / 또는 브랜치 (태그가 실제로 권장 됨)를 사용하는 것입니다. 나는 정말로 당신이 합류하고있는 지점에서 더미 빈 커밋을 권장하지 않을 것입니다. 매우 일반적인 규칙은 실제로 지점을 닫고 싶을 때까지 통합 지점으로 병합하지 않는 것입니다. 이것은 그렇지 않으면 지점이있는 지점에서 작업 할 때 사람들이 준수하려고 시도하는 관행입니다. 그러나 현실 세계에서 이상적인 것이 항상 실용적인 의미는 아닙니다. 옳은 일을하는 것이 모든 상황에 적합한 것은 아닙니다. 당신이 무엇을