Localizar consolidação de mesclagem, que inclui uma consolidação específica

183

Imagine a seguinte história:

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

Como posso descobrir quando a consolidação "c" foi mesclada no mestre (ou seja, encontrar a consolidação de mesclagem "h")?

Guillaume Morin
fonte

Respostas:

158

Seu exemplo mostra que a ramificação feature ainda está disponível.

Nesse caso, hé o último resultado de:

git log master ^feature --ancestry-path

Se a ramificação featurenão estiver mais disponível, você poderá mostrar os commits de mesclagem na linha do histórico entre ce master:

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

No entanto, isso também mostrará todas as mesclagens que ocorreram depois he entre ee gdepois feature.


Comparando o resultado dos seguintes comandos:

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

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

fornecerá o SHA-1 hcomo a última linha em comum.

Se você o tiver disponível, poderá usar comm -1 -2esses resultados. Se você estiver no msysgit, poderá usar o seguinte código perl para comparar:

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

(código perl de http://www.cyberciti.biz/faq/command-to-display-lines-common-in-files/ , que retirou de "alguém do grupo de notícias comp.unix.shell").

Consulte a substituição do processo se você deseja torná-lo uma linha.

Gauthier
fonte
4
Mesmo se você tiver comm, não poderá usá-lo porque a saída do comando "git rev-list" não está classificada lexicograficamente. É claro que você poderia classificar a saída de cada comando antes de procurar linhas comuns, mas o commit desejado não seria necessariamente o último. Então, acho que algo como o comando perl (embora obscuro) é necessário.
mhagger
12
Acabei de escrever um script git-when-mesclado que implementa essa sugestão (com vários outros recursos). Veja github.com/mhagger/git-when-merged
mhagger
2
Suponha que, em algum momento, tenha mastersido mesclado e feature, imediatamente, featuremesclado mastercomo um avanço rápido (dica de featuresubstituições master). Isso causaria o --first-parentretorno do pai errado?
Kelvin
4
Eu tentei, comm -1 -2mas não funcionou. commsó funciona em linhas classificadas. (O perl one-liner funciona, embora eu não poderia lê-lo.)
Domon
2
É um pouco tarde, mas adicionando para que as pessoas possam achar útil, já que o git nativamente não fornece isso. Existem alguns casos de cantos que acho que essa resposta não atende (veja minha resposta abaixo stackoverflow.com/a/43716029/58678, que lida com a maioria dos casos de cantos). Aqui estão os casos de canto: git find-merge h master(não retorna nada, mas deve retornar h), git find-merge d master(retorna f, mas deve retornar d), git find-merge c feature(retorna e, mas deve retornar g).
hIpPy
133

Adicione isto ao seu ~/.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'"

Então você pode usar os aliases como este:

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

Para ver a mensagem do commit de mesclagem e outros detalhes, use git show-mergecom os mesmos argumentos.

(Com base na resposta de Gauthier . Agradecemos a Rosen Matev e javabrett por corrigir um problema sort.)

robinst
fonte
6
Lindo! Esta é a melhor solução drop-in.
N Jones
1
Cuidado para sort -k2 | uniq -f1 -d | sort -n | tail -1 | cut -f2não encontrar corretamente a última linha em comum. Aqui está um exemplo em que falha.
Rosen Matev
1
@RosenMatev Ele encontra 16db9fef5c581ab0c56137d04ef08ef1bf82b0b7aqui quando eu o executo na sua pasta, isso não é esperado? Em qual SO você está?
robinst
1
@robinst, o seu está correto. Eu tenho "(GNU coreutils) 8.4" e para mim encontra29c40c3a3b33196d4e79793bd8e503a03753bad1
Rosen Matev
2
@ Powlo: Nada, é apenas um comando em vez de dois por conveniência.
robinst
28

O git-get-merge irá localizar e mostrar o commit de mesclagem que você está procurando:

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

O comando segue os filhos do commit fornecido até que uma mesclagem em outro ramo (presumivelmente mestre) seja encontrada.

Jian
fonte
22

Ou seja, para resumir o post de 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: porque usa substituição de processo " <()", não é compatível com POSIX, e pode não funcionar com seu shell. Funciona com bashou zshembora.

Totor
fonte
4
Geralmente não copio / colo código da Internet, mas quando o faço, ele funciona perfeitamente! Obrigado Totor por me permitir não pensar.
Ben
2
@ TheodoreR.Smith infelizmente, a sintaxe <()não é compatível com POSIX. Você precisa usar bash, zshou uma concha apoiar substituição processo . Eu editei minha resposta de acordo.
21814 Totor
Não funcionou para mim. Acho que meu ramo de recursos foi mesclado primeiro com outro ramo antes de ser mesclado ao mestre. Isso poderia explicar por que seus comandos resultam em "Mesclar ramo 'release_branch'" em vez de "Mesclar ramo 'feature_branch'".
amigos estão dizendo sobre tim
14

Eu precisava fazer isso e, de alguma forma, encontrei git-when-merged(o que realmente faz referência a essa pergunta do SO, mas Michael Haggerty nunca adicionou uma referência ao seu muito bom script Python aqui). Então agora eu tenho.

Alex Dupuy
fonte
Na verdade, cheguei a essa pergunta na página dele.
Flavius
12

Com base na grande resposta de Gauthier, não precisamos usar commpara comparar as listas. Como estamos procurando o último resultado no --ancestry-pathqual também está --first-parent, podemos simplesmente saudar o último na saída do primeiro:

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

Ou, para algo rápido e reutilizável, aqui está uma função para aparecer .bashrc:

function git-find-merge() {
  git rev-list $1..master --ancestry-path | grep -f <(git rev-list $1..master --first-parent) | tail -1
}
maldade
fonte
Isso funcionou para mim. commnão funcionou quando as entradas não foram classificadas.
precisa
4

Para a multidão de Ruby, há coisas sem sentido . Muito fácil.

$ gem install git-whence
$ git whence 1234567
234557 Merge pull request #203 from branch/pathway
aço
fonte
4

Eu uso o script bash abaixo, que coloco no caminho ~/bin/git-find-merge. É baseado na resposta de Gauthier e na resposta do evilstreak com poucos ajustes para lidar com casos de canto. commlança quando as entradas não são classificadas. grep -ffunciona perfeitamente.

Caixas de canto:

  • Se commit for um commit de mesclagem no caminho do primeiro pai da ramificação, retorne o commit.
  • Se a confirmação for uma confirmação de não mesclagem no caminho do primeiro pai da ramificação, retorne a ramificação. É uma mesclagem ou confirmação ff apenas no branch e não há uma boa maneira de descobrir o commit correto.
  • Se commit e branch forem iguais, retorne commit.

~/bin/git-find-merge roteiro:

#!/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

O que me permite fazer isso:

(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

Este script também está disponível no meu github .

hIpPy
fonte
2
git log --topo-order

Em seguida, procure a primeira mesclagem antes da confirmação.

romano
fonte
1

Minha versão ruby ​​da idéia do @ robinst, funciona duas vezes mais rápido (o que é importante ao procurar por commit muito antigo).

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

Você pode usá-lo assim:

ruby find-commit.rb SHA master
Kaplan Ilya
fonte
0

Você pode tentar algo assim. A idéia é percorrer todos os commit de mesclagem e ver se o commit "c" é acessível a partir de um deles:

$ 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
holygeek
fonte
É o mesmo que:git rev-list <SHA-1_for_c>..master --ancestry-path --merges
Eugen Konkov
0

Eu tive que fazer isso várias vezes (obrigado a todos que responderam a essa pergunta!) E acabei escrevendo um script (usando o método de Gauthier) que eu poderia adicionar à minha pequena coleção de utilitários git. Você pode encontrá-lo aqui: https://github.com/mwoehlke/git-utils/blob/master/bin/git-merge-point .

Mateus
fonte