2012-03-19 2 views
17

Ho due rami: trunk, produzione. Ho trovato un problema nel bagagliaio, ho risolto il problema e l'ho eseguito, l'ho spinto. Ora è stato testato e ho bisogno di unire le modifiche nel ramo di produzione come hot-fix. Provo a usare il cherry-pick. Tuttavia non funziona perché un file modificato nella correzione è stato rinominato nel trunk in precedenza durante alcuni refactoring che non voglio portare in produzione.Il backport cambia dal file rinominato

Non voglio unire tutto, ma prendere solo questo commesso. Il cherry pick fallisce con il conflitto "cancellato da noi" (ovviamente, il nuovo file non è mai esistito nemmeno nel ramo di produzione).

Qual è il modo corretto per portare i cambiamenti nel vecchio file?

risposta

11

userei buon vecchio patch per questo:

git show COMMIT_ID -- old/file/name.txt | patch new/file/name.txt 
+1

Sì, funziona per me. Tuttavia sarebbe interessante trovare una soluzione migliore, soprattutto nel caso in cui ci siano diversi file rinominati. – kan

0

Questa è una specie di complicato. Ad esempio, è possibile creare una patch da una diff e applicarla al vecchio file. Ma in futuro per prevenire questi problemi, consiglierei di fare delle correzioni sul ramo di produzione e testarlo prima lì, quindi unire dalla produzione al tronco.

+0

Sì, ho capito che, tuttavia, non è sempre possibile prevedere cosa andrà in hot-fix. Sto provando a farlo tramite 'format-patch' /' apply-patch', ma non fa nulla (nessun errore, nessuna modifica). Si prega di dare un suggerimento di modo corretto per usarlo. – kan

+2

Controlla la sezione "Rinomina la gestione in git" in questo post http://blogs.atlassian.com/2011/10/confluence_git_rename_merge_oh_my/ – ralphtheninja

0

provo lo stesso problema e hanno cercato di trovare una soluzione.

I risolto utilizzando una sequenza di rebases. Non ho fatto ulteriori test di questi, quindi usali a tuo rischio!

Se vi trovate interessati hanno un'occhiata su github:

https://github.com/fraschfn/cherry-pick

2

Per ciliegio raccogliere modifiche a qualsiasi numero di file, in caso di una directory rinominare tra i rami:

git diff ... | sed -e 's|<old dir>|<new dir>|' | git apply - 
14

Se:

  • Ti aspettavi/speravano che Git rileverebbe la mossa o rinominare di il file sul tronco, ma non lo ha fatto, e
  • Il repository ha un numero ragionevole di file

... allora dovete assolutamente pensare di cambiare la vostra configurazione git come questo:

$ git config merge.renameLimit 999999 

E 'possibile che durante un mergge/cherry-pick, git stia colpendo il limite di controllo del file predefinito (penso che sia 400 o 1000 o qualcosa del genere) prima che sia in grado di localizzare la giusta corrispondenza di rename. Il superamento di questo limite può causare l'operazione di unire/cherry-pick su un tempo più lungo durante la ricerca del file rinominato, ma può aiutare a evitare le sfide di unione "eliminate da noi".

Questo dovrebbe fare il trucco, ma se il file rinominato era piccolo e le modifiche tra i rami sono significative, potresti anche giocare con l'impostazione -X rename-threshold, ad es. abbassandolo dal 50% predefinito con -X rename-threshold=25%.

+2

Una nota su 'rename-threshold' per altri utenti - questo include tutte le modifiche nel tempo. Ho avuto un'unione in cui il rinominare originale ha cambiato due righe nel file, ma dal momento che era stato pesantemente modificato * dopo * quel punto, git non ha ancora rilevato la similarità del file senza abbassare 'rename-threshold'. – zebediah49

3

Di fronte lo stesso problema, ho chiesto a un collega che cosa avrebbe fatto, e la sua risposta immediata è stata:

git checkout production 

git mv production-filename trunk-filename && git commit -m "Just fooling git" 
git cherry-pick trunk-commit 
git mv trunk-filename production-filename && git commit -m "Undo the damage" 

# Now squash the 3 commits 
git rebase -i HEAD~3 

funzionato come un fascino per me.

+2

Questo ha funzionato da me. Ho usato una GUI, ma ho seguito gli stessi passi. Mi ci è voluto un minuto per capire cosa stia facendo, quindi elencherò i passaggi: 1. Rinominare file/directory sul ramo di destinazione in modo che corrispondano al ramo di origine e al commit. 2. Cherrypick la modifica dal ramo di origine al ramo di destinazione. 3. Rinominare file/directory sul ramo di destinazione come erano originariamente e commettere. 4. Squash quei 3 commit in un commit. – Tolli

0

Ho creato uno script di shell che tenta di eseguire un cherry-pick mentre indovina le mosse di file (non funziona se hai rinominato il file stesso, solo se lo hai spostato in un'altra cartella): Tuttavia: attualmente lo farà fallire se il commit aggiunge nuovi file o se rinominare i file.

#!/bin/bash 
# 
# Attemps to guess file moves (rename of folders) when cherry-pick'ing. 
# Gaspard van Koningsveld 
# 
[ "$1" == "" ] && echo "usage: $0 <commit-hash-to-cherry-pick>" && exit 1 
TMP_PATCH_FILE="temp-cherry-pick-patch" 
function abort() { 
    echo "Aborting" 
    "rm" -f "$TMP_PATCH_FILE" 
    exit 1 
} 
function main() { 
    echo "Retreiving commit patch..." 
    "git" show "$1" > "$TMP_PATCH_FILE" || abort 

    echo "Matching renamed files..." 
    sedcmds="" 
    for oldfile in $("grep" -E '(--- a|\+\+\+ b)' "$TMP_PATCH_FILE" | "cut" -c 7- | "sort" | "uniq"); do 
    [ -f "$oldfile" ] && continue 
    renamefound=0 
    oldfilepart="$oldfile" 
    while [ $renamefound -eq 0 ]; do 
     possiblefiles=$("git" ls-files "**/$oldfilepart") 
     if [ "$possiblefiles" != "" ]; then 
     if [ $("wc" -l <<< "$possiblefiles") == "1" ]; then 
      echo " $oldfile > $possiblefiles" 
      sedcmds="$sedcmds s|/$oldfile|/$possiblefiles|g;" 
      break 
     else 
      echo " ERROR: More than one rename possibility found for file $oldfile:" 
      echo "$possiblefiles" 
      abort 
     fi 
     fi 
     prevoldfilepart="$oldfilepart" 
     oldfilepart="${oldfilepart#*/}" 
     if [ "$prevoldfilepart" == "$oldfilepart" ]; then 
     echo " ERROR: Could not find rename for $oldfile." 
     abort 
     fi 
    done 
    done 
    echo "Renaming files in patch..." 
    "sed" -i "$sedcmds" "$TMP_PATCH_FILE" || abort 
    echo "Applying patch as new commit..." 
    "sed" -i "s/^commit /From commit /;s/^Author: /From: /" "$TMP_PATCH_FILE" || abort 
    "git" am -3 "$TMP_PATCH_FILE" 

    "rm" -f "$TMP_PATCH_FILE" 
} 
main "[email protected]"