Kadang-kadang secara efektif tidak mungkin (dengan beberapa pengecualian di mana Anda mungkin beruntung memiliki data tambahan) dan solusi di sini tidak akan berfungsi.
Git tidak menyimpan riwayat referensi (termasuk cabang). Hanya menyimpan posisi saat ini untuk setiap cabang (kepala). Ini berarti Anda dapat kehilangan beberapa sejarah cabang di git seiring waktu. Setiap kali Anda bercabang misalnya, segera hilang cabang mana yang asli. Semua yang dilakukan cabang adalah:
git checkout branch1 # refs/branch1 -> commit1
git checkout -b branch2 # branch2 -> commit1
Anda mungkin berasumsi bahwa komitmen pertama adalah cabang. Ini cenderung menjadi masalah tetapi tidak selalu demikian. Tidak ada yang menghentikan Anda dari melakukan ke salah satu cabang terlebih dahulu setelah operasi di atas. Selain itu, cap waktu git tidak dijamin dapat diandalkan. Tidak sampai Anda berkomitmen untuk keduanya bahwa mereka benar-benar menjadi cabang secara struktural.
Sementara dalam diagram kita cenderung melakukan angka secara konseptual, git tidak memiliki konsep sekuens yang stabil ketika pohon komit bercabang. Dalam hal ini Anda dapat menganggap angka-angka (urutan menunjukkan) ditentukan oleh cap waktu (mungkin menyenangkan untuk melihat bagaimana UI git menangani hal-hal ketika Anda mengatur semua cap waktu menjadi sama).
Inilah yang manusia harapkan secara konseptual:
After branch:
C1 (B1)
/
-
\
C1 (B2)
After first commit:
C1 (B1)
/
-
\
C1 - C2 (B2)
Inilah yang sebenarnya Anda dapatkan:
After branch:
- C1 (B1) (B2)
After first commit (human):
- C1 (B1)
\
C2 (B2)
After first commit (real):
- C1 (B1) - C2 (B2)
Anda akan menganggap B1 sebagai cabang asli tetapi itu bisa benar-benar menjadi cabang mati (seseorang melakukan checkout -b tetapi tidak pernah berkomitmen untuk itu). Baru setelah Anda berkomitmen untuk keduanya Anda mendapatkan struktur cabang yang sah dalam git:
Either:
/ - C2 (B1)
-- C1
\ - C3 (B2)
Or:
/ - C3 (B1)
-- C1
\ - C2 (B2)
Anda selalu tahu bahwa C1 datang sebelum C2 dan C3 tetapi Anda tidak pernah andal tahu jika C2 datang sebelum C3 atau C3 datang sebelum C2 (karena Anda dapat mengatur waktu di workstation Anda dengan apa pun misalnya). B1 dan B2 juga menyesatkan karena Anda tidak tahu cabang mana yang lebih dulu. Anda dapat membuat tebakan yang sangat bagus dan biasanya akurat dalam banyak kasus. Ini agak seperti trek balap. Semua hal umumnya sama dengan mobil maka Anda dapat mengasumsikan bahwa mobil yang datang di pangkuan mulai pangkuan di belakang. Kami juga memiliki konvensi yang sangat andal, misalnya master akan hampir selalu mewakili cabang yang paling lama hidup meskipun sayangnya saya telah melihat kasus di mana bahkan ini tidak terjadi.
Contoh yang diberikan di sini adalah contoh pelestarian sejarah:
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)
Nyata di sini juga menyesatkan karena kita sebagai manusia membacanya dari kiri ke kanan, root to leaf (ref). Git tidak melakukan itu. Di mana kita melakukan (A-> B) di kepala kita git (A <-B atau B-> A). Bunyinya dari ref ke root. Ac bisa di mana saja tetapi cenderung daun, setidaknya untuk cabang aktif. Seorang ref menunjuk ke sebuah komit dan melakukan hanya berisi suka kepada orang tua mereka, bukan untuk anak-anak mereka. Ketika komit adalah gabungan komit, komit akan memiliki lebih dari satu orangtua. Orang tua pertama selalu merupakan komit asli yang digabungkan. Orang tua lain selalu melakukan komitmen yang digabungkan ke dalam komitmen asli.
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)))))
Ini bukan representasi yang sangat efisien, melainkan ekspresi dari semua jalur yang dapat diambil git dari setiap referensi (B1 dan B2).
Penyimpanan internal Git lebih mirip seperti ini (bukan berarti A sebagai orangtua muncul dua kali):
F->D,I | D->C | C->B,H | B->A | A->X | J->I | I->H,C | H->G | G->A
Jika Anda membuang komit git mentah, Anda akan melihat nol atau lebih bidang induk. Jika ada nol, itu berarti tidak ada orang tua dan komit adalah root (Anda sebenarnya dapat memiliki beberapa root). Jika ada satu, itu berarti tidak ada penggabungan dan itu bukan komit root. Jika ada lebih dari satu itu berarti komit adalah hasil penggabungan dan semua orang tua setelah yang pertama adalah gabungan komit.
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)
Ketika keduanya mengenai A rantai mereka akan sama, sebelum itu rantai mereka akan sama sekali berbeda. Komitmen pertama yang dimiliki dua komitmen lainnya adalah leluhur yang sama dan dari mana mereka menyimpang. mungkin ada beberapa kebingungan di sini antara istilah commit, branch, dan ref. Anda sebenarnya bisa menggabungkan komit. Inilah yang sebenarnya dilakukan penggabungan. Seorang ref hanya menunjuk ke sebuah komit dan sebuah cabang tidak lebih dari sebuah ref dalam folder .git / refs / head, lokasi folder adalah yang menentukan bahwa ref adalah cabang daripada sesuatu yang lain seperti sebuah tag.
Di mana Anda kehilangan sejarah adalah penggabungan akan melakukan satu dari dua hal tergantung pada keadaan.
Mempertimbangkan:
/ - B (B1)
- A
\ - C (B2)
Dalam hal ini gabungan di kedua arah akan membuat komit baru dengan induk pertama sebagai komit yang ditunjukkan oleh cabang yang dicentang saat ini dan induk kedua sebagai komit di ujung cabang yang Anda gabungkan ke cabang Anda saat ini. Itu harus membuat komitmen baru karena kedua cabang memiliki perubahan sejak nenek moyang mereka yang harus digabungkan.
/ - B - D (B1)
- A /
\ --- C (B2)
Pada titik ini D (B1) sekarang memiliki kedua set perubahan dari kedua cabang (itu sendiri dan B2). Namun cabang kedua tidak memiliki perubahan dari B1. Jika Anda menggabungkan perubahan dari B1 ke B2 sehingga mereka disinkronkan maka Anda mungkin mengharapkan sesuatu yang terlihat seperti ini (Anda dapat memaksa git merge untuk melakukannya seperti ini dengan --no-ff):
Expected:
/ - B - D (B1)
- A / \
\ --- C - E (B2)
Reality:
/ - B - D (B1) (B2)
- A /
\ --- C
Anda akan mendapatkannya meskipun B1 memiliki komitmen tambahan. Selama tidak ada perubahan dalam B2 yang tidak dimiliki B1, kedua cabang akan digabungkan. Itu melakukan fast forward yang seperti rebase (rebases juga memakan atau linearisasi sejarah), kecuali tidak seperti rebase karena hanya satu cabang yang memiliki set perubahan, ia tidak harus menerapkan perubahan dari satu cabang di atas itu dari yang lain.
From:
/ - B - D - E (B1)
- A /
\ --- C (B2)
To:
/ - B - D - E (B1) (B2)
- A /
\ --- C
Jika Anda berhenti bekerja pada B1 maka banyak hal baik untuk menjaga sejarah dalam jangka panjang. Hanya B1 (yang mungkin master) yang akan maju secara khusus sehingga lokasi B2 dalam riwayat B2 berhasil menunjukkan titik bahwa ia digabungkan ke dalam B1. Inilah yang git harapkan Anda lakukan, untuk bercabang B dari A, maka Anda dapat menggabungkan A menjadi B sebanyak yang Anda suka dengan perubahan yang terakumulasi, namun ketika menggabungkan B kembali ke A, tidak diharapkan bahwa Anda akan bekerja pada B dan selanjutnya . Jika Anda terus bekerja pada cabang Anda setelah maju cepat menggabungkannya kembali ke cabang yang Anda kerjakan maka menghapus riwayat B sebelumnya setiap kali. Anda benar-benar membuat cabang baru setiap kali setelah komit maju cepat ke sumber lalu komit ke cabang.
0 1 2 3 4 (B1)
/-\ /-\ /-\ /-\ /
---- - - - -
\-/ \-/ \-/ \-/ \
5 6 7 8 9 (B2)
1 sampai 3 dan 5 hingga 8 adalah cabang struktural yang muncul jika Anda mengikuti sejarah untuk 4 atau 9. Tidak ada cara di git untuk mengetahui mana dari cabang struktural yang tidak disebutkan namanya dan tidak direferensikan milik dengan cabang bernama dan referensi sebagai ujung struktur. Anda mungkin berasumsi dari gambar ini bahwa 0 hingga 4 milik B1 dan 4 hingga 9 milik B2 tetapi selain dari 4 dan 9 tidak dapat mengetahui cabang mana yang dimiliki cabang mana, saya hanya menggambarnya dengan cara yang memberikan ilusi itu. 0 mungkin milik B2 dan 5 mungkin milik B1. Ada 16 kemungkinan yang berbeda dalam hal ini yang dinamai cabang masing-masing cabang struktural milik.
Ada sejumlah strategi git yang mengatasi hal ini. Anda dapat memaksa git merge untuk tidak pernah maju cepat dan selalu membuat cabang gabungan. Cara mengerikan untuk melestarikan sejarah cabang adalah dengan tag dan / atau cabang (tag sangat direkomendasikan) menurut beberapa konvensi yang Anda pilih. Saya benar-benar tidak akan merekomendasikan komit kosong boneka di cabang tempat Anda bergabung. Konvensi yang sangat umum adalah untuk tidak bergabung ke cabang integrasi sampai Anda ingin benar-benar menutup cabang Anda. Ini adalah praktik yang harus dipatuhi oleh orang-orang karena Anda sedang berusaha untuk memiliki cabang. Namun di dunia nyata ideal tidak selalu berarti praktis melakukan hal yang benar tidak layak untuk setiap situasi. Jika apa yang Anda