Bagaimana cara memberi tahu git untuk selalu memilih versi lokal saya untuk penggabungan yang berkonflik pada file tertentu?

100

Katakanlah saya berkolaborasi dengan seseorang melalui repositori git, dan ada file tertentu yang tidak ingin saya terima perubahan eksternal apa pun.

Adakah cara bagi saya untuk mengatur repo lokal saya agar tidak mengeluh tentang penggabungan yang berkonflik setiap kali saya melakukan git pull? Saya ingin selalu memilih versi lokal saya saat menggabungkan file ini.

saffsd
sumber
1
Baru saja menambahkan solusi sederhana melalui .gitattributes dan "driver gabungan" yang sangat mendasar
VonC
12
TD; LR:echo 'path/to/file merge=ours' >> .gitattributes && git config --global merge.ours.driver true
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
@CiroSantilli: Bekerja seperti pesona di Linux. Driver ini cukup sederhana untuk dipasang di Git ...
krlmlr
Apakah Anda ingin memasukkan perubahan Anda ke file? Atau, misalnya, file konfigurasi yang defaultnya disimpan di git.
Ian Ringrose
Komentar @CiroSantilli 新疆 改造 中心 六四 事件 法轮功 benar, tetapi akan membuat perilaku ini terjadi untuk setiap repo di sistem Anda dengan --globaltag. Jika Anda hanya menginginkan perilaku ini untuk satu repo, tinggalkan --globalbenderanya:echo 'path/to/file merge=ours' >> .gitattributes && git config merge.ours.driver true
majorobot

Jawaban:

140

Pada contoh spesifik dari file konfigurasi, saya setuju dengan jawaban Ron :
konfigurasi harus "pribadi" ke ruang kerja Anda (karenanya "diabaikan", seperti dalam "dideklarasikan dalam .gitignorefile").
Anda mungkin memiliki template file konfigurasi dengan nilai tokenized di dalamnya, dan skrip yang mengubah config.templatefile itu menjadi file konfigurasi pribadi (dan diabaikan).


Namun, pernyataan khusus itu tidak menjawab pertanyaan yang lebih luas yang lebih umum, yaitu pertanyaan Anda (!):

Bagaimana cara memberi tahu git untuk selalu memilih versi lokal saya untuk penggabungan yang berkonflik pada file tertentu? (untuk file atau grup file apa pun)

Penggabungan jenis ini adalah "penggabungan salinan", di mana Anda akan selalu menyalin versi 'milik kami' atau 'milik mereka' setiap kali ada konflik.

(seperti yang dicatat Brian Vandenberg di komentar , ' ours' dan ' theirs' di sini digunakan untuk penggabungan .
Mereka dibalik untuk rebase : lihat " Why is the meaning of “ours” and “theirs” reversed with git-svn", yang menggunakan rebase, " git rebase, melacak 'lokal' dan 'jarak jauh' " )

Untuk "file" (file secara umum, tidak berbicara tentang file "config", karena ini adalah contoh yang buruk), Anda akan melakukannya dengan skrip kustom yang dipanggil melalui penggabungan.
Git akan memanggil skrip tersebut karena Anda telah menentukan nilai gitattributes , yang mendefinisikan driver gabungan khusus .

Dalam hal ini, "custom merge driver" adalah skrip yang sangat sederhana yang pada dasarnya tidak akan mengubah versi saat ini, sehingga memungkinkan Anda untuk selalu memilih versi lokal Anda.

YAITU., Seperti dicatat oleh Ciro Santilli :

echo 'path/to/file merge=ours' >> .gitattributes
git config --global merge.ours.driver true

Mari kita uji dalam skenario sederhana, dengan msysgit 1.6.3 di Windows, hanya dalam sesi DOS:

cd f:\prog\git\test
mkdir copyMerge\dirWithConflicts
mkdir copyMerge\dirWithCopyMerge
cd copyMerge
git init
Initialized empty Git repository in F:/prog/git/test/copyMerge/.git/

Sekarang, mari buat dua file, yang keduanya akan memiliki konflik, tetapi akan digabungkan secara berbeda.

echo a > dirWithConflicts\a.txt
echo b > dirWithCopyMerge\b.txt
git add -A
git commit -m "first commit with 2 directories and 2 files"
[master (root-commit) 0adaf8e] first commit with 2 directories and 2 files

Kami akan memperkenalkan "konflik" pada konten kedua file tersebut di dua cabang git yang berbeda:

git checkout -b myBranch
Switched to a new branch 'myBranch'
echo myLineForA >> dirWithConflicts\a.txt
echo myLineForB >> dirWithCopyMerge\b.txt
git add -A
git commit -m "add modification in myBranch"
[myBranch 97eac61] add modification in myBranch

git checkout master
Switched to branch 'master'
git checkout -b hisBranch
Switched to a new branch 'hisBranch'
echo hisLineForA >> dirWithConflicts\a.txt
echo hisLineForB >> dirWithCopyMerge\b.txt
git add -A
git commit -m "add modification in hisBranch"
[hisBranch 658c31c] add modification in hisBranch

Sekarang, mari kita coba menggabungkan "hisBranch" ke "myBranch", dengan:

  • resolusi manual untuk penggabungan yang bertentangan
  • kecuali untuk dirWithCopyMerge\b.txtdi mana aku selalu ingin menjaga saya versi b.txt.

Karena penggabungan terjadi di ' MyBranch', kami akan beralih kembali ke sana, dan menambahkan gitattributesarahan ' ' yang akan menyesuaikan perilaku penggabungan.

git checkout myBranch
Switched to branch 'myBranch'
echo b.txt merge=keepMine > dirWithCopyMerge\.gitattributes
git config merge.keepMine.name "always keep mine during merge"
git config merge.keepMine.driver "keepMine.sh %O %A %B"
git add -A
git commit -m "prepare myBranch with .gitattributes merge strategy"
[myBranch ec202aa] prepare myBranch with .gitattributes merge strategy

Kami memiliki .gitattributesfile yang ditentukan di dirWithCopyMergedirektori (hanya ditentukan di cabang tempat penggabungan akan terjadi :) myBranch, dan kami memiliki .git\configfile yang sekarang berisi driver gabungan.

[merge "keepMine"]
        name = always keep mine during merge
        driver = keepMine.sh %O %A %B

Jika Anda belum mendefinisikan keepMine.sh, dan tetap meluncurkan penggabungan, inilah yang Anda dapatkan.

git merge hisBranch
sh: keepMine.sh: command not found
fatal: Failed to execute internal merge
git st
# On branch myBranch
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   dirWithConflicts/a.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

type dirWithConflicts\a.txt
a
<<<<<<< HEAD:dirWithConflicts/a.txt
myLineForA
=======
hisLineForA
>>>>>>> hisBranch:dirWithConflicts/a.txt

Ini baik saja:

  • a.txt siap untuk digabungkan dan memiliki konflik di dalamnya
  • b.txtmasih belum tersentuh, karena driver gabungan seharusnya menanganinya (karena arahan dalam .gitattributesfile di direktorinya).

Tentukan keepMine.shdi mana saja di Anda %PATH%(atau $PATHuntuk teman Unix kami. Saya melakukan keduanya tentu saja: Saya memiliki sesi Ubuntu dalam sesi VirtualBox)

Seperti yang dikomentari oleh lrkwz , dan dijelaskan di bagian " Merge Strategies " dari Menyesuaikan Atribut Git - Git , Anda dapat mengganti skrip shell dengan perintah shell true.

git config merge.keepMine.driver true

Namun dalam kasus umum, Anda dapat menentukan file skrip:

keepMine.sh

# I want to keep MY version when there is a conflict
# Nothing to do: %A (the second parameter) already contains my version
# Just indicate the merge has been successfully "resolved" with the exit status
exit 0

(yang satu driver penggabungan sederhana;) (Bahkan sederhana dalam kasus, penggunaan true)
(Jika Anda ingin menjaga versi lainnya, cukup tambahkan sebelum exit 0baris:
cp -f $3 $2.
Itu saja Anda menggabungkan pengemudi akan aways menjaga versi datang dari yang lain. cabang, menimpa perubahan lokal apa pun)

Sekarang, mari coba lagi penggabungan dari awal:

git reset --hard
HEAD is now at ec202aa prepare myBranch with .gitattributes merge strategy

git merge hisBranch
Auto-merging dirWithConflicts/a.txt
CONFLICT (content): Merge conflict in dirWithConflicts/a.txt
Auto-merging dirWithCopyMerge/b.txt
Automatic merge failed; fix conflicts and then commit the result.

Penggabungan gagal ... hanya untuk a.txt .
Edit a.txt dan tinggalkan baris dari 'hisBranch', lalu:

git add -A
git commit -m "resolve a.txt by accepting hisBranch version"
[myBranch 77bc81f] resolve a.txt by accepting hisBranch version

Mari kita periksa bahwa b.txt telah dipertahankan selama penggabungan ini

type dirWithCopyMerge\b.txt
b
myLineForB

Komit terakhir memang mewakili gabungan penuh :

git show -v 77bc81f5e
commit 77bc81f5ed585f90fc1ca5e2e1ddef24a6913a1d
Merge: ec202aa 658c31c
git merge hisBranch
Already up-to-date.

(Garis yang dimulai dengan Gabung membuktikan itu)


Pertimbangkan bahwa Anda dapat menentukan, menggabungkan dan / atau menimpa driver gabungan, karena Git akan:

  • memeriksa <dir>/.gitattributes(yang ada di direktori yang sama dengan jalur yang dimaksud): akan berlaku atas yang lain .gitattributesdi direktori
  • Kemudian memeriksa .gitattributes(yang ada di direktori induk), hanya akan mengatur direktif jika belum diatur
  • Akhirnya ia memeriksa $GIT_DIR/info/attributes. File ini digunakan untuk mengganti pengaturan dalam pohon. Ini akan menimpa <dir>/.gitattributesarahan.

Dengan "menggabungkan", yang saya maksud adalah "menggabungkan" beberapa driver gabungan.
Nick Green mencoba, di komentar , untuk benar-benar menggabungkan driver gabungan: lihat " Menggabungkan pom melalui driver git python ".
Namun, seperti yang disebutkan dalam pertanyaannya yang lain , ini hanya berfungsi jika terjadi konflik (modifikasi bersamaan di kedua cabang).

VonC
sumber
1
Terima kasih atas jawaban rinci! Saya mengerti bahwa tidak masuk akal untuk file konfigurasi kontrol-versi, tetapi saya mencari contoh langsung yang memotivasi. Memang, pertanyaan yang lebih luas itulah yang membuat saya tertarik. Saya belum pernah mendengar tentang git merge drivers sebelumnya, jadi terima kasih telah mencerahkan saya.
saffsd
6
The cp -f $3 $2mungkin harus dikutip, yaitu cp -f "$3" "$2".
Arc
1
@VonC terima kasih atas jawaban terperinci! Masalah yang saya hadapi adalah itu tergantung pada orang yang mengatur driver dalam file .git / config mereka. Saya ingin menambahkan informasi driver ke proyek itu sendiri, sehingga akan otomatis dan lebih sedikit pekerjaan penyiapan yang harus dilakukan. Ada petunjuk?
Juan Delgado
2
@ulmangt: Anda juga dapat menyimpan banyak skrip itu di repo git, ... selama Anda menemukan cara untuk menambahkan direktori induknya ke PATH(Unix atau Windows PATH). Karena skrip itu akan ditafsirkan melalui shell bash Unix, atau melalui shell Windows MsysGit bash MingWin, itu akan menjadi portabel.
VonC
5
@VonC Terima kasih. Satu masalah lagi. Dalam keadaan tertentu (jika belum ada perubahan apa pun pada cabang lokal yang digabungkan ke) tampaknya driver gabungan tidak pernah dipanggil, yang mengakibatkan file lokal diubah (yang seharusnya menggunakan driver gabungan kustom untuk mencegahnya berubah selama penggabungan). Apakah ada cara untuk memaksa git agar selalu menggunakan driver gabungan?
ulmangt
1

Seperti yang dikomentari oleh @ ciro-santilli, cara sederhana untuk melakukannya dengan menggunakan .gitattributespengaturannya:

path/to/file merge=ours

dan aktifkan strategi ini dengan:

git config --global merge.ours.driver true

(Saya menambahkan ini sebagai jawaban agar lebih terlihat tetapi menjadikannya Wiki Komunitas untuk tidak mencoba mendapatkan kredit pengguna di atas untuk diri saya sendiri. Harap beri suara positif pada komentarnya di bawah Q di sini untuk memberinya kudos!)

Greg Dubicki
sumber
(Selain: jika seseorang memberikan jawaban sebagai komentar dan tidak menambahkan jawaban, tidak masalah untuk menulis jawaban non-CW dan mendapatkan kredit. Jika mereka masih menjadi anggota aktif, Anda dapat melakukan ping untuk menambahkan jawaban jika Anda ingin, tetapi secara teknis mereka sudah memiliki kesempatan :-)).
halfer
0

Kami memiliki banyak file konfigurasi yang tidak ingin kami timpa. Namun .gitignore dan .gitattributes tidak berfungsi dalam situasi kami. Solusi kami adalah menyimpan file konfigurasi di cabang konfigurasi. Kemudian, izinkan file diubah selama git merge, tetapi segera setelah penggabungan, gunakan "git checkout branch -." untuk menyalin file konfigurasi kami dari cabang konfigurasi setelah setiap penggabungan. Jawaban stackoverflow mendetail di sini

HamletHub
sumber