2009-11-19 15 views
30

Se si desidera spostare il HEAD al genitore della corrente HEAD, questo è facile:Riferimento il figlio di un commit in Git

git reset --hard HEAD^ 

Ma esiste un modo semplice per fare l'esatto contrario di questa operazione , cioè, imposta la testa sul primo commit figlio della testa corrente?

In questo momento, uso gitk come soluzione alternativa (alt-tab, freccia su, alt-tab, tasto centrale), ma vorrei una soluzione più elegante, che possa essere utilizzata anche quando gitk non è a disposizione.

+0

puoi anche consultare i script/comando ['git children-of'] (http://stackoverflow.com/a/20141722/6309)! – VonC

+0

C'è una buona soluzione su http://stackoverflow.com/questions/2263674/how-do-i-find-the-next-commit-in-git –

risposta

16

Molto probabilmente non è la soluzione più veloce possibile, ma fa quello che mi serve:

 
#!/bin/bash 

REV=$1 

if [ x$REV == x ]; then 
    echo "Usage: git-get-child []" 
    exit 
fi 

HASH=$(git-rev-parse $REV) 

NUM=$2 

if [ x$NUM == x ]; then 
    NUM=1 
fi 

git rev-list --all --parents | grep " $HASH" | sed -n "${NUM}s/\([^ ]*\) .*$/\\1/p" 

Il git rev-list --all --parents fa esattamente quello che mi serve: si itera su tutti i commit raggiungibili, e stampa la seguente riga per ogni:

SHA1_commit SHA1_parent1 SHA1_parent2 ecc

lo spazio nell'espressione grep garantisce che solo le linee si trovano dove lo SHA1 in questione è un genitore. Quindi otteniamo l'ennesima riga per l'ennesimo figlio e otteniamo SHA1 del bambino.

+0

Credo che dovrebbe essere 'git rev-list --all - i genitori | grep -m 1 -B $ (($ NUM-1)) "$ HASH" | capo -1 | sed 's /. * //' else, altrimenti non funziona quando $ NUM! = 1 – Schwern

+2

Vedi sotto per un [miglioramento delle prestazioni] potenzialmente significativo (http://stackoverflow.com/questions/1761825/referencing-the -child-of-a-commit-in-git/5353204 # 5353204) – MatrixFrog

+0

Si noti che questo * non * trova pegni penzolanti, vale a dire i commit non più raggiungibili da alcun ramo. 'git reset --hard HEAD ^' tipicamente rende l'HEAD commit irraggiungibile (a meno che non si trovi anche su un ramo). Quindi questo potrebbe non trovare tutti i commit. Per trovare tutti i commit, è necessario utilizzare 'git rev-list --walk-reflog' e/o' git fsck --unreachable'. – sleske

3

È possibile utilizzare gitk ... poiché ci possono essere più di un bambino, probabilmente non esiste un modo semplice come HEAD^.

Se si desidera annullare l'intera operazione, è possibile utilizzare anche il reflog. Utilizzare git reflog per trovare il puntatore del commit, che è possibile utilizzare per il comando reset. Vedi here.

0

Non è strettamente possibile dare una buona risposta - dato che git è distribuito, la maggior parte dei figli del commit che chiedi potrebbero trovarsi in repository che non hai sul tuo computer locale! Questa è ovviamente una risposta stupida, ma qualcosa su cui riflettere. Git implementa raramente operazioni che non può implementare correttamente.

+11

Ovviamente ho bisogno solo dei bambini dal repository attuale. – AttishOculus

2

Dipende da cosa stai chiedendo. Potrebbe esserci un numero infinito di figli del capo attuale in un numero infinito di rami, alcuni locali, alcuni remoti e molti che sono stati ribaltati e si trovano nel tuo repository, ma non fanno parte di una storia che intendi pubblicare.

Per un caso semplice, se è stato appena effettuato il ripristino su HEAD^, è possibile riprendere il bambino che ha appena gettato via come [email protected]{1}.

12

Il metodo sopra riportato con git rev-list --all considera tutti i commit disponibili, che possono essere molto e spesso non necessari. Se i commit bambino interessanti sono raggiungibili da qualche ramo, il numero di commit che uno script interessato a bambino impegna deve elaborare possono essere ridotti:

branches=$(git branch --contains $commit) 

determinerà l'insieme dei rami che $ commettono è un antenato di.

Utilizzando questo set, git rev-list --parents ^$commit $branches dovrebbe restituire esattamente l'insieme di tutte le relazioni padre-figlio tra $ commit e tutte le diramazioni di cui è un antenato.

+0

Questo ha funzionato per me, tuttavia ho dovuto filtrare la riga '* (nessun ramo)', come ero a quel punto su un ramo distaccato. Per il mio deposito questo era circa 10 volte più veloce, 0,13 s contro 1,4 secondi. –

2

Sulla base della risposta fornita in How do I find the next commit in git?, ho un'altra soluzione che funziona per me.

Supponendo che si desidera trovare nella prossima revisione sul ramo "master", allora si può fare:

git log --reverse ${commit}..master | sed 's/commit //; q' 

Ciò presuppone anche che c'è uno prossima revisione, ma che è una specie di assunta dalla domanda comunque.

1

È possibile utilizzare il succo del creatore per Hudson (ora Jenkins) Kohsuke Kawaguchi (novembre 2013):
kohsuke/git-children-of:

Dato un commit, trovare bambini immediati di che commettono.

#!/bin/bash -e 
# given a commit, find immediate children of that commit. 
for arg in "[email protected]"; do 
    for commit in $(git rev-parse $arg^0); do 
    for child in $(git log --format='%H %P' --all | grep -F " $commit" | cut -f1 -d' '); do 
     git describe $child 
    done 
    done 
done 

Metti che lo script in una cartella a cui fa riferimento la tua $PATH, e semplicemente digitare:

git children-of <a-commit> 
4

basata in parte su Paul Wagland's answer e in parte su his source, io sono using the following:

git log --ancestry-path --format=%H ${commit}..master | tail -1 

Ho scoperto che la risposta di Paul mi ha dato l'output sbagliato per i commit più vecchi (forse a causa della fusione?), dove la differenza principale è il flag --ancestry-path.

+0

Usa '--reverse' e ​​usa' head -1' per un leggero aumento di velocità –

+0

Vedi [mia risposta] (http://stackoverflow.com/a/39558576/5353461) per una soluzione più generica –

3

Per spostare solo la testa (come richiesto - questa operazione non aggiorna l'indice o albero di lavoro), l'uso:

git reset --soft $(git child)

Avrete bisogno di utilizzare la configurazione di seguito elencati.

Spiegazione

Sulla base di @Michael's answer, ho inciso l'alias child nel mio .gitconfig.

Funziona come previsto nel caso predefinito ed è anche versatile.

# Get the child commit of the current commit. 
# Use $1 instead of 'HEAD' if given. Use $2 instead of curent branch if given. 
child = "!bash -c 'git log --format=%H --reverse --ancestry-path ${1:-HEAD}..${2:\"$(git rev-parse --abbrev-ref HEAD)\"} | head -1' -" 

Il valore predefinito dando al bambino di HEAD (argomento meno che un altro commit-ish è dato) seguendo la discendenza un passo verso la punta del ramo corrente (meno che un altro commit-ish è dato come secondo argomento) .

Utilizzare %h anziché %H se si desidera il modulo di hash breve.

Con una testa staccata, non c'è ramo, ma ricevendo il primo figlio può ancora essere raggiunto con questo alias:

# For the current (or specified) commit-ish, get the all children, print the first child 
children = "!bash -c 'c=${1:-HEAD}; set -- $(git rev-list --all --not \"$c\"^@ --children | grep $(git rev-parse \"$c\")); shift; echo $1' -" 

Modificare il $1 per $* per stampare tutti i bambini

+0

Grazie - I l'ho risolto –

Problemi correlati