Trova commit di unione che include un commit specifico


183

Immagina la seguente storia:

       c---e---g--- feature
      /         \
-a---b---d---f---h--- master

Come posso trovare quando il commit "c" è stato unito al master (ovvero, trova il comando di unione "h")?

Risposte:


158

Il tuo esempio mostra che il ramo featureè ancora disponibile.

In tal caso hè l'ultimo risultato di:

git log master ^feature --ancestry-path

Se il ramo featurenon è più disponibile, è possibile mostrare i commit di unione nella riga della cronologia tra ce master:

git log <SHA-1_for_c>..master --ancestry-path --merges

Questo mostrerà comunque anche tutte le fusioni avvenute dopo h, e tra ee gavanti feature.


Confronto del risultato dei seguenti comandi:

git rev-list <SHA-1_for_c>..master --ancestry-path

git rev-list <SHA-1_for_c>..master --first-parent

ti darà lo SHA-1 di hcome l'ultima riga in comune.

Se lo hai disponibile, puoi utilizzare comm -1 -2questi risultati. Se sei su msysgit, puoi usare il seguente codice perl per confrontare:

perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/'  file1 file2

(codice perl da http://www.cyberciti.biz/faq/command-to-display-lines-common-in-files/ , che lo ha preso da "qualcuno del gruppo di notizie comp.unix.shell").

Vedere la sostituzione del processo se si desidera trasformarlo in una riga.


4
Anche se hai comm, non puoi usarlo perché l'output del comando "git rev-list" non è ordinato lessicograficamente. Ovviamente potresti ordinare l'output di ciascun comando prima di cercare linee comuni, ma il commit desiderato non sarebbe necessariamente l'ultimo. Quindi penso che sia necessario qualcosa di simile al comando perl (sebbene oscuro).
mhagger,

12
Ho appena scritto uno script git-when-merged che implementa questo suggerimento (con alcune altre funzionalità). Vedi github.com/mhagger/git-when-merged
mhagger

2
Supponiamo che ad un certo punto sia masterstato unito feature, quindi immediatamente featureunito mastercome avanzamento rapido (punta di featurerimpiazzo master). Ciò causerebbe la --first-parentrestituzione del genitore sbagliato?
Kelvin,

4
Ci ho provato comm -1 -2ma non ha funzionato. commFunziona solo su linee ordinate. (Il perl one-liner funziona, anche se non riuscivo a leggerlo.)
Domon

2
Questo è un po 'in ritardo, ma aggiungendo in modo che le persone possano trovarlo utile perché Git non lo fornisce in modo nativo. Ci sono alcuni casi corner che penso che questa risposta non gestisca (vedi la mia risposta sotto stackoverflow.com/a/43716029/58678 che gestisce la maggior parte dei casi corner). Ecco i casi angolari: git find-merge h master(non restituisce nulla ma deve restituire h), git find-merge d master(restituisce f ma deve restituire d), git find-merge c feature(restituisce e ma deve restituire g).
hIpPy

133

Aggiungi questo al tuo ~/.gitconfig:

[alias]
    find-merge = "!sh -c 'commit=$0 && branch=${1:-HEAD} && (git rev-list $commit..$branch --ancestry-path | cat -n; git rev-list $commit..$branch --first-parent | cat -n) | sort -k2 -s | uniq -f1 -d | sort -n | tail -1 | cut -f2'"
    show-merge = "!sh -c 'merge=$(git find-merge $0 $1) && [ -n \"$merge\" ] && git show $merge'"

Quindi puoi usare gli alias in questo modo:

# current branch
git find-merge <SHA-1>
# specify master
git find-merge <SHA-1> master

Per visualizzare il messaggio del commit unione e altri dettagli, utilizzare git show-mergecon gli stessi argomenti.

(Basato sulla risposta di Gauthier . Grazie a Rosen Matev e javabrett per aver risolto un problema con sort.)


6
Bellissimo! Questa è la migliore soluzione drop-in.
N Jones,

1
Fai attenzione a sort -k2 | uniq -f1 -d | sort -n | tail -1 | cut -f2non trovare correttamente l'ultima riga in comune. Ecco un esempio in cui fallisce.
Rosen Matev,

1
@RosenMatev Lo trova 16db9fef5c581ab0c56137d04ef08ef1bf82b0b7qui quando lo eseguo sul tuo incolla, non è previsto? Su quale sistema operativo sei?
Robininst

1
@robinst, il tuo è corretto. Ho "(GNU coreutils) 8.4" e per me trova29c40c3a3b33196d4e79793bd8e503a03753bad1
Rosen Matev,

2
@powlo: niente, è solo un comando invece di due per comodità.
Robininst

28

git-get-merge localizzerà e mostrerà il commit di merge che stai cercando:

pip install git-get-merge
git get-merge <SHA-1>

Il comando segue i figli del commit dato fino a quando non viene trovata un'unione in un altro ramo (presumibilmente master).


22

Cioè, per riassumere il post di Gauthier:

perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' <(git rev-list --ancestry-path <SHA-1_for_c>..master) <(git rev-list --first-parent <SHA-1_for_c>..master) | tail -n 1

EDIT: poiché utilizza la sostituzione di processo " <()", non è compatibile con POSIX e potrebbe non funzionare con la shell. Funziona con basho zshcomunque.


4
Spesso non copio / incollo codice da Internet, ma quando lo faccio funziona perfettamente! Grazie Totor per avermi permesso di non pensare.
Ben

2
@ TheodoreR.Smith, sfortunatamente, la sintassi <()non è compatibile con POSIX. È necessario utilizzare bash, zsho un rivestimento portante sostituzione di processo . Ho modificato la mia risposta di conseguenza.
Totor

Non ha funzionato per me. Penso che il mio ramo delle caratteristiche sia stato prima unito a un altro ramo prima di essere unito al master. Ciò potrebbe spiegare perché i comandi generano "Unisci ramo 'release_branch'" anziché "Unisci ramo 'feature_branch'".
Tim Kuipers,

14

Avevo bisogno di farlo, e in qualche modo ho trovato git-when-merged(che in realtà fa riferimento a questa domanda SO, ma Michael Haggerty non ha mai aggiunto un riferimento al suo bellissimo script Python qui). Quindi ora ho.


In realtà sono arrivato a questa domanda dalla sua pagina.
Flavio

12

Basandoci sulla grande risposta di Gauthier, non abbiamo bisogno di usare commper confrontare le liste. Dal momento che stiamo cercando l'ultimo risultato in --ancestry-pathcui è presente anche --first-parent, possiamo semplicemente cercare quest'ultimo nell'output del primo:

git rev-list <SHA>..master --ancestry-path | grep -f <(git rev-list <SHA>..master --first-parent) | tail -1

O per qualcosa di scattante e riutilizzabile, ecco una funzione in cui entrare .bashrc :

function git-find-merge() {
  git rev-list $1..master --ancestry-path | grep -f <(git rev-list $1..master --first-parent) | tail -1
}

Questo ha funzionato per me. commnon ha funzionato quando gli input non sono stati ordinati.
hppy

4

Per la folla di Ruby, c'è da fare . Molto facile.

$ gem install git-whence
$ git whence 1234567
234557 Merge pull request #203 from branch/pathway

4

Uso sotto lo script bash che metto sul percorso ~/bin/git-find-merge. Si basa sulla risposta di Gauthier e sulla risposta di Evilstreak con alcune modifiche per gestire casi d'angolo. commgenera quando gli input non sono ordinati. grep -ffunziona perfettamente.

Valigie angolari:

  • Se commit è un commit di unione sul percorso del ramo principale del genitore, quindi restituire commit.
  • Se commit è un commit NON-merge sul percorso principale del ramo del ramo, quindi restituire il ramo. È una fusione o il commit è solo su branch e non c'è un buon modo per capire il commit giusto.
  • Se commit e branch sono uguali, restituire commit.

~/bin/git-find-merge script:

#!/bin/bash

commit=$1
if [ -z $commit ]; then
    echo 1>&2 "fatal: commit is required"
    exit 1
fi
commit=$(git rev-parse $commit)
branch=${2-@}

# if branch points to commit (both are same), then return commit
if [ $commit == $(git rev-parse $branch) ]; then
    git log -1 $commit
    exit
fi

# if commit is a merge commit on first-parent path of branch,
# then return commit
# if commit is a NON-merge commit on first-parent path of branch,
# then return branch as it's either a ff merge or commit is only on branch
# and there is not a good way to figure out the right commit
if [[ $(git log --first-parent --pretty='%P' $commit..$branch | \
    cut -d' ' -f1 | \
    grep $commit | wc -l) -eq 1 ]]; then
    if [ $(git show -s --format="%P" $commit | wc -w) -gt 1 ]; then
        # if commit is a merge commit
        git log -1 $commit
    else
        # if commit is a NON-merge commit
        echo 1>&2 ""
        echo 1>&2 "error: returning the branch commit (ff merge or commit only on branch)"
        echo 1>&2 ""
        git log -1 $branch
    fi
    exit
fi

# 1st common commit from bottom of first-parent and ancestry-path
merge=$(grep -f \
    <(git rev-list --first-parent  $commit..$branch) \
    <(git rev-list --ancestry-path $commit..$branch) \
        | tail -1)
if [ ! -z $merge ]; then
    git log -1 $merge
    exit
fi

# merge commit not found
echo 1>&2 "fatal: no merge commit found"
exit 1

Il che mi permette di fare questo:

(master)
$ git find-merge <commit>    # to find when commit merged to current branch
$ git find-merge <branch>    # to find when branch merged to current branch
$ git find-merge <commit> pu # to find when commit merged to pu branch

Questo script è disponibile anche sul mio github .


2
git log --topo-order

Quindi cerca la prima unione prima del commit.


1

La mia versione ruby ​​dell'idea di @ robinst funziona due volte più velocemente (il che è importante quando si cerca un commit molto vecchio).

find-commit.rb

commit = ARGV[0]
master = ARGV[1] || 'origin/master'

unless commit
  puts "Usage: find-commit.rb commit [master-branch]"
  puts "Will show commit that merged <commit> into <master-branch>"
  exit 1
end

parents = `git rev-list #{commit}..#{master} --reverse --first-parent --merges`.split("\n")
ancestry = `git rev-list #{commit}..#{master} --reverse --ancestry-path --merges`.split("\n")
merge = (parents & ancestry)[0]

if merge
  system "git show #{merge}"
else
  puts "#{master} doesn't include #{commit}"
  exit 2
end

Puoi semplicemente usarlo in questo modo:

ruby find-commit.rb SHA master

0

Puoi provare qualcosa del genere. L'idea è di iterare attraverso tutti i commit di unione e vedere se il commit "c" è raggiungibile da uno di essi:

$ git log --merges --format='%h' master | while read mergecommit; do
  if git log --format='%h' $mergecommit|grep -q $c; then
    echo $mergecommit;
    break
  fi
done

È lo stesso di:git rev-list <SHA-1_for_c>..master --ancestry-path --merges
Eugen Konkov,

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.