Come unire o selezionare selettivamente le modifiche da un altro ramo in Git?


1450

Sto usando git su un nuovo progetto che ha due rami di sviluppo paralleli, ma attualmente sperimentali:

  • master: importazione di codebase esistenti più alcune mod di cui sono generalmente sicuro
  • exp1: ramo sperimentale n. 1
  • exp2: ramo sperimentale n. 2

exp1e exp2rappresentano due approcci architettonici molto diversi. Fino a quando non andrò oltre non ho modo di sapere quale (se uno dei due) funzionerà. Mentre faccio progressi in un ramo a volte ho delle modifiche che potrebbero essere utili nell'altro ramo e vorrei unire solo quelle.

Qual è il modo migliore per unire i cambiamenti selettivi da un ramo di sviluppo all'altro lasciando tutto il resto?

Approcci che ho considerato:

  1. git merge --no-commit seguito da unstaging manuale di un gran numero di modifiche che non voglio rendere comuni tra i rami.

  2. Copia manuale dei file comuni in una directory temporanea seguita da git checkoutper spostarsi sull'altro ramo e quindi copia più manuale dalla directory temporanea nell'albero di lavoro.

  3. Una variazione su quanto sopra. Abbandonare i exprami per ora e utilizzare due repository locali aggiuntivi per la sperimentazione. Ciò rende la copia manuale dei file molto più semplice.

Tutti e tre questi approcci sembrano noiosi e soggetti a errori. Spero che ci sia un approccio migliore; qualcosa di simile a un parametro del percorso del filtro che renderebbe git-mergepiù selettivo.


5
Se le modifiche nei rami sperimentali sono ben organizzate in commit separati, è meglio pensare in termini di unione di commit selettivi anziché file selettivi. La maggior parte delle risposte che seguono presuppone che sia così.
Akaihola,

2
Una combinazione di git merge -s ours --no-commitseguiti da alcuni non git read-treesarebbe una buona soluzione per questo? Vedere stackoverflow.com/questions/1214906/...
VonC

34
Una più recente questione ha una sola riga, ben scritto risposta: stackoverflow.com/questions/10784523/...
brahn

Risposte:


475

Utilizzare il comando cherry-pick per ottenere singoli commit da un ramo.

Se le modifiche desiderate non rientrano nei singoli commit, utilizzare il metodo mostrato qui per suddividere il commit in singoli commit . In parole povere, si utilizza git rebase -iper ottenere il commit originale da modificare, quindi git reset HEAD^per ripristinare selettivamente le modifiche, quindi git commitper eseguire il commit di quel bit come nuovo commit nella cronologia.

C'è un altro bel metodo qui in Red Hat Magazine, dove usano git add --patcho forse git add --interactiveti permette di aggiungere solo parti di un pezzo, se vuoi dividere diverse modifiche in un singolo file (cerca "diviso" in quella pagina).

Dopo aver diviso le modifiche, ora puoi scegliere solo quelle che desideri.


14
Da quanto ho capito, ciò è inutilmente più contorto della risposta più votata.
Alexander Bird,

54
Questa è tecnicamente la risposta corretta, la risposta corretta appare "contorta". --- La risposta più votata è solo una risposta rapida e sporca "fa il trucco", che per la maggior parte delle persone è tutto ciò di cui si tratta (:
Jacob

3
@akaihola: HEAD ^ è corretto. Vedi man git-rev-parse: un suffisso ^ per un parametro di revisione indica il primo genitore di quell'oggetto commit. Il prefisso ^ notazione viene utilizzato per escludere i commit raggiungibili da un commit.
Tyler Rick,

13
Volevo solo condividere un altro approccio che sembra il più pulito e meno contorto di tutti: jasonrudolph.com/blog/2009/02/25/… totale semplicità e fantastico
superuseroi,

14
Confuso dal dibattito su quale approccio sia "corretto"? Considera la differenza tra file e commit (vedi Note in fondo) . OP vuole unire FILES e non menziona IMPEGNI. La risposta più votata è specifica per i file; la risposta accettata utilizza cherry-pick, che è specifica per gli commit. Cherry-pick può essere la chiave per unire i commit in modo selettivo, ma può essere molto doloroso per spostare file da un ramo all'altro. Sebbene i commit siano il cuore della forza di Git, non dimenticare che i file hanno ancora un ruolo!
Kay V,

970

Ho avuto lo stesso identico problema menzionato sopra da te. Ma ho trovato questo più chiaro nel spiegare la risposta.

Sommario:

  • Verifica i percorsi dal ramo che desideri unire,

    $ git checkout source_branch -- <paths>...
    

    Suggerimento: funziona anche senza --essere visto nel post collegato.

  • o per unire selettivamente gli hunk

    $ git checkout -p source_branch -- <paths>...
    

    In alternativa, utilizzare reset e quindi aggiungere con l'opzione -p,

    $ git reset <paths>...
    $ git add -p <paths>...
    
  • Finalmente impegno

    $ git commit -m "'Merge' these changes"
    

9
L'articolo collegato di Bart J è l'approccio migliore. Chiaro, semplice, un comando. È quello che sto per usare. :)
Pistos,

256
Questa non è una vera unione. Stai selezionando le modifiche per file anziché per commit e perderai tutte le informazioni di commit esistenti (autore, messaggio). Certo, questo è buono se vuoi unire tutte le modifiche in alcuni file ed è ok che devi ripetere tutti i commit. Ma se i file contengono entrambe le modifiche da unire e altre da scartare, uno dei metodi forniti in altre risposte ti servirà meglio.
Akaihola,

10
@mykhal e altri: questo mette in scena automaticamente i file nell'indice, quindi se hai fatto il check out foo.cfai git reset HEAD foo.cper annullare la messa in scena di quel file e puoi quindi diff. L'ho scoperto dopo averlo provato e tornato qui per cercare una risposta a questo
michiakig,

12
per vedere le modifiche puoi anche usare:git diff --cached
OderWat,

9
Secondo questa risposta git checkout -p <revision> -- <path> sarà lo stesso che emettere i primi tre comandi che hai descritto :)
7hi4g0

338

Per unire selettivamente i file da un ramo a un altro ramo, esegui

git merge --no-ff --no-commit branchX

dove si branchXtrova il ramo che si desidera unire nel ramo corrente.

L' --no-commitopzione metterà in scena i file che sono stati uniti da Git senza effettivamente eseguirne il commit. Questo ti darà l'opportunità di modificare i file uniti come desideri e poi di impegnarli da soli.

A seconda di come si desidera unire i file, ci sono quattro casi:

1) Vuoi una vera unione.

In questo caso, accetti i file uniti nel modo in cui Git li ha uniti automaticamente e poi li commetti.

2) Ci sono alcuni file che non vuoi unire.

Ad esempio, si desidera conservare la versione nel ramo corrente e ignorare la versione nel ramo da cui si sta unendo.

Per selezionare la versione nel ramo corrente, eseguire:

git checkout HEAD file1

Questo recupererà la versione di file1nel ramo corrente e sovrascriverà il file1comando automatico di Git.

3) Se vuoi la versione in branchX (e non una vera unione).

Correre:

git checkout branchX file1

Questo recupererà la versione di file1in branchXe sovrascriverà file1automaticamente unita da Git.

4) L'ultimo caso è se si desidera selezionare solo fusioni specifiche file1.

In questo caso, puoi modificare file1direttamente la modifica , aggiornarla a qualunque cosa tu voglia file1diventare la versione e quindi eseguire il commit.

Se Git non è possibile unire automaticamente un file, segnalerà il file come " unmerged " e produrre una copia in cui è necessario risolvere i conflitti manualmente.



Per spiegare ulteriormente con un esempio, supponiamo che tu voglia unirti branchXal ramo corrente:

git merge --no-ff --no-commit branchX

Quindi si esegue il git statuscomando per visualizzare lo stato dei file modificati.

Per esempio:

git status

# On branch master
# Changes to be committed:
#
#       modified:   file1
#       modified:   file2
#       modified:   file3
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:      file4
#

Where file1, file2e file3sono i file che git si sono uniti automaticamente con successo.

Ciò significa che i cambiamenti in mastere branchXper tutti e tre questi file sono stati combinati insieme senza alcun conflitto.

È possibile controllare come è stata eseguita l'unione eseguendo il git diff --cached;

git diff --cached file1
git diff --cached file2
git diff --cached file3

Se ritieni che alcune fusioni siano indesiderabili, puoi farlo

  1. modifica direttamente il file
  2. Salva
  3. git commit

Se non si desidera unire file1e si desidera conservare la versione nel ramo corrente

Correre

git checkout HEAD file1

Se non si desidera unire file2e si desidera solo la versionebranchX

Correre

git checkout branchX file2

Se vuoi file3unirti automaticamente, non fare nulla.

Git lo ha già unito a questo punto.


file4sopra è una fusione fallita di Git. Ciò significa che ci sono cambiamenti in entrambi i rami che si verificano sulla stessa linea. Qui è dove dovrai risolvere i conflitti manualmente. Puoi scartare l'unione fatta modificando direttamente il file o eseguendo il comando di checkout per la versione nel ramo che vuoi file4diventare.


Infine, non dimenticare di git commit.


10
Attenzione però: se git merge --no-commit branchXè solo un avanzamento veloce, il puntatore verrà aggiornato e --no-commit viene quindi ignorato silenziosamente
cfi

16
@cfi Che ne dici di aggiungere --no-ffper prevenire quel comportamento?
Eduardo Costa,

5
Consiglio vivamente di aggiornare questa risposta con l'opzione "--no-ff" di Eduardo. Ho letto tutto (il che era altrimenti eccezionale) solo per far avanzare rapidamente la mia unione.
Funktr0n,

7
Questa soluzione offre i migliori risultati e flessibilità.
Thiago Macedo,

20
A differenza della risposta con il maggior numero di voti, questa soluzione ha preservato la mia storia di unione, che è importante per me mentre intrico impegni parziali avanti e indietro tra i rami. Non ho provato tutte le altre soluzioni suggerite, quindi forse anche alcune di loro lo fanno.
ws_e_c421,

107

Non mi piacciono gli approcci di cui sopra. L'uso di cherry-pick è ottimo per scegliere una singola modifica, ma è una seccatura se si desidera apportare tutte le modifiche tranne alcune sbagliate. Ecco il mio approccio.

Non ci sono --interactiveargomenti che puoi passare per git merge.

Ecco l'alternativa:

Hai alcune modifiche alla "caratteristica" del ramo e vuoi portarne alcune, ma non tutte, al "padrone" in un modo non sciatto (cioè non vuoi scegliere e impegnare ognuna)

git checkout feature
git checkout -b temp
git rebase -i master

# Above will drop you in an editor and pick the changes you want ala:
pick 7266df7 First change
pick 1b3f7df Another change
pick 5bbf56f Last change

# Rebase b44c147..5bbf56f onto b44c147
#
# Commands:
# pick = use commit
# edit = use commit, but stop for amending
# squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

git checkout master
git pull . temp
git branch -d temp

Quindi basta avvolgerlo in uno script di shell, cambiare master in $ a e cambiare funzionalità in $ da e sei a posto:

#!/bin/bash
# git-interactive-merge
from=$1
to=$2
git checkout $from
git checkout -b ${from}_tmp
git rebase -i $to
# Above will drop you in an editor and pick the changes you want
git checkout $to
git pull . ${from}_tmp
git branch -d ${from}_tmp

Ho sistemato la formattazione - questo è un metodo abbastanza carino, se vuoi fare una selezione di commit
1800 INFORMAZIONI

Sto usando questa tecnica ora e sembra aver funzionato davvero bene.
dylanfm,

4
Si potrebbe desiderare di cambiare git rebase -i $toa git rebase -i $to || $SHELL, in modo che l'utente può chiamare git --skip, ecc, come necessario se il rebase fallisce. Vale anche la pena concatenare le linee insieme al &&posto di newline.
Sircolinton,

2
Sfortunatamente, sembra che il link nella risposta sia morto.
ThomasW,

Non solo il link è morto, ma ha un avviso di cattiva reputazione WOT. Pertanto l'ho rimosso.
Jean-François Corbett,

93

C'è un altro modo di procedere:

git checkout -p

È un mix tra git checkoute git add -pe potrebbe essere esattamente quello che stai cercando:

   -p, --patch
       Interactively select hunks in the difference between the <tree-ish>
       (or the index, if unspecified) and the working tree. The chosen
       hunks are then applied in reverse to the working tree (and if a
       <tree-ish> was specified, the index).

       This means that you can use git checkout -p to selectively discard
       edits from your current working tree. See the “Interactive Mode”
       section of git-add(1) to learn how to operate the --patch mode.

10
Questo è di gran lunga il metodo più semplice e più semplice, a patto che tu abbia solo un numero gestibile di modifiche da unire. Spero che più persone notino questa risposta e la votino. Esempio: git checkout --patch exp1 file_to_merge
Tyler Rick

1
Risposta simile pubblicata su questa domanda: stackoverflow.com/a/11593308/47185
Tyler Rick

Oh, non sapevo che il checkout avesse una patch! Invece ho fatto il checkout / reset / add -p.
Daniel C. Sobral,

2
Veramente il metodo più semplice. git checkout -p featurebranch nomefile. E la cosa migliore è quando il comando viene eseguito, ti dà ay / n / e /? / ... ecc. opzione per decidere come unire il file. Ho provato con e e ho anche potuto modificare la patch prima di applicare ... Che bello. Una vera fodera per unire file selettivi da altri rami.
Infocloggato il

55

Mentre alcune di queste risposte sono piuttosto buone, mi sembra che nessuna abbia effettivamente risposto al vincolo originale di OP: selezionare determinati file da determinati rami. Questa soluzione lo fa, ma può essere noiosa se ci sono molti file.

Diciamo che avere i master, exp1e exp2rami. Si desidera unire un file da ciascuno dei rami sperimentali in master. Vorrei fare qualcosa del genere:

git checkout master
git checkout exp1 path/to/file_a
git checkout exp2 path/to/file_b

# save these files as a stash
git stash
# merge stash with master
git merge stash

Questo ti darà differenze nel file per ciascuno dei file che desideri. Niente di più. Nientemeno. È utile avere cambiamenti di file radicalmente diversi tra le versioni - nel mio caso, cambiare un'app da Rails 2 a Rails 3.

EDIT : questo unirà i file, ma farà una fusione intelligente. Non sono riuscito a capire come usare questo metodo per ottenere informazioni diff nel file (forse lo faranno ancora per differenze estreme. Piccole cose fastidiose come gli spazi bianchi vengono ricongiunte a meno che tu non usi l' -s recursive -X ignore-all-spaceopzione)


4
Nota anche: è possibile eseguire più file da un determinato ramo tutti in linea, ad esempiogit checkout exp1 path/to/file_a path/to/file_x
EMiller

2
Questo è bellissimo. Ho fatto git checkout feature <path>/*per ottenere gruppi di file.
Isherwood,

Funziona bene, ma ha aggiunto due oggetti commit aggiuntivi. Non è un grosso problema ma un po 'disordinato
MightyPork,

@MightyPork hai ragione. Sfortunatamente, da quando l'ho scritto tanto tempo fa, non sono più sicuro del motivo per cui i passaggi "git stash" e "git merge stash" sono presenti al posto di un "git commit".
Eric Hu,

2
Oh, è chiaro, penso. In questo modo unisce un solo file, non necessariamente sovrascrivendo le modifiche precedenti sul ramo di destinazione.
MightyPork,

48

1800 La risposta di INFORMAZIONI è completamente corretta. Come git noob, però, "usa git cherry-pick" non mi è bastato per capirlo senza scavare un po 'di più su Internet, quindi ho pensato di pubblicare una guida più dettagliata nel caso in cui ci fosse qualcuno barca simile.

Il mio caso d'uso era voler selezionare selettivamente le modifiche dal ramo github di qualcun altro nel mio. Se disponi già di una filiale locale con le modifiche, devi solo eseguire i passaggi 2 e 5-7.

  1. Crea (se non creato) una filiale locale con le modifiche che desideri apportare.

    $ git branch mybranch <base branch>

  2. Passaci dentro.

    $ git checkout mybranch

  3. Elimina le modifiche desiderate dall'account dell'altra persona. Se non l'hai già fatto, ti consigliamo di aggiungerli come telecomando.

    $ git remote add repos-w-changes <git url>

  4. Abbassa tutto dal loro ramo.

    $ git pull repos-w-changes branch-i-want

  5. Visualizza i registri di commit per vedere quali modifiche desideri:

    $ git log

  6. Tornare al ramo in cui si desidera eseguire il pull delle modifiche.

    $ git checkout originalbranch

  7. Cherry raccoglie i tuoi impegni, uno per uno, con gli hash.

    $ git cherry-pick -x hash-of-commit

Suggerimento per il cappello: http://www.sourcemage.org/Git_Guide


3
Suggerimento: utilizzare innanzitutto il git cherrycomando (consultare prima il manuale) per identificare i commit non ancora uniti.
Akaihola,

Funziona .. 1. ha creato un nuovo ramo 2.creato alcuni file / ha apportato alcune modifiche 3. commit 4. verifica il ramo principale 5. Esegui git cherry-pick -x hash-of-commit e risolvi i conflitti di unione sei a posto andare.
RamPrasadBismil,

Il tuo link non funziona più. Puoi aggiornarlo per favore?
creep3007,

42

Ecco come è possibile sostituire il Myclass.javafile nel masterramo con Myclass.javanel feature1ramo. Funzionerà anche se Myclass.javanon esiste master.

git checkout master
git checkout feature1 Myclass.java

Nota che questo sovrascriverà - non unirà - e ignorerà piuttosto le modifiche locali nel ramo principale.


6
Questo non si fonderà. Sovrascriverà semplicemente le modifiche sul master con le modifiche dal ramo feature1.
Skunkwaffle,

3
Perfettamente, stavo cercando questo tipo di unione in cui theirssovrascrivi ours=> +1 Cheers;)
olibre

1
Alcune volte tutto ciò che vuoi fare è sostituire l'intero file, quindi questo è quello che volevo, ma devi assicurarti di voler perdere tutte le modifiche apportate a questo file.
MagicLAMP

1
Soluzione più pulita, dato che l'OP ha voluto in particolare sostituire l'intero file con l'equivalente in un altro ramo:2. Manual copying of common files into a temp directory followed by ...copying out of the temp directory into the working tree.
Brent Faust,

29

Il modo semplice, per unire effettivamente file specifici da due rami, non solo sostituire file specifici con quelli di un altro ramo.

Fase 1: diff. I rami

git diff branch_b > my_patch_file.patch

Crea un file patch della differenza tra il ramo corrente e branch_b

Passaggio 2: applicare la patch sui file corrispondenti a un motivo

git apply -p1 --include=pattern/matching/the/path/to/file/or/folder my_patch_file.patch

note utili sulle opzioni

È possibile utilizzare *come carattere jolly nel modello di inclusione.

Non è necessario sfuggire alle barre.

Inoltre, potresti usare --exclude invece e applicarlo a tutto tranne i file che corrispondono allo schema, o invertire la patch con -R

L'opzione -p1 è un blocco dal comando * unix patch e il fatto che il contenuto del file patch anteponga ogni nome di file con a/o b/(o più a seconda di come è stato generato il file patch) che è necessario rimuovere in modo che possa capire il file reale al percorso del file a cui deve essere applicata la patch.

Controlla la pagina man per git-apply per ulteriori opzioni.

Step tre: non c'è step tre

Ovviamente vorresti impegnare le tue modifiche, ma chi può dire che non hai altre modifiche correlate che vuoi fare prima di effettuare il commit.


1
Questo è stato molto utile laddove current_branch ha avuto molte modifiche "aggiuntive" che dovevano essere preservate. Ottenuto il diff delle sole modifiche apportate da branch_b come: git diff HEAD ... branch_b (sì - tre periodi fa il trucco magico).
Saad Malik,

@masukomi, Al passaggio 2, non dovresti aggiungere il file patch creato nel passaggio 1 come argomento?
Spiralis,

Per me, tutte le modifiche sono state respinte. Qualche idea sul perché?
LinusGeffarth,

pensiero iniziale @LinusGeffarth è che forse hai ottenuto i rami all'indietro quando hai fatto la patch? seguirà al di fuori di SO per vedere se riusciamo a capirlo.
Masukomi,

24

Ecco come puoi fare in modo che la cronologia segua solo un paio di file da un altro ramo con un minimo sforzo, anche se una fusione più "semplice" avrebbe portato molte più modifiche che non desideri.

Innanzitutto, prenderai il passo insolito di dichiarare in anticipo che ciò che stai per commettere è un'unione, senza che git faccia nulla ai file nella tua directory di lavoro:

git merge --no-ff --no-commit -s ours branchname1

. . . dove "branchname" è qualunque cosa tu pretenda di fondere. Se dovessi impegnarti subito, non farebbe alcun cambiamento ma mostrerebbe comunque origini da parte dell'altro ramo. È possibile aggiungere più rami / tag / ecc. alla riga di comando, se necessario, anche. A questo punto, tuttavia, non ci sono modifiche da eseguire, quindi procurati i file dalle altre revisioni.

git checkout branchname1 -- file1 file2 etc

Se ti stai unendo da più di un altro ramo, ripeti se necessario.

git checkout branchname2 -- file3 file4 etc

Ora i file dell'altro ramo sono nell'indice, pronti per il commit, con cronologia.

git commit

e avrai molte spiegazioni da fare in quel messaggio di commit.

Si prega di notare, tuttavia, nel caso in cui non fosse chiaro, che è una cosa sbagliata da fare. Non è nello spirito a cosa serve un "ramo", e la scelta della ciliegia è un modo più onesto per fare ciò che faresti, qui. Se vuoi fare un'altra "unione" per altri file sullo stesso ramo che non hai portato l'ultima volta, ti fermerà con un messaggio "già aggiornato". È un sintomo di non ramificarsi quando dovremmo avere, nel ramo "da" dovrebbe esserci più di un ramo diverso.


3
Il tuo primo comando ( git merge --no-ff --no-commit -s outs branchname1) è esattamente quello che stavo cercando! Grazie!
RobM,

1
Con più rami, la cronologia richiesta, è necessario unire i singoli file e modificare il contenuto del file prima di eseguire il push, questa sembra essere un'alternativa decente. Ad esempio dev => master, ma si desidera modificare la definizione di host o simile prima di passare a master.
timss

15

So di essere un po 'in ritardo, ma questo è il mio flusso di lavoro per unire file selettivi.

#make a new branch ( this will be temporary)
git checkout -b newbranch
# grab the changes 
git merge --no-commit  featurebranch
# unstage those changes
git reset HEAD
(you can now see the files from the merge are unstaged)
# now you can chose which files are to be merged.
git add -p
# remember to "git add" any new files you wish to keep
git commit

Ho usato una leggera variazione su questo. Invece di fondermi, ho scelto la ciliegia. Fa il lavoro. L'unico aspetto negativo di questo approccio è la perdita del riferimento all'hash di commit originale.
Matt Florence,

15

Il modo più semplice è impostare il repository sul ramo con cui si desidera unire, quindi eseguire,

git checkout [branch with file] [path to file you would like to merge]

Se corri

git status

vedrai il file già in scena ...

Quindi corri

git commit -m "Merge changes on '[branch]' to [file]"

Semplice.


3
Questa è quasi la migliore risposta che ho trovato. si prega di consultare jasonrudolph.com/blog/2009/02/25/… Così chiaro, conciso e funziona!
superuseroi,

1
che sostituirà completamente il contenuto dei file dal ramo di origine anziché unirli
Amare,

Stavo per rispondere in questo modo, ho pensato di inventare cose nuove a cui non è stata ancora data risposta! Ma questo è il modo più semplice per farlo. Questo dovrebbe essere al top!
Irfandy Jip,

15

Ho trovato questo post per contenere la risposta più semplice. Fai semplicemente:

$ #git checkout <branch from which you want files> <file paths>

Esempio:

$ #pulling .gitignore file from branchB into current branch
$ git checkout branchB .gitignore

Vedi il post per maggiori informazioni.


3
Questo non si fonde davvero, sovrascrive il file sul ramo corrente.
Igor Ralic,

1
@igrali Questo è un commento utile, ma rispetto alla difficoltà dei modi "corretti" per farlo, questa è una buona soluzione. Bisogna solo stare molto attenti.
owensmartin,

12

È strano che Git non abbia ancora uno strumento così conveniente "pronto all'uso". Lo uso pesantemente quando aggiorno un ramo della vecchia versione (che ha ancora molti utenti software) solo con alcune correzioni del ramo della versione corrente. In questo caso è spesso necessario ottenere rapidamente solo alcune righe di codice dal file nel trunk, ignorando molte altre modifiche (che non dovrebbero andare nella vecchia versione) ... E ovviamente l' unione interattiva a tre vie è necessario in questo caso, git checkout --patch <branch> <file path>non è utilizzabile per questo scopo di unione selettiva.

Puoi farlo facilmente:

Aggiungi questa riga alla [alias]sezione del tuo file globale .gitconfigo locale .git/config:

[alias]
    mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; /C/BCompare3/BCompare.exe $2.theirs $2 $2.base $2; rm -f $2.theirs; rm -f $2.base;' -"

Implica che usi Beyond Compare. Passa al software che preferisci, se necessario. Oppure puoi cambiarlo in unione automatica a tre vie se non hai bisogno della fusione selettiva interattiva:

[alias]
    mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; git merge-file $2 $2.base $2.theirs; rm -f $2.theirs; rm -f $2.base;' -"

Quindi utilizzare in questo modo:

git mergetool-file <source branch> <file path>

Questo ti darà la vera opportunità di unione selettiva per alberi di qualsiasi file in un altro ramo.


10

Non è esattamente quello che stavi cercando, ma mi è stato utile:

git checkout -p <branch> -- <paths> ...

È un mix di alcune risposte.


2
Questo è davvero utile e potrebbe essere aggiunto a quella che per me è la migliore risposta, la risposta di @ alvinabad. Quando si esegue: git checkout HEAD file1per mantenere la versione corrente e separare il file file1, è possibile utilizzare l' -popzione per selezionare parte del file da unire. grazie per il trucco!
Simon C.

Questa è la mia risposta preferita. Semplice, al punto e funziona
Jesse Reza Khorasanee l'

8

Vorrei fare un

git diff commit1..commit2 filepattern | git-apply --index && git commit

In questo modo è possibile limitare l'intervallo di commit per un filepattern da un ramo.

Rubato da: http://www.gelato.unsw.edu.au/archives/git/0701/37964.html


In alcune situazioni, questo potrebbe essere molto utile. Tuttavia, se le modifiche si trovano in un ramo diverso, puoi semplicemente effettuare il checkout dalla punta di quel ramo, come nella risposta di Bart J sopra.
cdunn2001,

Chi è Bart Js?
Nero,

8

Ho avuto lo stesso identico problema menzionato sopra da te. Ma ho trovato questo blog git più chiaro nello spiegare la risposta.

Comando dal link sopra:

#You are in the branch you want to merge to
git checkout <branch_you_want_to_merge_from> <file_paths...>

hai provato questo? sono sicuro che i file verranno sostituiti da <branch_you_want_to_merge_from> anziché essere uniti
Amare,

7

Mi piace la risposta "git-interactive-merge", sopra, ma ce n'è una più semplice. Lascia che git faccia questo per te usando una combinazione rebase di interattivo e su:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o master

Quindi il caso è che tu voglia C1 e C2 dal ramo 'feature' (punto di diramazione 'A'), ma per il momento niente del resto.

# git branch temp feature
# git checkout master
# git rebase -i --onto HEAD A temp

Che, come sopra, ti porta all'editor interattivo in cui selezioni le righe 'pick' per C1 e C2 (come sopra). Salva ed esci, e poi procederà con il rebase e ti darà il ramo 'temp' e anche HEAD al master + C1 + C2:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o-master--C1---C2 [HEAD, temp]

Quindi puoi semplicemente aggiornare master su HEAD ed eliminare il ramo temporaneo e sei a posto:

# git branch -f master HEAD
# git branch -d temp

7

Che dire git reset --soft branch? Sono sorpreso che nessuno lo abbia ancora menzionato.

Per me, è il modo più semplice per selezionare selettivamente le modifiche da un altro ramo, poiché questo comando inserisce nel mio albero di lavoro tutte le modifiche alla diff e posso facilmente selezionare o ripristinare quella di cui ho bisogno. In questo modo, ho il pieno controllo dei file impegnati.


6

So che questa domanda è vecchia e ci sono molte altre risposte, ma ho scritto il mio script chiamato 'pmerge' per unire parzialmente le directory. È un work in progress e sto ancora imparando sia script git che bash.

Questo comando utilizza git merge --no-commite quindi non applica le modifiche che non corrispondono al percorso fornito.

Utilizzo: git pmerge branch path
Esempio:git merge develop src/

Non l'ho testato ampiamente. La directory di lavoro deve essere libera da qualsiasi modifica senza commit e file non tracciati.

#!/bin/bash

E_BADARGS=65

if [ $# -ne 2 ]
then
    echo "Usage: `basename $0` branch path"
    exit $E_BADARGS
fi

git merge $1 --no-commit
IFS=$'\n'
# list of changes due to merge | replace nulls w newlines | strip lines to just filenames | ensure lines are unique
for f in $(git status --porcelain -z -uno | tr '\000' '\n' | sed -e 's/^[[:graph:]][[:space:]]\{1,\}//' | uniq); do
    [[ $f == $2* ]] && continue
    if git reset $f >/dev/null 2>&1; then
        # reset failed... file was previously unversioned
        echo Deleting $f
        rm $f
    else
        echo Reverting $f
        git checkout -- $f >/dev/null 2>&1
    fi
done
unset IFS

4

È possibile utilizzare read-treeper leggere o unire l'albero remoto specificato nell'indice corrente, ad esempio:

git remote add foo git@example.com/foo.git
git fetch foo
git read-tree --prefix=my-folder/ -u foo/master:trunk/their-folder

Per eseguire l'unione, utilizzare -minvece.

Vedi anche: Come unire una sottodirectory in git?


3

Un approccio semplice per fusione / commit selettivi per file:

git checkout dstBranch git merge srcBranch // make changes, including resolving conflicts to single files git add singleFile1 singleFile2 git commit -m "message specific to a few files" git reset --hard # blow away uncommitted changes


3

Se non hai troppi file che sono stati modificati, questo ti lascerà senza ulteriori commit.

1. Ramo duplicato temporaneamente
$ git checkout -b temp_branch

2. Ripristina l'ultimo commit desiderato
$ git reset --hard HEAD~n , dove si ntrova il numero di commit necessari per tornare indietro

3. Controlla ogni file dal ramo originale
$ git checkout origin/original_branch filename.ext

Ora puoi eseguire il commit e forzare il push (per sovrascrivere il telecomando), se necessario.


3

Se avete solo bisogno di unire una directory e congedo particolare tutto il resto intatto e tuttavia conservare la storia, si potrebbe forse provare questo ... creare una nuova target-branchvia della masterprima di sperimentare.

I passaggi seguenti presuppongono che tu abbia due rami target-branche source-branchche la directory dir-to-mergeche desideri unire sia in source-branch. Supponi anche di avere altre directory come dir-to-retainnel target che non desideri modificare e conservare la cronologia. Inoltre, presuppone che ci siano conflitti di unione nel file dir-to-merge.

git checkout target-branch
git merge --no-ff --no-commit -X theirs source-branch
# the option "-X theirs", will pick theirs when there is a conflict. 
# the options "--no--ff --no-commit" prevent a commit after a merge, and give you an opportunity to fix other directories you want to retain, before you commit this merge.

# the above, would have messed up the other directories that you want to retain.
# so you need to reset them for every directory that you want to retain.
git reset HEAD dir-to-retain
# verify everything and commit.

2

Quando sono cambiati solo pochi file tra i commit correnti dei due rami, unisco manualmente le modifiche passando attraverso i diversi file.

git difftoll <branch-1>..<branch-2>

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.