Come faccio a dire a Git di selezionare sempre la mia versione locale per le unioni conflittuali su un file specifico?

Supponiamo che io collabori con qualcuno tramite un repository git e che ci sia un particolare file in cui non desidero accettare modifiche esterne.

C’è un modo per me di creare il mio repository locale per non lamentarmi di un’unione conflittuale ogni volta che tiro fuori? Mi piacerebbe selezionare sempre la mia versione locale quando unisco questo file.

Sull’istanza specifica di un file di configurazione, sono d’accordo con la risposta di Ron :
una configurazione dovrebbe essere “privata” nel proprio spazio di lavoro (quindi “ignorata”, come in “dichiarato in un file .gitignore “).
Si può avere un modello di file di configurazione con valori tokenizzati e uno script che trasforma il file config.template in un file di configurazione privato (e ignorato).


Tuttavia, questa specifica osservazione non risponde a quale sia una domanda più generale, cioè la tua domanda (!):

Come faccio a dire a Git di selezionare sempre la mia versione locale per le unioni conflittuali su un file specifico? (per qualsiasi file o gruppo di file)

Questo tipo di unione è un “copia unione”, in cui copi sempre la “nostra” o la “loro” versione di un file ogni volta che c’è un conflitto.

(Come osserva Brian Vandenberg nei commenti , ours ” e ” theirs ” sono qui usati per un’unione .
Sono invertiti per un rebase : vedi ” Why is the meaning of “ours” and “theirs” reversed with git-svn “, che usa un rebase, ” git rebase , tenendo traccia di” locale “e” remoto ” “)

Per “un file” (un file in generale, non parlando di un file “config”, poiché è un cattivo esempio), lo si otterrebbe con uno script personalizzato chiamato attraverso le unioni.
Git chiamerà quello script perché definirai un valore gitattributes , che definisce un driver di unione personalizzato .

Il “custom merge driver” è, in questo caso, uno script molto semplice che sostanzialmente manterrà invariata la versione attuale, consentendo quindi di selezionare sempre la versione locale.


Proviamo in un semplice scenario, con un msysgit 1.6.3 su Windows, in una semplice sessione 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/ 

Ora creiamo due file, che avranno entrambi dei conflitti, ma che saranno uniti in modo diverso.

 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 

Introdurremo un “conflitto” nel contenuto di entrambi i file in due diversi rami git:

 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 

Ora proviamo a unire “hisBranch” su “myBranch”, con:

  • risoluzione manuale per conflitti conflittuali
  • ad eccezione di dirWithCopyMerge\b.txt dove voglio sempre mantenere la mia versione di b.txt .

Poiché l’unione si verifica in ” MyBranch “, torneremo ad esso e aggiungeremo le direttive ” gitattributes ” che personalizzeranno il comportamento di unione.

 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 

Abbiamo un file .gitattributes definito nella directory dirWithCopyMerge (definito solo nel ramo in cui si verificherà l’unione: myBranch ), e abbiamo un file .git\config che ora contiene un driver di unione.

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

Se non si definisce ancora keepMine.sh e si avvia comunque l’unione, ecco cosa si ottiene.

 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 ..." to update what will be committed) # (use "git checkout -- ..." 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 

Questo va bene:

  • a.txt è pronto per essere unito e ha conflitto in esso
  • b.txt è ancora intatto, poiché il driver di merge dovrebbe occuparsene (a causa della direttiva nel file .gitattributes nella sua directory).

Definisci un keepMine.sh ovunque nel tuo %PATH% (o $PATH per il nostro amico Unix. Ovviamente faccio entrambi: ho una sessione di Ubuntu in una sessione di VirtualBox)

Come commentato da lrkwz e descritto nella sezione ” Unisci strategie ” di Personalizzare Git – Attributi Git , puoi sostituire lo script della shell con il comando shell true .

 git config merge.keepMine.driver true 

Ma nel caso generale, è ansible definire un file di script:

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 

(era un semplice driver di unione;) (Anche più semplice in questo caso, usa true )
(Se si desidera mantenere l’altra versione, aggiungere prima la riga exit 0 :
cp -f $3 $2 .
Questo è tutto. Unisci il driver che vorrebbe mantenere la versione proveniente dall’altro ramo, ignorando qualsiasi modifica locale)

Ora, riprova l’unione dall’inizio:

 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. 

L’unione non riesce … solo per a.txt .
Modifica a.txt e lascia la linea da “hisBranch”, quindi:

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

Controlliamo che b.txt sia stato conservato durante questa unione

 type dirWithCopyMerge\b.txt b myLineForB 

L’ultimo commit rappresenta l’unione completa :

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

(La linea che inizia con Merge lo dimostra)


Considera che puoi definire, combinare e / o sovrascrivere il driver di unione, poiché Git:

  • esaminare /.gitattributes (che si trova nella stessa directory del percorso in questione): prevarrà sugli altri .gitattributes nelle directory
  • Quindi esamina .gitattributes (che si trova nella directory padre), imposta le direttive solo se non è già impostato
  • Alla fine esamina $GIT_DIR/info/attributes . Questo file viene utilizzato per sovrascrivere le impostazioni in-tree. Sovrascriverà le direttive /.gitattributes .

Per “combinazione”, intendo il driver di unione multipla “aggregato”.
Nick Green cerca, nei commenti , di combinare effettivamente i driver di unione: vedi ” Unisci il driver di git via python git “.
Tuttavia, come menzionato nella sua altra domanda , funziona solo in caso di conflitti (modifica simultanea in entrambi i rami).

Abbiamo diversi file di configurazione che non vogliamo mai sovrascrivere. Tuttavia .gitignore e .gitattributes non hanno funzionato nella nostra situazione. La nostra soluzione era di memorizzare i file di configurazione in un ramo di configurazione. Quindi, consenti ai file di essere modificati durante la git merge, ma immediatamente dopo l’unione usa il “git checkout branch -.” copiare i nostri file di configurazione dal ramo configs dopo ogni unione. Stackoverflow dettagliato rispondi qui