Cara mengatasi penggabungan konflik di Git

4769

Bagaimana cara menyelesaikan konflik gabungan di Git?

Spoike
sumber
31
Posting blog berikut ini tampaknya memberikan contoh yang sangat baik tentang cara menangani konflik gabungan dengan Git yang seharusnya membuat Anda bergerak ke arah yang benar. Menangani dan Menghindari Konflik di Git
mwilliams
4
Anda dapat mengkonfigurasi alat gabungan (kdiff3 jebaird.com/2013/07/08/... ) dan kemudian menggunakan git mergetool. Saat Anda bekerja di tim pengembang besar, Anda akan selalu menghadapi konflik gabungan.
Grady G Cooper
Jangan lupa bahwa Anda dapat mengurangi sebagian besar konflik menggabungkan dengan secara teratur menggabungkan hilir!
Semut P
8
Pertanyaan yang menarik: Ditanyakan pada tahun 2008, pembukaan 100% berakhir tanpa petunjuk sama sekali tentang apa itu sebenarnya (apakah ini tentang GUI? Tentang perintah git? Tentang semantik? Tentang mendorong / menarik atau hanya konflik umum?) - benar-benar tidak dapat dijawab. 30 jawaban, semuanya (sejauh yang diperlihatkan sekilas) kurang lebih terjadi tentang berbagai alat diff3 dan gabungan, tidak ada yang diterima. Jawaban terpilih menyebutkan sebuah perintah yang bahkan tidak bekerja di luar kotak dengan gitinstalasi default . Berhasil mencapai halaman mulai SE untuk saya hari ini, 2017, dengan pandangan 1,3 juta dan ribuan suara. Menarik.
AnoE

Jawaban:

2913

Mencoba: git mergetool

Ini membuka GUI yang langkah Anda melalui setiap konflik, dan Anda bisa memilih cara untuk menggabungkan. Kadang-kadang membutuhkan sedikit pengeditan tangan setelahnya, tetapi biasanya cukup dengan sendirinya. Ini jauh lebih baik daripada melakukan semuanya dengan tangan.

Sesuai komentar @JoshGlover:

Perintah

tidak harus membuka GUI kecuali Anda menginstalnya. Menjalankan git mergetooluntuk saya menghasilkan vimdiffdigunakan. Anda dapat menginstal salah satu alat berikut untuk menggunakannya sebagai gantinya: meld, opendiff, kdiff3, tkdiff, xxdiff, tortoisemerge, gvimdiff, diffuse, ecmerge, p4merge, araxis, vimdiff, emerge.

Di bawah ini adalah contoh prosedur yang digunakan vimdiffuntuk menyelesaikan konflik gabungan. Berdasarkan tautan ini

Langkah 1 : Jalankan perintah berikut di terminal Anda

git config merge.tool vimdiff
git config merge.conflictstyle diff3
git config mergetool.prompt false

Ini akan menetapkan vimdiff sebagai alat penggabungan default.

Langkah 2 : Jalankan perintah berikut di terminal

git mergetool

Langkah 3 : Anda akan melihat tampilan vimdiff dalam format berikut

  ╔═══════╦══════╦════════╗
  ║       ║      ║        ║
  ║ LOCAL ║ BASE ║ REMOTE ║
  ║       ║      ║        ║
  ╠═══════╩══════╩════════╣
  ║                       ║
  ║        MERGED         ║
  ║                       ║
  ╚═══════════════════════╝

4 pandangan ini

LOCAL - ini adalah file dari cabang saat ini

DASAR - nenek moyang yang sama, bagaimana file terlihat sebelum kedua perubahan

REMOTE - file yang Anda gabungkan ke cabang Anda

MERGED - hasil merger, inilah yang akan disimpan dalam repo

Anda dapat menavigasi di antara tampilan ini menggunakan ctrl+ w. Anda dapat langsung mencapai tampilan MERGED menggunakan ctrl+ wdiikuti oleh j.

Info lebih lanjut tentang navigasi vimdiff di sini dan di sini

Langkah 4 . Anda dapat mengedit tampilan MERGED dengan cara berikut

Jika Anda ingin mendapatkan perubahan dari REMOTE

:diffg RE  

Jika Anda ingin mendapatkan perubahan dari BASE

:diffg BA  

Jika Anda ingin mendapat perubahan dari LOCAL

:diffg LO 

Langkah 5 . Simpan, Keluar, Berkomitmen dan Bersihkan

:wqa simpan dan keluar dari vi

git commit -m "message"

git clean Hapus file tambahan (misalnya * .orig) yang dibuat oleh alat diff.

Peter Burns
sumber
54
FYI dapat Anda gunakan git mergetool -yuntuk menyimpan beberapa penekanan tombol jika Anda menggabungkan banyak file sekaligus.
davr
373
Yah, itu tidak selalu membuka GUI kecuali Anda menginstalnya. Menjalankan git mergetooluntuk saya menghasilkan vimdiffdigunakan. Anda dapat menginstal salah satu alat berikut untuk menggunakannya sebagai gantinya: meld opendiff kdiff3 tkdiff xxdiff tortoisemerge gvimdiff diffuse ecmerge p4merge araxis vimdiff emerge.
Josh Glover
31
Poin bagus, Josh. Di ubuntu saya sudah beruntung dengan meld, tampilan penggabungan tiga arahnya tidak buruk. Pada OSX git memilih default yang bagus.
Peter Burns
18
Ini membuka KDiff3. Yang saya sama sekali tidak tahu cara menggunakannya.
David Murdoch
7
Anda juga dapat menggunakan Beyond Compare 3 sekarang ( git mergetool -t bc3).
AzP
1703

Inilah kemungkinan kasus penggunaan, dari atas:

Anda akan melakukan beberapa perubahan, tetapi oops, Anda tidak up to date:

git fetch origin
git pull origin master

From ssh://[email protected]:22/projectname
 * branch            master     -> FETCH_HEAD
Updating a030c3a..ee25213
error: Entry 'filename.c' not uptodate. Cannot merge.

Jadi, Anda mendapatkan informasi terbaru dan coba lagi, tetapi mengalami konflik:

git add filename.c
git commit -m "made some wild and crazy changes"
git pull origin master

From ssh://[email protected]:22/projectname
 * branch            master     -> FETCH_HEAD
Auto-merging filename.c
CONFLICT (content): Merge conflict in filename.c
Automatic merge failed; fix conflicts and then commit the result.

Jadi Anda memutuskan untuk melihat perubahannya:

git mergetool

Oh my, oh my, hulu mengubah beberapa hal, tetapi hanya menggunakan perubahan saya ... tidak ... perubahan mereka ...

git checkout --ours filename.c
git checkout --theirs filename.c
git add filename.c
git commit -m "using theirs"

Dan kemudian kami mencoba yang terakhir kalinya

git pull origin master

From ssh://[email protected]:22/projectname
 * branch            master     -> FETCH_HEAD
Already up-to-date.

Ta-da!

CoolAJ86
sumber
19
Ini sangat membantu karena saya memiliki banyak kesalahan penggabungan dengan file biner (aset seni) dan menggabungkannya sepertinya selalu gagal, jadi saya harus menimpanya dengan file baru selalu dan tidak "menggabungkan"
petrocket
188
Cermat! Arti --our dan --theirs dibalik. --our == remote. --theirs == lokal. Lihatgit merge --help
mmell
57
Dalam kasus saya, saya mengkonfirmasi bahwa --theirs = repositori jarak jauh, --ours = repositori lokal saya sendiri. Ini kebalikan dari komentar @mmell.
Aryo
24
@mmell Hanya dengan rebase, rupanya. Lihatthis question
Navin
184
Kawan-kawan, "milik kita" dan "milik mereka" relatif terhadap apakah Anda bergabung atau tidak. Jika Anda menggabungkan , maka "milik kami" berarti cabang yang Anda gabungkan, dan "milik mereka" adalah cabang tempat Anda bergabung. Ketika Anda melakukan rebasing , maka "milik kami" berarti komit yang Anda gunakan untuk rebound. , sedangkan "milik mereka" mengacu pada komitmen yang ingin Anda rebase.
736

Saya menemukan alat penggabungan jarang membantu saya memahami konflik atau resolusi. Saya biasanya lebih berhasil melihat penanda konflik dalam editor teks dan menggunakan git log sebagai pelengkap.

Berikut ini beberapa tips:

Kiat Pertama

Hal terbaik yang saya temukan adalah menggunakan gaya konflik gabungan "diff3":

git config merge.conflictstyle diff3

Ini menghasilkan penanda konflik seperti ini:

<<<<<<<
Changes made on the branch that is being merged into. In most cases,
this is the branch that I have currently checked out (i.e. HEAD).
|||||||
The common ancestor version.
=======
Changes made on the branch that is being merged in. This is often a 
feature/topic branch.
>>>>>>>

Bagian tengah adalah seperti apa leluhur yang sama itu terlihat. Ini berguna karena Anda dapat membandingkannya dengan versi atas dan bawah untuk mendapatkan pemahaman yang lebih baik tentang apa yang diubah pada setiap cabang, yang memberi Anda ide yang lebih baik tentang tujuan dari setiap perubahan itu.

Jika konflik hanya beberapa baris, ini umumnya membuat konflik menjadi sangat jelas. (Mengetahui cara memperbaiki konflik sangat berbeda; Anda perlu mengetahui apa yang sedang dikerjakan orang lain. Jika Anda bingung, mungkin lebih baik memanggil orang itu ke kamar Anda sehingga mereka dapat melihat apa yang Anda cari. di.)

Jika konflik lebih panjang, maka saya akan memotong dan menempelkan masing-masing dari tiga bagian menjadi tiga file terpisah, seperti "milikku", "umum" dan "milik mereka".

Kemudian saya dapat menjalankan perintah berikut untuk melihat dua bakhil berbeda yang menyebabkan konflik:

diff common mine
diff common theirs

Ini tidak sama dengan menggunakan alat gabungan, karena alat gabungan akan mencakup semua bakhil diff yang tidak saling bertentangan juga. Saya menemukan itu mengganggu.

Kiat Dua

Seseorang sudah menyebutkan ini, tetapi memahami maksud di balik setiap perbedaan biasanya sangat membantu untuk memahami dari mana konflik berasal dan bagaimana cara mengatasinya.

git log --merge -p <name of file>

Ini menunjukkan semua komit yang menyentuh file di antara leluhur yang sama dan dua kepala yang Anda gabungkan. (Jadi itu tidak termasuk komit yang sudah ada di kedua cabang sebelum penggabungan.) Ini membantu Anda mengabaikan bingkah yang berbeda yang jelas bukan merupakan faktor dalam konflik Anda saat ini.

Tip Tiga

Verifikasi perubahan Anda dengan alat otomatis.

Jika Anda memiliki tes otomatis, jalankan itu. Jika Anda memiliki serat , jalankan itu. Jika ini adalah proyek yang dapat dibangun, kemudian bangun sebelum Anda berkomitmen, dll. Dalam semua kasus, Anda perlu melakukan sedikit pengujian untuk memastikan perubahan Anda tidak merusak apa pun. (Heck, bahkan gabungan tanpa konflik dapat merusak kode kerja.)

Kiat Empat

Rencanakan ke depan; berkomunikasi dengan rekan kerja.

Merencanakan ke depan dan mengetahui apa yang sedang dikerjakan orang lain dapat membantu mencegah penggabungan konflik dan / atau membantu menyelesaikannya lebih awal - sementara perinciannya masih segar dalam ingatan.

Misalnya, jika Anda tahu bahwa Anda dan orang lain sama-sama mengerjakan refactoring yang berbeda yang akan memengaruhi set file yang sama, Anda harus berbicara satu sama lain sebelumnya dan mendapatkan pemahaman yang lebih baik tentang tipe perubahan apa yang Anda masing-masing lakukan. membuat. Anda mungkin menghemat banyak waktu dan usaha jika Anda melakukan perubahan yang direncanakan secara serial daripada secara paralel.

Untuk refactoring utama yang memotong sejumlah besar kode, Anda harus sangat mempertimbangkan bekerja secara seri: semua orang berhenti mengerjakan area kode itu sementara satu orang melakukan refactoring lengkap.

Jika Anda tidak dapat bekerja secara serial (karena tekanan waktu, mungkin), maka berkomunikasi tentang konflik gabungan yang diharapkan setidaknya membantu Anda menyelesaikan masalah lebih cepat sementara detailnya masih segar dalam ingatan. Sebagai contoh, jika seorang rekan kerja membuat serangkaian tindakan yang mengganggu selama periode satu minggu, Anda dapat memilih untuk bergabung / rebase di cabang rekan kerja itu satu atau dua kali sehari setiap hari selama minggu itu. Dengan begitu, jika Anda menemukan konflik merger / rebase, Anda dapat menyelesaikannya lebih cepat daripada jika Anda menunggu beberapa minggu untuk menggabungkan semuanya bersama dalam satu benjolan besar.

Kiat Lima

Jika Anda tidak yakin tentang penggabungan, jangan paksa itu.

Penggabungan dapat terasa luar biasa, terutama ketika ada banyak file yang saling bertentangan dan penanda konflik mencakup ratusan baris. Sering kali ketika memperkirakan proyek perangkat lunak kami tidak memasukkan cukup waktu untuk item overhead seperti menangani penggabungan degil, jadi terasa seperti hambatan nyata untuk menghabiskan beberapa jam membedah setiap konflik.

Dalam jangka panjang, perencanaan ke depan dan menyadari apa yang sedang dikerjakan orang lain adalah alat terbaik untuk mengantisipasi konflik gabungan dan mempersiapkan diri Anda untuk menyelesaikannya dengan benar dalam waktu yang lebih singkat.

Mark E. Haase
sumber
6
Opsi diff3 adalah fitur hebat untuk memiliki gabungan. Satu-satunya GUI yang saya temui menunjukkan itu milik Perforce p4merge, yang dapat diinstal dan digunakan secara terpisah dari alat Perforce lainnya (yang saya tidak pernah gunakan, tetapi mendengar keluhan tentang).
alxndr
3
Setelah upaya rebase yang menghasilkan konflik gabungan: $ git log --merge -p build.xml output: fatal: --merge tanpa MERGE_HEAD?
Ed Randall
bagaimana jika saya memiliki perubahan pada satu file dari branch1 dan penghapusan file itu di branch2. Bagaimana saya bisa menyelesaikan konflik gabungan itu? Apakah ada cara menggunakan git di mana saya bisa menggabungkan mereka dengan menyimpan perubahan satu cabang?
Sayang
git config merge.conflictstyle diff3- Terima kasih Pak. Ini luar biasa dan telah membebaskan saya dari upaya untuk menemukan (dan membayar $$) untuk GUI penggabungan 3 cara yang baik. IMO ini lebih baik karena menunjukkan leluhur bersama serta lokal / jauh, dan menunjukkan garis-garis log komit terakhir yang (AFAIK) tidak GUI lakukan. Komit pasti membantu Anda mengidentifikasi kode apa yang dimiliki cabang apa.
ffxsam
Saya telah menemukan bahwa kadang-kadang gaya konflik diff3 menghasilkan bakhil diff besar yang sebagian besar identik, sedangkan default akan menghasilkan bakhil yang lebih kecil, lebih mudah dikelola. Sayangnya, saya tidak memiliki alat reproduksi yang dapat saya gunakan untuk laporan bug. Tetapi jika Anda mengalami masalah ini, Anda mungkin mempertimbangkan mematikan opsi sementara.
Dave Abrahams
348
  1. Identifikasi file mana yang dalam konflik (Git harus memberi tahu Anda ini).

  2. Buka setiap file dan periksa perbedaannya; Git membatasi mereka. Semoga akan jelas versi mana dari setiap blok untuk disimpan. Anda mungkin perlu mendiskusikannya dengan sesama pengembang yang melakukan kode.

  3. Setelah Anda menyelesaikan konflik dalam file git add the_file.

  4. Setelah Anda menyelesaikan semua konflik, lakukan git rebase --continueatau perintah apa pun yang dikatakan Git lakukan ketika Anda selesai.

davetron5000
sumber
38
@Justin Anggap Git sebagai pelacakan konten daripada melacak file. Maka mudah untuk melihat bahwa konten yang Anda perbarui tidak ada dalam repositori dan perlu ditambahkan. Cara berpikir ini juga menjelaskan mengapa Git tidak melacak folder kosong: Meskipun secara teknis file, tidak ada konten untuk dilacak.
Gareth
7
konten ada di sana, konflik terjadi karena ada 2 versi konten. Karena itu "git add" tidak terdengar benar. Dan itu tidak berfungsi (git add, git commit) jika Anda hanya ingin mengkomit satu file setelah konflik diselesaikan ("fatal: tidak dapat melakukan commit parsial selama penggabungan.")
Dainius
1
Ya, secara teknis, ini menjawab pertanyaan yang ditanyakan, tetapi bukan jawaban yang bisa digunakan, menurut saya, maaf. Apa gunanya membuat satu cabang sama dengan cabang lainnya? Tentu saja gabungan akan memiliki konflik ..
Thufir
5
Thulfir: siapa yang mengatakan sesuatu tentang membuat satu cabang sama dengan cabang lainnya? Ada skenario berbeda di mana Anda perlu menggabungkan, tanpa "membuat satu cabang sama dengan yang lain". Pertama adalah ketika Anda selesai dengan cabang pengembangan dan ingin memasukkan perubahannya ke cabang utama; setelah ini, cabang pengembangan dapat dihapus. Yang lain adalah ketika Anda ingin rebase cabang pengembangan Anda, untuk memudahkan penggabungan akhir akhirnya menjadi master.
Teemu Leisti
4
@JustinGrant membuat git addfile dalam indeks; itu tidak menambahkan apa pun ke repositori. git commitmenambahkan hal-hal ke repositori. Penggunaan ini masuk akal untuk penggabungan - penggabungan secara otomatis menggelar semua perubahan yang dapat digabungkan secara otomatis; Anda bertanggung jawab untuk menggabungkan sisa perubahan dan menambahkannya ke indeks saat Anda selesai.
Mark E. Haase
105

Lihatlah jawaban di pertanyaan Stack Overflow. Batalkan penggabungan dalam Git , terutama jawaban Charles Bailey yang menunjukkan cara melihat versi berbeda dari file yang bermasalah, misalnya,

# Common base version of the file.
git show :1:some_file.cpp

# 'Ours' version of the file.
git show :2:some_file.cpp

# 'Theirs' version of the file.
git show :3:some_file.cpp
Pat Notz
sumber
Periksa juga opsi "-m" untuk "git checkout -m" - ini memungkinkan Anda untuk mengekstrak lalat yang berbeda kembali ke ruang kerja Anda
qneill
Ini menyelamatkan saya. Melihat setiap file secara terpisah memungkinkan saya untuk mengingat apa yang saya lakukan di setiap cabang. Lalu saya bisa membuat keputusan untuk memilih.
Rohmer
99

Gabungkan konflik terjadi ketika perubahan dilakukan pada file pada saat yang bersamaan. Inilah cara mengatasinya.

git CLI

Berikut adalah langkah-langkah sederhana apa yang harus dilakukan ketika Anda masuk ke dalam keadaan konflik:

  1. Perhatikan daftar file yang konflik dengan: git status(di bawah Unmerged pathsbagian).
  2. Selesaikan konflik secara terpisah untuk setiap file dengan salah satu pendekatan berikut:

    • Gunakan GUI untuk menyelesaikan konflik: git mergetool(cara termudah).

    • Untuk menerima versi remote / lainnya, gunakan: git checkout --theirs path/file. Ini akan menolak setiap perubahan lokal yang Anda lakukan untuk file itu.

    • Untuk menerima versi lokal / kami, gunakan: git checkout --ours path/file

      Namun Anda harus berhati-hati, karena perubahan jarak jauh yang dilakukan konflik karena beberapa alasan.

      Terkait: Apa arti tepat dari "milik kita" dan "milik mereka" dalam git?

    • Edit file yang konflik secara manual dan cari blok kode antara <<<<</ >>>>>lalu pilih versi dari atas atau bawah =====. Lihat: Bagaimana konflik disajikan .

    • Konflik jalur dan nama file dapat diselesaikan dengan git add/ git rm.

  3. Akhirnya, meninjau file siap berkomitmen menggunakan: git status.

    Jika Anda masih memiliki file di bawah Unmerged paths, dan kau menyelesaikan konflik secara manual, kemudian membiarkan Git tahu bahwa Anda dipecahkan dengan: git add path/file.

  4. Jika semua konflik berhasil diselesaikan, lakukan perubahan dengan: git commit -adan dorong ke remote seperti biasa.

Lihat juga: Mengatasi konflik gabungan dari baris perintah di GitHub

Untuk tutorial praktis, periksa: Skenario 5 - Memperbaiki Konflik Penggabungan oleh Katacoda .

DiffMerge

Saya telah berhasil menggunakan DiffMerge yang secara visual dapat membandingkan dan menggabungkan file pada Windows, macOS dan Linux / Unix.

Secara grafis dapat menunjukkan perubahan antara 3 file dan memungkinkan penggabungan otomatis (saat aman untuk melakukannya) dan kontrol penuh atas pengeditan file yang dihasilkan.

DiffMerge

Sumber gambar: DiffMerge (tangkapan layar Linux)

Cukup unduh dan jalankan dalam repo sebagai:

git mergetool -t diffmerge .

macOS

Di macOS Anda dapat menginstal melalui:

brew install caskroom/cask/brew-cask
brew cask install diffmerge

Dan mungkin (jika tidak disediakan) Anda perlu pembungkus ekstra sederhana berikut ditempatkan di PATH Anda (misalnya /usr/bin):

#!/bin/sh
DIFFMERGE_PATH=/Applications/DiffMerge.app
DIFFMERGE_EXE=${DIFFMERGE_PATH}/Contents/MacOS/DiffMerge
exec ${DIFFMERGE_EXE} --nosplash "$@"

Kemudian Anda dapat menggunakan pintasan keyboard berikut:

  • - Alt- Up/ Downuntuk melompat ke perubahan sebelumnya / selanjutnya.
  • - Alt- Left/ Rightuntuk menerima perubahan dari kiri atau kanan

Atau Anda dapat menggunakan opendiff (bagian dari Xcode Tools) yang memungkinkan Anda menggabungkan dua file atau direktori bersamaan untuk membuat file atau direktori ketiga.

kenorb
sumber
79

Jika Anda sering melakukan komit kecil, maka mulailah dengan melihat komit dengan git log --merge. Kemudian git diffakan menunjukkan kepada Anda konflik.

Untuk konflik yang melibatkan lebih dari beberapa baris, lebih mudah untuk melihat apa yang terjadi pada alat GUI eksternal. Saya suka opendiff - Git juga mendukung vimdiff, gvimdiff, kdiff3, tkdiff, meld, xxdiff, muncul di luar kotak dan Anda dapat menginstal yang lain: git config merge.tool "your.tool"akan mengatur alat yang Anda pilih dan kemudian git mergetoolsetelah penggabungan yang gagal akan menunjukkan perbedaan dalam konteks.

Setiap kali Anda mengedit file untuk menyelesaikan konflik, git add filenameakan memperbarui indeks dan diff Anda tidak akan lagi menampilkannya. Ketika semua konflik ditangani dan file mereka telah git add-ed, git commitakan melengkapi penggabungan Anda.

Paul
sumber
8
Menggunakan "git add" adalah trik sebenarnya di sini. Anda bahkan mungkin tidak ingin mengkomit (mungkin Anda ingin menyembunyikan), tetapi Anda harus melakukan "git add" untuk menyelesaikan penggabungan. Saya pikir mergetool melakukan add untuk Anda (meskipun tidak ada di halaman manual), tetapi jika Anda melakukan penggabungan secara manual, Anda perlu menggunakan "git add" untuk melengkapinya (bahkan jika Anda tidak ingin komit).
nobar
47

Lihat Bagaimana Konflik Disajikan atau, dalam Git, git mergedokumentasi untuk memahami apa yang menggabungkan penanda konflik.

Juga, bagian Bagaimana Mengatasi Konflik menjelaskan bagaimana menyelesaikan konflik:

Setelah melihat konflik, Anda dapat melakukan dua hal:

  • Putuskan untuk tidak bergabung. Satu-satunya pembersihan yang Anda butuhkan adalah mereset file indeks ke HEADkomit untuk membalik 2. dan untuk membersihkan perubahan susunan yang dibuat oleh 2. dan 3 .; git merge --abortdapat digunakan untuk ini.

  • Selesaikan konflik. Git akan menandai konflik di pohon kerja. Edit file ke dalam bentuk dan git addke indeks. Gunakan git commituntuk menyegel kesepakatan.

Anda dapat mengatasi konflik dengan sejumlah alat:

  • Gunakan mergetool. git mergetooluntuk meluncurkan mergetool grafis yang akan membantu Anda melalui penggabungan.

  • Lihatlah perbedaannya. git diffakan menampilkan diff tiga arah, menyoroti perubahan dari HEADdan MERGE_HEADversi.

  • Lihatlah perbedaan dari setiap cabang. git log --merge -p <path>akan menampilkan diffs pertama untuk HEADversi dan kemudian MERGE_HEADversi.

  • Lihatlah yang asli. git show :1:filenamemenunjukkan leluhur yang sama, git show :2:filenamemenunjukkan HEADversi, dan git show :3:filenamemenunjukkan MERGE_HEADversi.

Anda juga dapat membaca tentang penanda konflik gabungan dan cara mengatasinya di bagian buku Pro Git Konflik Penggabungan Dasar .

Peter Mortensen
sumber
42

Saya ingin versi saya atau versi mereka sepenuhnya, atau ingin meninjau perubahan individu dan memutuskan untuk masing-masing.

Sepenuhnya menerima versi saya atau mereka :

Terima versi saya (lokal, milik kami):

git checkout --ours -- <filename>
git add <filename>              # Marks conflict as resolved
git commit -m "merged bla bla"  # An "empty" commit

Terima versi mereka (jarak jauh, milik mereka):

git checkout --theirs -- <filename>
git add <filename>
git commit -m "merged bla bla"

Jika Anda ingin melakukannya untuk semua file konflik jalankan:

git merge --strategy-option ours

atau

git merge --strategy-option theirs

Tinjau semua perubahan dan terima satu per satu

  1. git mergetool
  2. Tinjau perubahan dan terima salah satu versi untuk masing-masingnya.
  3. git add <filename>
  4. git commit -m "merged bla bla"

Default mergetoolberfungsi di baris perintah . Cara menggunakan mergetool baris perintah harus menjadi pertanyaan terpisah.

Anda juga dapat menginstal alat visual untuk ini, misalnya melddan jalankan

git mergetool -t meld

Ini akan membuka versi lokal (milik kita), versi "basis" atau "digabung" (hasil penggabungan saat ini) dan versi jarak jauh (milik mereka). Simpan versi gabungan ketika Anda selesai, jalankan git mergetool -t meldlagi sampai Anda mendapatkan "Tidak ada file yang perlu digabung", kemudian pergi ke Langkah 3. dan 4.

Tidak ada ide
sumber
Perintah ini: git checkout --theirs - <filename> mengubah SEMUA file menjadi milik mereka, bukan hanya <filename>
Donato
Sebenarnya saya salah. Ini hanya memperbarui file yang ditentukan.
Donato
40

Untuk pengguna Emacs yang ingin menyelesaikan konflik penggabungan secara semi-manual:

git diff --name-status --diff-filter=U

memperlihatkan semua file yang memerlukan resolusi konflik.

Buka masing-masing file satu per satu, atau sekaligus dengan:

emacs $(git diff --name-only --diff-filter=U)

Saat mengunjungi buffer yang membutuhkan pengeditan di Emacs, ketik

ALT+x vc-resolve-conflicts

Ini akan membuka tiga buffer (milikku, milik mereka, dan buffer output). Navigasi dengan menekan 'n' (wilayah berikutnya), 'p' (wilayah previsi). Tekan 'a' dan 'b' untuk menyalin masing-masing wilayah tambang atau mereka ke buffer output. Dan / atau edit buffer output secara langsung.

Setelah selesai: Tekan 'q'. Emacs bertanya apakah Anda ingin menyimpan buffer ini: ya. Setelah menyelesaikan tanda buffer itu diselesaikan dengan menjalankan dari teriminal:

git add FILENAME

Setelah selesai dengan semua tipe buffer

git commit

untuk menyelesaikan penggabungan.

ECI
sumber
33

Bonus:

Dalam berbicara tentang tarik / ambil / gabungkan jawaban di atas, saya ingin berbagi trik yang menarik dan produktif,

git pull --rebase

Perintah di atas adalah perintah yang paling berguna dalam hidup git saya yang menghemat banyak waktu.

Sebelum mendorong perubahan yang baru Anda komit ke server jarak jauh, coba git pull --rebaseagak git pulldan manual mergedan itu akan secara otomatis menyinkronkan perubahan server jarak jauh terbaru (dengan mengambil + gabungan) dan akan menempatkan komit terbaru lokal Anda di bagian atas di git log. Tidak perlu khawatir tentang tarikan / penggabungan manual.

Jika terjadi konflik, gunakan saja

git mergetool
git add conflict_file
git rebase --continue

Temukan detailnya di: http://gitolite.com/git-pull--rebase

Sazzad Hissain Khan
sumber
32

Sederhananya, jika Anda tahu betul bahwa perubahan dalam salah satu repositori tidak penting, dan ingin menyelesaikan semua perubahan yang mendukung yang lain, gunakan:

git checkout . --ours

untuk menyelesaikan perubahan yang mendukung repositori Anda , atau

git checkout . --theirs

untuk menyelesaikan perubahan demi repositori lain atau utama .

Atau Anda harus menggunakan alat gabungan GUI untuk menelusuri file satu per satu, katakanlah alat gabungan p4merge, atau tulis nama siapa pun yang telah Anda instal

git mergetool -t p4merge

dan setelah menyelesaikan file, Anda harus menyimpan dan menutup, sehingga yang berikutnya akan terbuka.

Mohamed Selim
sumber
2
git checkout. --theirs menyelesaikan masalah saya terima kasih
Ramesh Chand
jika Anda lebih suka menyelesaikan konflik secara manual coba buka folder dalam Visual Studio Code, itu menandai file dengan konflik dan warna garis konflik di dalamnya
Mohamed Selim
31

Harap ikuti langkah-langkah berikut untuk memperbaiki gabungan konflik di Git:

  1. Periksa status Git: status git

  2. Dapatkan patchset: git fetch (checkout patch yang tepat dari komit Git Anda)

  3. Periksa cabang lokal (temp1 dalam contoh saya di sini): git checkout -b temp1

  4. Tarik konten terbaru dari master: git pull - master asal asal

  5. Mulai mergetool dan periksa konfliknya dan perbaiki ... dan periksa perubahan di cabang jarak jauh dengan cabang Anda saat ini: git mergetool

  6. Periksa lagi statusnya: status git

  7. Hapus file yang tidak diinginkan yang dibuat secara lokal oleh mergetool, biasanya mergetool membuat file tambahan dengan ekstensi * .orig. Silakan hapus file itu karena itu hanya duplikat dan perbaiki perubahan secara lokal dan tambahkan versi yang benar dari file Anda. git tambahkan #Anda_changed_correct_files

  8. Periksa lagi statusnya: status git

  9. Komit perubahan ke id komit yang sama (ini menghindari set patch terpisah baru): git commit --amend

  10. Dorong ke cabang master: git push (ke repositori Git Anda)

Rehabilitasi
sumber
28

Ada 3 langkah:

  1. Temukan file mana yang menyebabkan konflik dengan perintah

    git status
    
  2. Periksa file, di mana Anda akan menemukan konflik ditandai seperti

    <<<<<<<<head
    blablabla
    
  3. Ubah sesuai keinginan Anda, lalu komit dengan perintah

    git add solved_conflicts_files
    git commit -m 'merge msg'
    
Qijun Liu
sumber
Bekerja untukku! Terima kasih!
Nuwan Jayawardene
Anda harus memperhatikan jika melakukan ini selama rebase. Anda harus menggunakan git rebase --continue bukannya git commit
Samuel Dauzon
27

Anda bisa memperbaiki gabungan konflik dalam beberapa cara seperti yang telah dijelaskan oleh pihak lain.

Saya pikir kunci sebenarnya adalah mengetahui bagaimana perubahan mengalir dengan repositori lokal dan jarak jauh. Kunci untuk ini adalah memahami cabang pelacakan. Saya telah menemukan bahwa saya menganggap cabang pelacakan sebagai 'bagian yang hilang di tengah' antara saya, direktori file aktual saya dan remote yang didefinisikan sebagai asal.

Saya pribadi memiliki kebiasaan 2 hal untuk membantu menghindari hal ini.

Dari pada:

git add .
git commit -m"some msg"

Yang memiliki dua kelemahan -

a) Semua file baru / yang diubah ditambahkan dan itu mungkin termasuk beberapa perubahan yang tidak diinginkan.
b) Anda tidak bisa meninjau daftar file terlebih dahulu.

Jadi saya malah melakukannya:

git add file,file2,file3...
git commit # Then type the files in the editor and save-quit.

Dengan cara ini Anda lebih disengaja tentang file mana yang akan ditambahkan dan Anda juga bisa meninjau daftar dan berpikir lebih banyak saat menggunakan editor untuk pesan. Saya menemukan itu juga meningkatkan pesan komit saya ketika saya menggunakan editor layar penuh daripada -mopsi.

[Pembaruan - seiring waktu berlalu saya telah beralih lebih ke:

git status # Make sure I know whats going on
git add .
git commit # Then use the editor

]

Juga (dan lebih relevan dengan situasi Anda), saya mencoba untuk menghindari:

git pull

atau

git pull origin master.

karena pull menyiratkan penggabungan dan jika Anda memiliki perubahan secara lokal yang tidak ingin digabungkan, Anda dapat dengan mudah berakhir dengan kode gabungan dan / atau menggabungkan konflik untuk kode yang seharusnya tidak digabungkan.

Sebaliknya saya coba lakukan

git checkout master
git fetch   
git rebase --hard origin/master # or whatever branch I want.

Anda juga dapat menemukan ini bermanfaat:

git branch, fork, fetch, gabung, rebase dan clone, apa bedanya?

Michael Durrant
sumber
Hei, saya agak mengerti jawaban Anda. Tapi karena saya baru gitub menggabungkan konflik, saya pikir ada sesuatu yang hilang. Apa yang terjadi pada modifikasi lokal Anda ketika Anda melakukan git checkout masterdan git fetch dan git rebase --hard origin/master
Suhaib
Saya percaya Anda harus menambahkan rincian lebih lanjut tentang apa yang harus dilakukan. Contoh lain yang membingungkan saya, Anda sebutkan dalam jawaban Anda: ya git add ., apakah ini akan menyimpan modifikasi lokal kami sehingga kami dapat menindaklanjutinya git checkout master? atau mereka dua skenario yang berbeda?
Suhaib
@MichaelDurrant $ git rebase --hard origin/master b5a30cc159ba8dd error: unknown option hard 'use: git rebase [-i] [options] [--exec <cmd>] [--tanggal <newbase>] [[upstream>] [<branch>] atau: git rebase [-i] [ options] [--exec <cmd>] [--onto <newbase>] --root [<branch>] atau: git rebase --continue | --abort | --skip | --edit-todo `
likejudo
24

Jawaban CoolAJ86 meringkaskan semuanya. Jika Anda memiliki perubahan di kedua cabang dalam potongan kode yang sama Anda harus melakukan penggabungan manual. Buka file dalam konflik di editor teks apa pun dan Anda akan melihat struktur berikut.

(Code not in Conflict)
>>>>>>>>>>>
(first alternative for conflict starts here)
Multiple code lines here
===========
(second alternative for conflict starts here)
Multiple code lines here too    
<<<<<<<<<<<
(Code not in conflict here)

Pilih salah satu alternatif atau kombinasi keduanya dengan cara yang Anda inginkan kode baru, sambil menghapus tanda sama dan kurung sudut.

git commit -a -m "commit message"
git push origin master
iankit
sumber
17
git log --merge -p [[--] path]

Tampaknya tidak selalu bekerja untuk saya dan biasanya menampilkan setiap komit yang berbeda antara dua cabang, ini terjadi bahkan ketika menggunakan -- untuk memisahkan jalur dari perintah.

Apa yang saya lakukan untuk mengatasi masalah ini adalah membuka dua baris perintah dan dalam satu langkah

git log ..$MERGED_IN_BRANCH --pretty=full -p [path]

dan yang lainnya

git log $MERGED_IN_BRANCH.. --pretty=full -p [path]

Mengganti $MERGED_IN_BRANCHdengan cabang saya bergabung dan [path]dengan file yang bertentangan. Perintah ini akan mencatat semua komit, dalam bentuk tambalan, antara ( ..) dua komit. Jika Anda membiarkan satu sisi kosong seperti pada perintah di atas git akan secara otomatis menggunakan HEAD(cabang yang Anda gabungkan dalam hal ini).

Ini akan memungkinkan Anda untuk melihat komit apa yang masuk ke file di dua cabang setelah mereka menyimpang. Ini biasanya membuatnya lebih mudah untuk menyelesaikan konflik.

Brian Di Palma
sumber
16

Menggunakan patience

Saya terkejut tidak ada orang lain yang berbicara tentang menyelesaikan konflik menggunakan patiencedengan strategi rekursif gabungan. Untuk konflik penggabungan besar, menggunakan patiencememberikan hasil yang baik untuk saya. Idenya adalah bahwa ia akan mencoba untuk mencocokkan blok daripada garis individual.

Misalnya, jika Anda mengubah lekukan program Anda, strategi penggabungan Git standar terkadang cocok dengan kawat gigi tunggal {yang memiliki fungsi berbeda. Ini dihindari dengan patience:

git merge -s recursive -X patience other-branch

Dari dokumentasi:

With this option, merge-recursive spends a little extra time to avoid 
mismerges that sometimes occur due to unimportant matching lines 
(e.g., braces from distinct functions). Use this when the branches to 
be merged have diverged wildly.

Perbandingan dengan nenek moyang yang sama

Jika Anda memiliki konflik penggabungan dan ingin melihat apa yang ada dalam pikiran orang ketika memodifikasi cabang mereka, kadang-kadang lebih mudah untuk membandingkan cabang mereka secara langsung dengan leluhur yang sama (bukan cabang kami). Untuk itu Anda bisa menggunakan merge-base:

git diff $(git merge-base <our-branch> <their-branch>) <their-branch>

Biasanya, Anda hanya ingin melihat perubahan untuk file tertentu:

git diff $(git merge-base <our-branch> <their-branch>) <their-branch> <file>
Conchylicultor
sumber
Dalam kasus saya ini tidak menyelesaikan konflik penggabungan dengan baik, karena untuk beberapa alasan ia menyimpan duplikat garis konfigurasi dalam proyek C #. Meskipun itu lebih ramah daripada SELURUH FILE IS BERBEDA, yang saya miliki sebelumnya
Mathijs Segers
15

Mulai 12 Desember 2016, Anda dapat menggabungkan cabang dan menyelesaikan konflik di github.com

Jadi, jika Anda tidak ingin menggunakan baris perintah atau alat pihak ketiga mana pun yang ditawarkan di sini dari jawaban yang lebih lama , gunakan alat asli GitHub.

Posting blog ini menjelaskan secara rinci, tetapi dasarnya adalah bahwa setelah 'menggabungkan' dua cabang melalui UI, Anda sekarang akan melihat opsi 'menyelesaikan konflik' yang akan membawa Anda ke editor yang memungkinkan Anda untuk menangani konflik gabungan ini.

masukkan deskripsi gambar di sini

Maxwell
sumber
ini tidak bertanya tentang github jadi saya memilih apa yang saya anggap sebagai jawaban yang sangat buruk.
mschuett
1
@mschuett benar, pertanyaannya adalah "bagaimana menyelesaikan konflik di git", bukan "bagaimana menyelesaikan konflik di github". Ada perbedaan dan sudah ada terlalu banyak orang yang berpikir git dan github adalah hal yang sama, jadi segala sesuatu yang menyebarkan perasaan itu salah.
Patrick Mevzek
15

Jika Anda ingin menggabungkan dari cabang (uji) untuk dikuasai, Anda dapat mengikuti langkah-langkah ini:

Langkah 1 : Pergi ke cabang

git checkout test

Langkah 2 :

git pull --rebase origin master

Langkah 3 : Jika ada beberapa konflik, buka file-file ini untuk memodifikasinya.

Langkah 4 : Tambahkan perubahan ini

git add #your_changes_files

Langkah 5 :

git rebase --continue

Langkah 6 : Jika masih ada konflik, kembali ke langkah 3 lagi. Jika tidak ada konflik, lakukan hal berikut:

git push origin +test

Langkah 7 : Dan kemudian tidak ada konflik antara tes dan master. Anda dapat menggunakan penggabungan secara langsung.

Haimei
sumber
13

Saya selalu mengikuti langkah-langkah di bawah ini untuk menghindari konflik.

  • git checkout master (Datang ke cabang utama)
  • git pull (Perbarui master Anda untuk mendapatkan kode terbaru)
  • git checkout -b mybranch (Periksa cabang baru dan mulailah bekerja di cabang itu sehingga master Anda selalu tetap di atas trunk.)
  • git add. AND git commit AND git push (di cabang lokal Anda setelah perubahan Anda)
  • git checkout master (Kembali ke tuanmu.)

Sekarang Anda dapat melakukan hal yang sama dan mempertahankan sebanyak mungkin cabang lokal yang Anda inginkan dan bekerja secara simultan. Saya hanya melakukan checkout git ke cabang Anda bila diperlukan.

Chetan
sumber
12

Menggabungkan konflik dapat terjadi dalam situasi yang berbeda:

  • Saat menjalankan "git fetch" dan kemudian "git merge"
  • Saat menjalankan "git fetch" dan kemudian "git rebase"
  • Saat menjalankan "git pull" (yang sebenarnya sama dengan salah satu kondisi yang disebutkan di atas)
  • Saat menjalankan "git stash pop"
  • Ketika Anda menerapkan patch git (komit yang diekspor ke file yang akan ditransfer, misalnya, melalui email)

Anda perlu menginstal alat gabungan yang kompatibel dengan Git untuk menyelesaikan konflik. Saya pribadi menggunakan KDiff3, dan menurut saya bagus dan praktis. Anda dapat mengunduh versi Windows-nya di sini:

https://sourceforge.net/projects/kdiff3/files/

BTW jika Anda menginstal Git Extensions ada opsi di setup wizard untuk menginstal Kdiff3.

Kemudian setup git configs untuk menggunakan Kdiff sebagai mergetool:

$ git config --global --add merge.tool kdiff3
$ git config --global --add mergetool.kdiff3.path "C:/Program Files/KDiff3/kdiff3.exe"
$ git config --global --add mergetool.kdiff3.trustExitCode false

$ git config --global --add diff.guitool kdiff3
$ git config --global --add difftool.kdiff3.path "C:/Program Files/KDiff3/kdiff3.exe"
$ git config --global --add difftool.kdiff3.trustExitCode false

(Ingatlah untuk mengganti path dengan path sebenarnya dari file exe Kdiff.)

Maka setiap kali Anda menemukan konflik gabungan Anda hanya perlu menjalankan perintah ini:

$git mergetool

Kemudian itu membuka Kdiff3, dan pertama-tama mencoba untuk menyelesaikan konflik gabungan secara otomatis. Sebagian besar konflik akan diselesaikan secara spontan dan Anda harus memperbaiki sisanya secara manual.

Inilah yang terlihat seperti Kdiff3:

Masukkan deskripsi gambar di sini

Kemudian setelah Anda selesai, simpan file dan pergi ke file berikutnya dengan konflik dan Anda melakukan hal yang sama lagi sampai semua konflik diselesaikan.

Untuk memeriksa apakah semuanya berhasil digabungkan, jalankan saja perintah mergetool, Anda harus mendapatkan hasil ini:

$git mergetool
No files need merging
akazemis
sumber
8

Jawaban ini adalah untuk menambahkan alternatif bagi para pengguna VIM seperti saya yang lebih suka melakukan semuanya di dalam editor.


TL; DR

masukkan deskripsi gambar di sini


Tpope datang dengan plugin hebat ini untuk VIM yang disebut buron . Setelah terinstal, Anda dapat menjalankan :Gstatusuntuk memeriksa file yang memiliki konflik dan:Gdiff untuk membuka Git dalam penggabungan 3 cara.

Sekali dalam penggabungan 3-cara, buron akan membiarkan Anda mendapatkan perubahan dari salah satu cabang yang Anda gabungkan dengan cara berikut:

  • :diffget //2, dapatkan perubahan dari cabang ( HEAD ) asli:
  • :diffget //3, dapatkan perubahan dari menggabungkan cabang:

Setelah Anda selesai menggabungkan file, ketik :Gwritebuffer yang digabung. Vimcasts merilis video hebat yang menjelaskan secara rinci langkah-langkah ini.

Vicente Bolea
sumber
6

Gitlense Untuk Kode VS

Anda dapat mencoba Gitlense untuk VS Code, fitur utamanya adalah:

3. Mudah Menyelesaikan Konflik.

Saya sudah menyukai fitur ini:

masukkan deskripsi gambar di sini

2. Kesalahan Baris Saat Ini.

masukkan deskripsi gambar di sini

3. Talang Menyalahkan

masukkan deskripsi gambar di sini

4. Bilah Status Menyalahkan

masukkan deskripsi gambar di sini

Dan ada banyak fitur yang dapat Anda periksa di sini .

Ilyas karim
sumber
5

git ambil
git checkout master git rebase cabang Anda

Pada langkah ini Anda akan mencoba untuk memperbaiki konflik menggunakan IDE pilihan Anda

Anda dapat mengikuti tautan ini untuk memeriksa bagaimana memperbaiki konflik di file
https://help.github.com/articles/resolving-a-merge-conflict-using-the-command-line/

git add
git rebase --continue
git commit - ubah
git push origin HEAD: refs / draft / master (push like a draf)

Sekarang semuanya baik-baik saja dan Anda akan menemukan komit Anda di gerrit

Saya harap ini akan membantu setiap orang terkait masalah ini.

Baini.Marouane
sumber
3

Coba Visual Studio Code untuk mengedit jika Anda belum melakukannya. Apa yang dilakukan adalah setelah Anda mencoba menggabungkan (dan mendarat di konflik gabungan). Kode VS secara otomatis mendeteksi konflik gabungan.

Ini dapat membantu Anda dengan sangat baik dengan menunjukkan apa saja perubahan yang dilakukan pada yang asli dan harus Anda terima incomingatau

current change(artinya yang asli sebelum penggabungan) '?.

Itu membantu saya dan juga bisa bekerja untuk Anda!

PS: Ini hanya akan berfungsi jika Anda telah mengkonfigurasi git dengan kode dan Visual Studio Code Anda.

Kailash Bhalaki
sumber
2

Cara yang lebih aman untuk menyelesaikan konflik adalah dengan menggunakan git-mediate (solusi umum yang disarankan di sini cukup rentan terhadap kesalahan).

Lihat posting ini untuk pengantar cepat tentang cara menggunakannya.

yairchu
sumber
2

Bagi mereka yang menggunakan Visual Studio (2015 dalam kasus saya)

  1. Tutup proyek Anda di VS. Terutama dalam proyek-proyek besar VS cenderung panik ketika penggabungan menggunakan UI.

  2. Lakukan penggabungan prompt perintah.

    git checkout target_branch

    git menggabungkan source_branch

  3. Kemudian buka proyek dalam VS dan pergi ke Team Explorer -> Branch. Sekarang ada pesan yang mengatakan Gabung tertunda dan file yang bertentangan tercantum tepat di bawah pesan.

  4. Klik file yang bertentangan dan Anda akan memiliki opsi untuk Gabung, Bandingkan, Ambil Sumber, Ambil Target. Alat penggabungan dalam VS sangat mudah digunakan.

Mike
sumber
Saya menggunakan VS Code 2017 pada proyek yang sangat besar dan tidak perlu menutup proyek. Ini menanganinya dengan sangat baik :)
protoEvangelion
2

Jika Anda menggunakan intelliJ sebagai IDE, Cobalah menggabungkan induk ke cabang Anda dengan

git checkout <localbranch>
git merge origin/<remotebranch>

Ini akan menampilkan semua konflik seperti ini

A_MBPro: test anu $ git merge origin / Penggabungan otomatis src / test / java / com /.../ TestClass.java CONFLICT (konten): Gabungkan konflik dalam src / test / java / com /.../ TestClass.java

Sekarang perhatikan bahwa file TestClass.java ditampilkan dengan warna merah di intelliJ Status git juga akan ditampilkan

Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified:   src/test/java/com/.../TestClass.java

Buka file di intelliJ, itu akan memiliki bagian dengan

  <<<<<<< HEAD
    public void testMethod() {
    }
    =======
    public void testMethod() { ...
    }
    >>>>>>> origin/<remotebranch>

di mana HEAD berubah pada cabang lokal Anda dan asal / adalah perubahan dari cabang jarak jauh. Di sini simpan barang-barang yang Anda butuhkan dan hapus barang-barang yang tidak Anda butuhkan. Setelah itu langkah-langkah normal harus dilakukan. Itu adalah

   git add TestClass.java
   git commit -m "commit message"
   git push
AJC
sumber
2

Saya menggunakan Kode Visual Microsoft untuk menyelesaikan konflik. Sangat mudah digunakan. Saya menjaga proyek saya tetap terbuka di ruang kerja. Ini mendeteksi dan menyoroti konflik, apalagi memberikan opsi GUI untuk memilih perubahan apa pun yang ingin saya simpan dari KEPALA atau masuk. masukkan deskripsi gambar di sini

Ammar Mujeeb
sumber