Domanda

Ho il seguente layout del repository:

  • ramo principale (produzione)
  • integrazione
  • lavorando

Ciò che voglio ottenere è selezionare una serie di commit dal ramo di lavoro e unirli al ramo di integrazione.Sono abbastanza nuovo in Git e non riesco a capire come farlo esattamente (la scelta degli intervalli di commit in un'unica operazione, non la fusione) senza incasinare il repository.Qualche indicazione o pensiero su questo?Grazie!

È stato utile?

Soluzione

Quando si tratta di una serie di commit, la scelta migliore È era non pratico.

COME menzionato sotto di Keith Kim, Git 1.7.2+ ha introdotto la possibilità di selezionare una serie di commit (ma è comunque necessario essere consapevoli delle conseguenza della scelta selettiva per future fusioni)

git cherry-pick" ha imparato a scegliere una serie di commit
(per esempio."cherry-pick A..B" E "cherry-pick --stdin"), così feci "git revert";questi non supportano il controllo sequenziale più bello "rebase [-i]"ha, però.

Damiano Commenti e ci avverte:

Nel "cherry-pick A..B" modulo, A dovrebbe essere più vecchio di B.
Se sono nell'ordine sbagliato, il comando fallirà silenziosamente.

Se vuoi scegliere il allineare B Attraverso D (compreso) sarebbe B^..D.
Vedere "Git crea un ramo dalla gamma di commit precedenti?" Come un'illustrazione.

COME Jubob menziona nei commenti:

Ciò presuppone questo B non è un commit root;riceverai un "unknown revision" errore altrimenti.

Nota:a partire da Git 2.9.x/2.10 (terzo trimestre 2016), puoi scegliere una serie di commit direttamente su un ramo orfano (testa vuota):Vedere "Come rendere orfano un ramo esistente in Git".


Risposta originale (gennaio 2010)

UN rebase --onto sarebbe meglio, dove riproduci l'intervallo dato di commit sopra il tuo ramo di integrazione, come Charles Bailey descritto qui.
(inoltre, cerca "Ecco come trapiantare un ramo di un argomento basato su un ramo su un altro" nel file Pagina man di git rebase, per vedere un esempio pratico di git rebase --onto)

Se la tua filiale attuale è l'integrazione:

# Checkout a new temporary branch at the current location
git checkout -b tmp

# Move the integration branch to the head of the new patchset
git branch -f integration last_SHA-1_of_working_branch_range

# Rebase the patchset onto tmp, the old location of integration
git rebase --onto tmp first_SHA-1_of_working_branch_range~1 integration

Ciò riprodurrà tutto tra:

  • dopo il genitore di first_SHA-1_of_working_branch_range (quindi il ~1):il primo commit che desideri riprodurre
  • fino a "integration" (che punta all'ultimo commit che desideri riprodurre, dal file working ramo)

A "tmp" (che indica dove integration indicavo prima)

Se si verifica un conflitto quando uno di questi commit viene riprodotto:

  • o risolvilo ed esegui "git rebase --continue".
  • oppure salta questa patch ed esegui invece "git rebase --skip"
  • oppure annulla tutto con un "git rebase --abort" (e rimettere a posto il integration ramo sul tmp ramo)

Dopo di che rebase --onto, integration tornerà all'ultimo commit del ramo di integrazione (ovvero "tmp"ramo + tutti i commit riprodotti)

Con la raccolta delle ciliegie o rebase --onto, non dimenticare che ha conseguenze sulle fusioni successive, come descritto qui.


Un puro"cherry-pick"la soluzione è discusso qui, e implicherebbe qualcosa del tipo:

Se vuoi utilizzare un approccio patch allora "git format-patch|git am" e "git cherry" sono le tue opzioni.
Attualmente, git cherry-pick accetta solo un singolo commit, ma se vuoi scegliere l'intervallo B Attraverso D sarebbe B^..D nel gergo di Git, quindi

git rev-list --reverse --topo-order B^..D | while read rev 
do 
  git cherry-pick $rev || break 
done 

Ma in ogni caso, quando hai bisogno di "rigiocare" una serie di commit, la parola "rigiocare" dovrebbe spingerti a usare il comando "rebase" caratteristica di Git.

Altri suggerimenti

A partire dal git prelievo v1.7.2 ciliegia può accettare una serie di commit:

  

git cherry-pick imparato a prendere una serie di commit (ad esempio cherry-pick A..B e cherry-pick --stdin), così ha fatto git revert; questi non supportare il più bello rebase [-i] controllo di sequenziamento ha, però.

Supponiamo di avere 2 rami,

"branchA": comprende impegna da copiare (da "commitA" a "commitB"

"branchB": il ramo si desidera che le impegna a essere trasferiti da "branchA"

1)

 git checkout <branchA>

2) ottenere gli ID di "commitA" e "commitB"

3)

git checkout <branchB>

4)

git cherry-pick <commitA>^..<commitB>

5) Nel caso in cui si dispone di un conflitto, risolverlo e il tipo

git cherry-pick --continue

per continuare il processo di cherry-pick.

Sei sicuro che non vuoi fondere in realtà i rami? Se il ramo di lavoro ha alcuni commit recenti che non si desidera, si può semplicemente creare un nuovo ramo con una testa in corrispondenza del punto che si desidera.

Ora, se davvero si vuole cherry-pick una serie di commit, per qualsiasi motivo, un modo elegante per farlo è quello di tirare solo di un patchset e applicarlo al nuovo ramo di integrazione:

git format-patch A..B
git checkout integration
git am *.patch

Questo è essenzialmente ciò che git-rebase sta facendo in ogni caso, ma senza la necessità di giocare. È possibile aggiungere --3way a git-am se è necessario unire. Assicurarsi che non vi siano altri file .patch * già nella directory in cui si esegue questa operazione, se si seguono le istruzioni alla lettera ...

ho avvolto di VonC codice in uno script bash breve, git-multi-cherry-pick, per una facile esecuzione:

#!/bin/bash

if [ -z $1 ]; then
    echo "Equivalent to running git-cherry-pick on each of the commits in the range specified.";
    echo "";
    echo "Usage:  $0 start^..end";
    echo "";
    exit 1;
fi

git rev-list --reverse --topo-order $1 | while read rev 
do 
  git cherry-pick $rev || break 
done 

Attualmente sto usando questo come ricostruisco la storia di un progetto che ha avuto sia il codice 3rd-party e le personalizzazioni mescolati insieme nello stesso tronco svn. Ora sto dividendo a parte codice di base 3a parte, i moduli 3rd party, e le personalizzazioni sui loro propri rami Git per una migliore comprensione di personalizzazioni per il futuro. git-cherry-pick è utile in questa situazione dal momento che ho due alberi nello stesso repository, ma senza un antenato comune.

Tutte le opzioni di cui sopra vi chiederà di risolvere i conflitti di fusione. Se si uniscono i cambiamenti registrati per una squadra, è difficile avere risolto i conflitti di unione da parte degli sviluppatori e procedere. Tuttavia, "git merge" farà la fusione in un solo colpo, ma non si può passare una serie di revisioni come argomento. dobbiamo usare "git diff" e "git apply" comandi per fare la gamma fusione di giri. Ho osservato che "git apply" avrà esito negativo se il file patch è diff per troppi di file, quindi dobbiamo creare una patch per file e quindi applicare. Si noti che lo script non sarà in grado di eliminare i file che vengono eliminati nel ramo fonte. Questo è un caso raro, è possibile eliminare manualmente questi file dal ramo di destinazione. Lo stato di uscita di "git apply" non è zero se non è in grado di applicare la patch, se si utilizza l'opzione -3way cadrà di nuovo a 3 vie di unione e non devono preoccuparsi di questo fallimento.

Di seguito è riportato lo script.

enter code here



  #!/bin/bash

    # This script will merge the diff between two git revisions to checked out branch
    # Make sure to cd to git source area and checkout the target branch
    # Make sure that checked out branch is clean run "git reset --hard HEAD"


    START=$1
    END=$2

    echo Start version: $START
    echo End version: $END

    mkdir -p ~/temp
    echo > /tmp/status
    #get files
    git --no-pager  diff  --name-only ${START}..${END} > ~/temp/files
    echo > ~/temp/error.log
    # merge every file
    for file in `cat  ~/temp/files`
    do
      git --no-pager diff --binary ${START}..${END} $file > ~/temp/git-diff
      if [ $? -ne 0 ]
      then
#      Diff usually fail if the file got deleted 
        echo Skipping the merge: git diff command failed for $file >> ~/temp/error.log
        echo Skipping the merge: git diff command failed for $file
        echo "STATUS: FAILED $file" >>  /tmp/status
        echo "STATUS: FAILED $file"
    # skip the merge for this file and continue the merge for others
        rm -f ~/temp/git-diff
        continue
      fi

      git apply  --ignore-space-change --ignore-whitespace  --3way --allow-binary-replacement ~/temp/git-diff

      if [ $? -ne 0 ]
       then
#  apply failed, but it will fall back to 3-way merge, you can ignore this failure
         echo "git apply command filed for $file"
       fi
       echo
       STATUS=`git status -s $file`


       if [ ! "$STATUS" ]
       then
#   status is null if the merged diffs are already present in the target file
         echo "STATUS:NOT_MERGED $file"
         echo "STATUS: NOT_MERGED $file$"  >>  /tmp/status
       else
#     3 way merge is successful
         echo STATUS: $STATUS
         echo "STATUS: $STATUS"  >>  /tmp/status
       fi
    done

    echo GIT merge failed for below listed files

    cat ~/temp/error.log

    echo "Git merge status per file is available in /tmp/status"

Un'altra opzione potrebbe essere quella di fondersi con la nostra strategia per il commit prima del range e poi un 'normale' fondersi con l'ultimo commit di tale intervallo (o ramo quando è l'ultima). Quindi supponiamo solo 2345 e 3456 commit del maestro da unire in ramo della funzione:

master:
1234
2345
3456
4567

nel ramo di caratteristica:

git merge -s ours 4567
git merge 2345
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top