Іноді це фактично неможливо (за деякими винятками, де вам може пощастити мати додаткові дані), і рішення тут не спрацьовують.
Git не зберігає історію поновлення (що включає гілки). Він зберігає лише поточну позицію для кожної гілки (голови). Це означає, що ви можете з часом втратити частину філій історії в git. Кожен раз, коли ви займаєтесь галуззю, одразу втрачається, яка саме гілка була оригінальною. Все відділення - це:
git checkout branch1 # refs/branch1 -> commit1
git checkout -b branch2 # branch2 -> commit1
Ви можете припустити, що перше, що було доручено - це галузь. Це, як правило, так, але це не завжди так. Ніщо не заважає вам спочатку скористатися будь-яким відділенням після вищеописаної операції. Крім того, часові позначки git не гарантуються надійними. Це поки ви не покладете на себе обох, що вони справді стають гілками структурно.
Хоча на діаграмах ми, як правило, числимо чинимо концептуально, git не має реального стійкого поняття послідовності, коли фіксується дерево гілок. У цьому випадку ви можете припустити, що цифри (із зазначенням порядку) визначаються часовою міткою (може бути цікаво побачити, як інтерфейс git обробляє речі, коли ви встановлюєте всі часові позначки однакові).
Це те, що людина чекає концептуально:
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)
Справжнє тут також вводить в оману, оскільки ми, як люди, читаємо це зліва направо, корінь на лист (ref). Гіт цього не робить. Де ми робимо (A-> B) в наших головах git робить (A <-B або 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
Якщо ви скидаєте необмежену команду git, ви побачите нульове або більше батьківських полів. Якщо є нуль, це означає, що немає батьків, а фіксація - корінь (ви можете мати декілька коренів). Якщо він є, це означає, що злиття не було, і це не кореневий запис. Якщо їх більше, це означає, що вчинення є результатом злиття, і всі батьки після першого є об'єднаннями.
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)
Коли обидва ударять А, їх ланцюг буде однаковим, до цього ланцюг буде зовсім іншим. Перший вчинок, що має ще два спільні дії, є спільним предком і від того вони розійшлися. тут може виникнути деяка плутанина між термінами зобов’язати, відділити і відіслати. Насправді ви можете об'єднати комісію. Це те, що насправді робить злиття. Посилання просто вказує на фіксацію, а гілка - це не що інше, як посилання на папку .git / refs / heads, розташування папки - це те, що визначає, що ref - це гілка, а не щось інше, наприклад тег.
Там, де ви втрачаєте історію, це злиття буде робити одну з двох речей залежно від обставин.
Поміркуйте:
/ - B (B1)
- A
\ - C (B2)
У цьому випадку злиття в будь-якому напрямку створить нову команду з першим батьківським, оскільки комісія, на яку вказує поточна перевірена гілка, а другий з батьків - як фіксація на кінчику гілки, яку ви об'єднали у вашу поточну гілку. Він повинен створити новий комітет, оскільки обидві гілки мають зміни, оскільки їх спільний пращур повинен поєднуватися.
/ - B - D (B1)
- A /
\ --- C (B2)
На даний момент D (B1) тепер має обидва набори змін з обох гілок (себе і B2). Однак друга гілка не має змін від B1. Якщо ви об'єднаєте зміни з B1 в B2, щоб вони були синхронізовані, то, можливо, ви очікуєте щось таке, що виглядає так (ви можете змусити git merge зробити це так, як це зроблено з --no-ff):
Expected:
/ - B - D (B1)
- A / \
\ --- C - E (B2)
Reality:
/ - B - D (B1) (B2)
- A /
\ --- C
Ви отримаєте це, навіть якщо B1 має додаткові комітети. Поки в B2 не буде змін, дві гілки будуть об'єднані. Це робить швидкий перемотка вперед, що схоже на ребазування (також бази їдять або історію лінеаризації), за винятком на відміну від повторної бази, оскільки лише одна гілка має набір змін, їй не потрібно застосовувати набір змін з однієї гілки поверх іншої від іншої.
From:
/ - B - D - E (B1)
- A /
\ --- C (B2)
To:
/ - B - D - E (B1) (B2)
- A /
\ --- C
Якщо ви припините роботу над B1, то в основному все добре для збереження історії в довгостроковій перспективі. Тільки B1 (який може бути головним) зазвичай просувається, тому розташування В2 в історії В2 успішно являє собою точку, що вона була об'єднана в B1. Це те, що git очікує від вас, щоб відгалужувати B від A, тоді ви можете об'єднати A в B скільки завгодно, як накопичуються зміни, однак при злитті B назад в A не очікується, що ви будете працювати над B і далі . Якщо ви продовжуєте працювати над своєю філією після швидкого переходу вперед, об'єднання її назад у гілку, над якою ви працювали, то кожен раз стираючи попередню історію Б. Ви дійсно створюєте нову гілку кожного разу після швидкого переходу вперед до джерела, а потім зобов’язання до гілки.
0 1 2 3 4 (B1)
/-\ /-\ /-\ /-\ /
---- - - - -
\-/ \-/ \-/ \-/ \
5 6 7 8 9 (B2)
1 - 3 і 5 - 8 - це структурні гілки, які відображаються, якщо ви слідкуєте за історією або 4, або 9. Ні в якому разі не можна дізнатися, до якої з цих безіменних і невіднесених структурних гілок належать названі і посилаються гілки як кінець будови. З цього креслення можна припустити, що 0 до 4 належить до B1, а 4 до 9 належить до B2, але крім 4 і 9 не можна було знати, до якої гілки належить, я просто намалював це таким чином, що дає ілюзія того. 0 може належати до B2, а 5 може належати B1. У цьому випадку існує 16 різних можливостей, до яких названа галузь, до якої могла б належати кожна структура.
Існує низка стратегій git, які допомагають вирішити цю проблему. Ви можете змусити git merge ніколи не переходити вперед і завжди створювати гілку злиття. Жахливим способом збереження історії філій є теги та / або гілки (теги дійсно рекомендуються) згідно з певним домовленістю. Я дійсно не рекомендував би пустую пусту комітку у гілці, в яку ви зливаєтесь. Дуже поширеною умовою є не об’єднуватись у галузь інтеграції, поки ви не захочете справді закрити свою філію. Це практика, якої люди повинні намагатися дотримуватися, оскільки в іншому випадку ви працюєте навколо того, щоб у вас були гілки. Однак у реальному світі ідеал не завжди є практичним, тому що робити правильну справу не під силу для кожної ситуації. Якщо те, що ти