Saya ingin tahu algoritma yang tepat (atau yang mendekati itu) di balik 'git merge'. Jawaban setidaknya untuk sub-pertanyaan ini akan membantu:
- Bagaimana git mendeteksi konteks perubahan non-konflik tertentu?
- Bagaimana git mengetahui bahwa ada konflik di baris yang sama persis?
- Hal apa yang dilakukan git auto-merge?
- Bagaimana kinerja git jika tidak ada basis umum untuk menggabungkan cabang?
- Bagaimana kinerja git ketika ada beberapa basis umum untuk menggabungkan cabang?
- Apa yang terjadi jika saya menggabungkan beberapa cabang sekaligus?
- Apa perbedaan antara strategi penggabungan?
Tetapi deskripsi dari keseluruhan algoritma akan jauh lebih baik.
Jawaban:
Anda mungkin lebih baik mencari deskripsi tentang algoritma penggabungan 3 arah. Deskripsi tingkat tinggi akan menjadi seperti ini:
B
- versi file yang merupakan leluhur dari kedua versi baru (X
danY
), dan biasanya basis yang paling baru (meskipun ada kasus di mana itu harus kembali lebih jauh, yang merupakan salah satu dari fitur gabungangit
defaultrecursive
)X
denganB
danY
denganB
.Algoritme lengkap menangani hal ini dengan lebih detail, dan bahkan memiliki beberapa dokumentasi ( https://github.com/git/git/blob/master/Documentation/technical/trivial-merge.txt untuk satu, bersama dengan
git help XXX
halaman , di mana XXX adalah salah satu darimerge-base
,merge-file
,merge
,merge-one-file
dan mungkin beberapa orang lain). Jika itu tidak cukup dalam, selalu ada kode sumber ...sumber
Bagaimana kinerja git ketika ada beberapa basis umum untuk menggabungkan cabang?
Artikel ini sangat membantu: http://codicesoftware.blogspot.com/2011/09/merge-recursive-strategy.html (ini adalah bagian 2 ).
Rekursif menggunakan diff3 secara rekursif untuk menghasilkan cabang virtual yang akan digunakan sebagai leluhur.
Misalnya:
Kemudian:
Ada 2 leluhur bersama terbaik (leluhur bersama yang bukan leluhur satu sama lain),
C
danD
. Git menggabungkannya menjadi cabang virtual baruV
, dan kemudian digunakanV
sebagai basis.Saya kira Git hanya akan melanjutkan jika ada lebih banyak nenek moyang terbaik yang sama, bergabung
V
dengan yang berikutnya.Artikel tersebut mengatakan bahwa jika ada konflik penggabungan saat membuat cabang virtual, Git hanya meninggalkan penanda konflik di mana mereka berada dan berlanjut.
Apa yang terjadi jika saya menggabungkan beberapa cabang sekaligus?
Seperti yang dijelaskan @Nevik Rehnel, itu tergantung pada strateginya, dijelaskan dengan baik di
man git-merge
MERGE STRATEGIES
bagian.Hanya
octopus
danours
/theirs
mendukung penggabungan beberapa cabang sekaligus,recursive
misalnya tidak.octopus
menolak bergabung jika akan ada konflik, danours
merupakan penggabungan yang sepele sehingga tidak ada konflik.Perintah tersebut menghasilkan komit baru yang akan memiliki lebih dari 2 orang tua.
Saya melakukannya
merge -X octopus
di Git 1.8.5 tanpa konflik untuk melihat bagaimana kelanjutannya.Keadaan awal:
Tindakan:
Negara bagian baru:
Seperti yang diharapkan,
E
memiliki 3 orang tua.TODO: bagaimana sebenarnya gurita beroperasi pada modifikasi file tunggal. Penggabungan dua-oleh-dua 3-arah rekursif?
Bagaimana kinerja git jika tidak ada basis umum untuk menggabungkan cabang?
@Torek menyebutkan bahwa sejak 2.9, penggabungan gagal tanpa
--allow-unrelated-histories
.Saya mencobanya secara empiris di Git 1.8.5:
a
mengandung:Kemudian:
a
mengandung:Penafsiran:
a\nc\n
sebagai penambahan satu barissumber
e379fdf34fee96cd205be83ff4e71699bdc32b18
), Git sekarang menolak untuk menggabungkan jika tidak ada basis penggabungan kecuali Anda menambahkan--allow-unrelated-histories
.--allow-unrelated-histories
dapat dihilangkan jika tidak ada jalur file umum antara cabang yang Anda gabungkan.ours
strategi penggabungan, tetapi tidak adatheirs
strategi penggabungan.recursive
+theirs
strategi hanya dapat menyelesaikan dua cabang. git-scm.com/docs/git-merge#_merge_strategiesSaya tertarik juga. Saya tidak tahu jawabannya, tapi ...
Saya pikir penggabungan git sangat canggih dan akan sangat sulit untuk dipahami - tetapi salah satu cara untuk mendekatinya adalah dari pendahulunya, dan dengan fokus pada inti perhatian Anda. Yaitu, mengingat dua file yang tidak memiliki leluhur yang sama, bagaimana git menggabungkan cara menggabungkannya, dan di mana konflik terjadi?
Mari kita coba mencari beberapa prekursor. Dari
git help merge-file
:Dari wikipedia: http://en.wikipedia.org/wiki/Git_%28software%29 -> http://en.wikipedia.org/wiki/Three-way_merge#Three-way_merge -> http: //en.wikipedia .org / wiki / Diff3 -> http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf
Tautan terakhir adalah pdf dari makalah yang menjelaskan
diff3
algoritme secara rinci. Ini adalah versi google pdf-viewer . Panjangnya hanya 12 halaman, dan algoritmanya hanya beberapa halaman - tetapi perawatan matematis lengkap. Itu mungkin tampak agak terlalu formal, tetapi jika Anda ingin memahami penggabungan git, Anda harus memahami versi yang lebih sederhana terlebih dahulu. Saya belum memeriksanya, tetapi dengan nama sepertidiff3
, Anda mungkin juga perlu memahami diff (yang menggunakan algoritma urutan umum terpanjang ). Namun, mungkin ada penjelasan yang lebih intuitif didiff3
luar sana, jika Anda memiliki google ...Sekarang, saya baru saja melakukan percobaan membandingkan
diff3
dangit merge-file
. Mereka mengambil tiga file yang sama masukan version1 OldVersion version2 dan konflik tanda jalan yang sama, dengan<<<<<<< version1
,=======
,>>>>>>> version2
(diff3
juga memiliki||||||| oldversion
), menunjukkan warisan bersama mereka.Saya menggunakan file kosong untuk versi lama , dan file yang hampir identik untuk versi1 dan versi2 hanya dengan satu baris tambahan ditambahkan ke versi2 .
Hasil:
git merge-file
mengidentifikasi satu baris yang diubah sebagai konflik; tetapidiff3
memperlakukan kedua file secara keseluruhan sebagai konflik. Jadi, secanggih diff3, penggabungan git bahkan lebih canggih, bahkan untuk kasus yang paling sederhana ini.Inilah hasil sebenarnya (saya menggunakan jawaban @ twalberg untuk teksnya). Perhatikan opsi yang diperlukan (lihat halaman manual masing-masing).
$ git merge-file -p fun1.txt fun0.txt fun2.txt
$ diff3 -m fun1.txt fun0.txt fun2.txt
Jika Anda benar-benar tertarik dengan ini, ini sedikit lubang kelinci. Bagi saya, ini tampaknya sedalam ekspresi reguler, algoritme urutan umum terpanjang dari diff, tata bahasa bebas konteks, atau aljabar relasional. Jika Anda ingin membacanya, saya rasa Anda bisa, tetapi itu akan membutuhkan studi yang pasti.
sumber
Inilah implementasi aslinya
http://git.kaarsemaker.net/git/blob/857f26d2f41e16170e48076758d974820af685ff/git-merge-recursive.py
Pada dasarnya Anda membuat daftar nenek moyang yang sama untuk dua komit dan kemudian menggabungkannya secara rekursif, baik meneruskan cepat, atau membuat komit virtual yang digunakan untuk dasar penggabungan tiga cara pada file.
sumber
Jika baris yang sama telah berubah di kedua sisi penggabungan, itu konflik; jika belum, perubahan dari satu sisi (jika ada) diterima.
Perubahan yang tidak bertentangan (lihat di atas)
Menurut definisi basis gabungan Git , hanya ada satu (leluhur bersama terbaru).
Itu tergantung pada strategi penggabungan (hanya yang
octopus
danours
/theirs
strateginya mendukung penggabungan lebih dari dua cabang).Ini dijelaskan di halaman
git merge
manual .sumber
git-merge-recursive
ada?git-merge-recursive
seharusnya (tidak ada halaman manual dan google tidak menghasilkan apa-apa). Info lebih lanjut tentang ini dapat ditemukan digit merge
dangit merge-base
halaman manual.git-merge
man page dangit-merge-base
halaman manual yang menunjukkan mendiskusikan beberapa nenek moyang yang sama dan penggabungan rekursif. Saya merasa jawaban Anda tidak lengkap tanpa diskusi semacam itu.