Git'e belirli bir dosyada çakışan birleştirmeler için her zaman yerel sürümümü seçmesini nasıl söylerim?


100

Biriyle bir git deposu aracılığıyla işbirliği yaptığımı ve herhangi bir harici değişikliği kabul etmek istemediğim belirli bir dosya olduğunu varsayalım.

Git çekerken her seferinde çakışan bir birleştirme hakkında şikayet etmemesi için yerel depomu kurmamın bir yolu var mı? Bu dosyayı birleştirirken her zaman yerel sürümümü seçmek istiyorum.


1
.Gitattributes aracılığıyla basit bir çözüm ve çok basit bir "birleştirme sürücüsü" ekledim
VonC

12
TD; LR:echo 'path/to/file merge=ours' >> .gitattributes && git config --global merge.ours.driver true
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

@CiroSantilli: Linux'ta bir cazibe gibi çalışıyor. Bu sürücü Git'e dahil edilecek kadar basit ...
krlmlr

Değişikliklerinizi dosyaya aktarmak ister misiniz? Veya, varsayılanın git'te depolandığı bir yapılandırma dosyası mı?
Ian Ringrose

@CiroSantilli 新疆 改造 中心 六四 事件 法轮功 'nin yorumu doğru, ancak bu davranışın sisteminizdeki --globaletiketi ile her repo için oluşmasını sağlayacaktır . Bu davranışı yalnızca tek bir depo için istiyorsanız, --globalbayrağı echo 'path/to/file merge=ours' >> .gitattributes && git config merge.ours.driver true
atlayın

Yanıtlar:


140

Bir yapılandırma dosyasının belirli bir örneğinde, Ron'un cevabına katılıyorum :
bir yapılandırma, çalışma alanınıza "özel" olmalıdır (bu nedenle "bir .gitignoredosyada bildirildiği gibi" "yok sayılır ").
Bir yapılandırma dosyası olabilir şablonu ile dizgecikli değerler içinde ve bu dönüşüm bir komut dosyası config.templateözel (ve göz ardı) yapılandırma dosyası içine dosyayı.


Bununla birlikte, bu özel açıklama, daha geniş ve daha genel bir sorunun ne olduğunu, yani sorunuzun (!) Yanıtını vermez:

Git'e belirli bir dosyada çakışan birleştirmeler için her zaman yerel sürümümü seçmesini nasıl söylerim? (herhangi bir dosya veya dosya grubu için)

Bu tür bir birleştirme, bir çatışma olduğunda bir dosyanın "bizim" veya "onların" sürümünü her zaman kopyalayacağınız bir "kopya birleştirme" dir.

(aynı Brian Vandenberg notları yorumlarda , ' ours' ve ' theirs' burada bir birleştirme işlemi için kullanılmaktadır .
Onlar edilir tersine bir için rebase : bakınız " Why is the meaning of “ours” and “theirs” reversed with git-svn", bir rebase kullanan, git rebase"takip 'uzak' 'Yerel' ve " )

"Bir dosya" için (genel olarak bir dosya, kötü bir örnek olduğu için bir "yapılandırma" dosyasından bahsetmeyen bir dosya), bunu birleştirme adı verilen özel bir komut dosyasıyla elde edersiniz.
Eğer bir tanımlayacaktır çünkü Git o senaryoyu arayacak gitattributes değer bir tanımlar, özel birleştirme sürücü .

"Özel birleştirme sürücüsü", bu durumda, temelde mevcut sürümü değiştirmeden koruyan ve böylece her zaman yerel sürümünüzü seçmenize izin veren çok basit bir komut dosyasıdır.

. IE, şöyle kaydetti tarafından Ciro Santilli :

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

Bunu basit bir senaryoda, Windows üzerinde bir msysgit 1.6.3 ile sadece bir DOS oturumunda test edelim:

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/

Şimdi, ikisinin de çakışması olacak, ancak farklı şekilde birleştirilecek iki dosya yapalım.

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

İki farklı git dalındaki her iki dosyanın içeriğinde bir "çakışma" oluşturacağız:

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

Şimdi, "hisBranch" ı "myBranch" ile birleştirmeyi deneyelim:

  • çakışan birleştirmeler için manuel çözünürlük
  • hariç için dirWithCopyMerge\b.txthep saklamak istediğiniz benim sürümünü b.txt.

Birleştirme ' MyBranch' içinde gerçekleştiğinden, ona geri döneceğiz gitattributesve birleştirme davranışını özelleştirecek ' ' direktiflerini ekleyeceğiz .

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

.gitattributesDizinde tanımlanmış bir dosyamız var dirWithCopyMerge(yalnızca birleştirmenin gerçekleşeceği dalda tanımlanmıştır myBranch) ve .git\configşimdi bir birleştirme sürücüsü içeren bir dosyamız var .

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

Henüz keepMine.sh tanımlamadıysanız ve birleştirme işlemini yine de başlattıysanız, işte size ne elde edersiniz.

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

Bu iyi:

  • a.txt birleştirilmeye hazır ve içinde çatışma var
  • b.txtbirleştirme sürücüsünün bununla ilgilenmesi gerektiğinden ( .gitattributesdizinindeki dosyadaki yönerge nedeniyle) hala dokunulmamıştır .

İçinde keepMine.shherhangi bir yeri tanımlayın %PATH%(veya $PATHUnix arkadaşımız için. Her ikisini de yapıyorum: VirtualBox oturumunda bir Ubuntu oturumum var)

Şöyle yorumladı tarafından lrkwz ve "açıklanan Birleştirme Stratejileri ait" bölümünde Özelleştirme Git - Git Nitelikler , bu kabuk komutuyla shell script değiştirebilirsiniz true.

git config merge.keepMine.driver true

Ancak genel durumda, bir komut dosyası tanımlayabilirsiniz:

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

(Bu basit bir birleştirme sürücü vardı;) (bu durumda, kullanımda bile daha basit true)
(eğer hemen önce ekleyin diğer sürümünü kullanmaya devam etmek istiyorsa exit 0hattı:
cp -f $3 $2.
İşte bu Şoför uzağa diğer gelen versiyonunu tutacak birleşir. şube, herhangi bir yerel değişikliği geçersiz kılar)

Şimdi, birleştirmeyi baştan tekrar deneyelim:

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.

Birleştirme başarısız olur ... sadece a.txt için .
A.txt dosyasını düzenleyin ve 'hisBranch' satırını bırakın, ardından:

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

Bu birleştirme sırasında b.txt'nin korunup korunmadığını kontrol edelim

type dirWithCopyMerge\b.txt
b
myLineForB

Son kaydetme, tam birleştirmeyi temsil eder :

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

(Merge ile başlayan satır bunu kanıtlıyor)


Git'in yapacağı gibi, birleştirme sürücüsünü tanımlayabileceğinizi, birleştirebileceğinizi ve / veya üzerine yazabileceğinizi düşünün:

  • incelemek <dir>/.gitattributes(söz konusu yolla aynı dizindedir): .gitattributesdizinlerde diğerine üstün gelecektir
  • Daha sonra .gitattributes(üst dizinde bulunan) inceler , yalnızca önceden ayarlanmadıysa yönergeleri ayarlayacaktır.
  • Sonunda inceliyor $GIT_DIR/info/attributes. Bu dosya, ağaç içi ayarları geçersiz kılmak için kullanılır. <dir>/.gitattributesDirektiflerin üzerine yazacak.

"Birleştirme" ile, "birleştirme" çoklu birleştirme sürücüsünü kastediyorum.
Nick Green , yorumlarda , birleştirme sürücülerini gerçekten birleştirmeye çalışır : bkz. " Pomları python git sürücüsü aracılığıyla birleştirme ".
Ancak, diğer sorusunda da belirtildiği gibi , yalnızca çatışma durumunda işe yarar (her iki dalda eşzamanlı değişiklik).


1
Ayrıntılı cevap için teşekkürler! Sürüm kontrolü yapılandırma dosyalarının mantıklı olmadığını anlıyorum, ancak açık ve motive edici bir örneğin peşindeydim. Doğrusu, beni ilgilendiren daha kapsamlı bir soru. Daha önce git birleştirme sürücülerini hiç duymamıştım, bu yüzden beni aydınlattığın için teşekkürler.
saffsd

6
cp -f $3 $2Muhtemelen, yani tırnak içine alınmalıdır cp -f "$3" "$2".
Ark

1
@VonC detaylı cevap için teşekkürler! Bununla ilgili sorunum, sürücüyü .git / config dosyalarına ayarlayan kişilere bağlı olması. Sürücü bilgisini projenin kendisine eklemek istiyorum, böylece otomatik olacak ve daha az kurulum çalışması yapılacak. Herhangi bir işaret var mı?
Juan Delgado

2
@ulmangt: Bu betiği git deposunda çok fazla saklayabilirsiniz, ... ana dizinini PATH(Unix veya Windows PATH) ' a eklemenin bir yolunu bulduğunuz sürece . Bu komut dosyası Unix bash kabuğu veya MingWin bash MsysGit Windows kabuğu aracılığıyla yorumlanacağından, taşınabilir olacaktır.
VonC

5
@VonC Teşekkürler. Bir sorun daha. Belirli koşullar altında (birleştirilen yerel şubede herhangi bir değişiklik yapılmadıysa), birleştirme sürücüsünün hiçbir zaman çağrılmadığı ve bunun sonucunda yerel dosyaların değiştirildiği (özel birleştirme sürücüsünü kullanması gereken birleştirme sırasında değişmelerini önlemek için). Git'i her zaman birleştirme sürücüsünü kullanmaya zorlamanın bir yolu var mı ?
ulmangt

1

@ Ciro-santilli'nin yorumladığı gibi, bunu .gitattributesayarlarla kullanmanın basit yolu :

path/to/file merge=ours

ve bu stratejiyi şununla etkinleştirin:

git config --global merge.ours.driver true

(Bunu daha görünür kılmak için bir cevap olarak ekliyorum, ancak kendim için kullanıcının kredilerinin üstüne çıkmayı denememek için Topluluk Wiki yapıyorum. Lütfen onu tebrik etmek için buradaki Q altındaki yorumunu yükseltin!)


(Bir kenara: eğer birisi cevabı yorum olarak verirse ve cevap eklemezse, CW olmayan bir cevap yazmak ve kredileri almak tamamen tamamdır. Eğer hala aktif bir üye iseler, cevap eklemek için onlara ping atabilirsiniz. dilersiniz, ancak teknik olarak zaten şansları vardı :-)).
29'da halfer

0

Asla üzerine yazılmasını istemediğimiz birden fazla yapılandırma dosyamız var. Ancak .gitignore ve .gitattributes bizim durumumuzda işe yaramadı. Çözümümüz, yapılandırma dosyalarını bir yapılandırma dalında depolamaktı. Ardından, git birleştirme sırasında dosyaların değiştirilmesine izin verin, ancak birleştirmenin hemen ardından "git checkout dalı -" kullanın. her birleştirmeden sonra yapılandırma dosyalarımızı yapılandırma dalından kopyalamak için. Ayrıntılı yığın aşımı yanıtı burada

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.