O Git 2.18 (Q2 2018) melhorará consideravelmente a --preserve-merge
opção adicionando uma nova opção.
" git rebase
" aprendi " --rebase-merges
" a transplantar toda a topologia do gráfico de confirmação em outro lugar .
(Observação: o Git 2.22, segundo trimestre de 2019, na verdade é preterido --preserve-merge
, e o Git 2.25, primeiro trimestre de 2020, para de anunciar o resultado " git rebase --help
" )
Veja cometer 25cff9f , cometer 7543f6f , cometer 1131ec9 , cometer 7ccdf65 , cometer 537e7d6 , cometer a9be29c , cometer 8f6aed7 , cometer 1644c73 , cometer d1e8b01 , cometer 4c68e7d , cometer 9055e40 , cometer cb5206e , cometer a01c2a5 , cometer 2f6b1d1 , cometer bf5c057 (25 de abril de 2018) de Johannes Schindelin ( dscho
) .
Veja commit f431d73 (25 Abr 2018) por Stefan Beller ( stefanbeller
) .
Veja commit 2429335 (25 de abril de 2018) por Phillip Wood ( phillipwood
) .
(Mesclado por Junio C Hamano - gitster
- na confirmação 2c18e6a , 23 de maio de 2018)
pull
: aceite --rebase-merges
recriar a topologia da ramificação
Semelhante ao preserve
modo simplesmente passando a --preserve-merges
opção para o rebase
comando, o merges
modo simplesmente passa a
--rebase-merges
opção.
Isso permitirá que os usuários reorganizem convenientemente topologias de confirmação não triviais ao obter novas confirmações, sem achatá-las.
git rebase
Agora, a página de manual tem uma seção completa dedicada a refazer o histórico com mesclagens .
Extrair:
Existem razões legítimas pelas quais um desenvolvedor pode querer recriar confirmações de mesclagem: para manter a estrutura da ramificação (ou "topologia de confirmação") ao trabalhar em várias ramificações inter-relacionadas.
No exemplo a seguir, o desenvolvedor trabalha em uma ramificação de tópico que refatora a maneira como os botões são definidos e em outra ramificação de tópico que usa essa refatoração para implementar um botão "Relatar um bug".
A saída de git log --graph --format=%s -5
pode ser assim:
* Merge branch 'report-a-bug'
|\
| * Add the feedback button
* | Merge branch 'refactor-button'
|\ \
| |/
| * Use the Button class for all buttons
| * Extract a generic Button class from the DownloadButton one
O desenvolvedor pode querer reestruturar essas confirmações para uma mais recente master
, mantendo a topologia da ramificação, por exemplo, quando se espera que a primeira ramificação do tópico seja integrada master
muito antes do segundo, por exemplo, para resolver conflitos de mesclagem com alterações na
DownloadButton
classe que foi feita em master
.
Essa rebase pode ser realizada usando a --rebase-merges
opção
Consulte commit 1644c73 para um pequeno exemplo:
rebase-helper
--make-script
: introduza um sinalizador para refazer a mesclagem
O sequenciador acabou de aprender novos comandos destinados a recriar a estrutura de ramificações ( semelhante em espírito --preserve-merges
, mas com um design substancialmente menos quebrado ).
Vamos permitir a rebase--helper
geração de listas de tarefas utilizando esses comandos, acionados pela nova --rebase-merges
opção.
Para uma topologia de consolidação como esta (em que o HEAD aponta para C):
- A - B - C (HEAD)
\ /
D
a lista de tarefas gerada ficaria assim:
# branch D
pick 0123 A
label branch-point
pick 1234 D
label D
reset branch-point
pick 2345 B
merge -C 3456 D # C
Qual é a diferença --preserve-merge
?
O compromisso 8f6aed7 explica:
Era uma vez, este desenvolvedor aqui pensou: não seria bom se, digamos, os patches do Git for Windows no topo do núcleo, o Git pudessem ser representados como um emaranhado de ramificações e serem reestruturados no topo do núcleo do Git para manter um conjunto cereja-pick'able de série de patches?
A tentativa original para responder a esta era: git rebase --preserve-merges
.
No entanto, esse experimento nunca teve a intenção de ser uma opção interativa, e apenas foi suportado git rebase --interactive
porque a implementação desse comando já parecia muito, muito familiar: foi projetada pela mesma pessoa que projetou --preserve-merges
: a sua verdadeiramente.
E por "você realmente", o autor se refere a: Johannes Schindelin ( dscho
) , que é o principal motivo (com alguns outros heróis - Hannes, Steffen, Sebastian, ...) de que temos o Git For Windows (embora de volta ao dia - 2009 - isso não foi fácil ).
Ele trabalha na Microsoft desde setembro de 2015 , o que faz sentido, considerando que a Microsoft agora usa fortemente o Git e precisa de seus serviços.
Essa tendência começou em 2013, na verdade, com o TFS . Desde então, a Microsoft gerencia o maior repositório Git do planeta ! E, desde outubro de 2018, a Microsoft adquiriu o GitHub .
Você pode ver Johannes falando neste vídeo para o Git Merge 2018 em abril de 2018.
Algum tempo depois, algum outro desenvolvedor (estou olhando para você, Andreas! ;-)) decidiu que seria uma boa ideia permitir --preserve-merges
combinar com --interactive
(com advertências!) E o mantenedor do Git (bem, o mantenedor interino do Git durante a ausência de Junio, isto é) concordou, e foi aí que o glamour do --preserve-merges
design começou a desmoronar rapidamente e sem glamour .
Aqui Jonathan está falando sobre Andreas Schwab, de Suse.
Você pode ver algumas das discussões deles em 2012 .
O motivo? No --preserve-merges
modo, os pais de uma consolidação de mesclagem (ou, de qualquer forma, de qualquer consolidação) não foram declarados explicitamente, mas estavam
implícitos no nome da consolidação passada para o pick
comando .
Isso tornou impossível, por exemplo, reordenar confirmações .
Sem mencionar a mudança de commits entre ramificações ou, deidade proibida, dividir ramificações de tópicos em duas.
Infelizmente, essas deficiências também impediram que o modo (cujo objetivo original era atender às necessidades do Git for Windows, com a esperança adicional de que também possa ser útil para outras pessoas) de atender às necessidades do Git for Windows.
Cinco anos depois, quando tornou-se realmente insustentável ter uma série de patches complicados e complicados de patches parcialmente relacionados e parcialmente não relacionados no Git for Windows, que eram reposicionados nas tags principais do Git de tempos em tempos (ganhando a ira imerecida do desenvolvedor) da git-remote-hg
série malfadada
que obsoleta a primeira abordagem concorrente do Git for Windows, para depois ser abandonada sem mantenedor mais tarde) era realmente insustentável, nasceram as " tesouras de jardinagem Git " : um script, que acompanhava o rebase interativo, que determinaria primeiro a topologia de ramificação dos patches a serem rebaseados, criaria uma lista de pseudo-tarefas para edição adicional, transformaria o resultado em uma lista de tarefas real (fazendo uso pesado doexec
comando para "implementar" os comandos da lista de tarefas ausentes) e, finalmente, recriar a série de patches sobre a nova confirmação de base.
(O script Git garden shears é mencionado neste patch no commit 9055e40 )
Isso foi em 2013.
E demorou cerca de três semanas para criar o design e implementá-lo como um script fora da árvore. Desnecessário dizer que a implementação precisou de alguns anos para estabilizar, enquanto o design em si se mostrava sólido.
Com este patch, a bondade das tesouras de jardim Git trata de git
rebase -i
si .
Passar a --rebase-merges
opção gerará uma lista de tarefas que pode ser facilmente entendida e onde é óbvio como reorganizar as confirmações .
Novas ramificações podem ser introduzidas inserindo label
comandos e chamando merge <label>
.
E assim que esse modo se tornar estável e aceito universalmente, podemos descartar o erro de design que ocorreu--preserve-merges
.
O Git 2.19 (terceiro trimestre de 2018) aprimora a nova --rebase-merges
opção, fazendo com que ela funcione --exec
.
A --exec
opção " " para " git rebase --rebase-merges
" colocou os comandos exec em locais errados, que foram corrigidos.
Consulte commit 1ace63b (09 de agosto de 2018) e commit f0880f7 (06 de agosto de 2018) por Johannes Schindelin ( dscho
) .
(Mesclado por Junio C Hamano - gitster
- in commit 750eb11 , 20 de agosto de 2018)
rebase --exec
: faça funcionar com --rebase-merges
A idéia de --exec
é anexar uma exec
chamada após cada uma pick
.
Desde a introdução do fixup!
/ s quash!
commits, essa idéia foi estendida para aplicar-se a "pick, possivelmente seguido de uma cadeia de correção / squash", ou seja, um executivo não seria inserido entre a pick
e qualquer uma de suas linhas fixup
ou correspondentes
squash
.
A implementação atual usa um truque sujo para conseguir isso: assume que há apenas comandos pick / fixup / squash e, em seguida,
insere as exec
linhas antes de qualquer um, pick
exceto o primeiro, e anexa um final.
Com as listas de tarefas geradas por git rebase --rebase-merges
, essa implementação simples mostra seus problemas: produz exatamente a coisa errada quando existem label
, reset
e merge
comandos.
Vamos mudar a implementação para fazer exatamente o que queremos: procure
pick
linhas, pule qualquer cadeia de correção / squash e insira a exec
linha . Espuma, enxágüe, repita.
Nota: nos esforçamos para inserir antes das linhas de comentário sempre que possível, pois confirmações vazias são representadas por linhas de seleção comentadas (e queremos inserir a linha executiva de uma seleção anterior antes dessa linha, e não depois).
Enquanto isso, adicione também exec
linhas após merge
comandos, porque eles são semelhantes em espírito aos pick
comandos: eles adicionam novos commits.
O Git 2.22 (Q2 2019) corrige o uso de refs / reescrito / hierarquia para armazenar estados intermediários de rebase, o que faz inerentemente a hierarquia por árvore de trabalho.
Consulte commit b9317d5 , commit 90d31ff , commit 09e6564 ( 07/03/2019 ) por Nguyễn Thái Ngọc Duy ( pclouds
) .
(Mesclado por Junio C Hamano - gitster
- in commit 917f2cd , 09 abr 2019)
Verifique se refs / reescrito / é por árvore de trabalho
O a9be29c (sequenciador: make refs gerado pelo label
comando worktree-local, 25/04/2018, Git 2.19) é adicionado refs/rewritten/
como espaço de referência por worktree.
Infelizmente (infelizmente), existem alguns lugares que precisam de atualização para garantir que seja realmente por área de trabalho.
- add_per_worktree_entries_to_dir()
é atualizado para garantir que a lista de referências analise por árvore de trabalho em refs/rewritten/
vez de por árvore de repo.
common_list[]
é atualizado para que git_path()
retorne o local correto. Isso inclui " rev-parse --git-path
".
Essa bagunça é criada por mim.
Comecei a tentar consertá-lo com a introdução de refs/worktree,
onde todos os árbitros serão por área de trabalho sem tratamentos especiais.
Refs / reescritos infelizes vieram antes de refs / worktree, portanto é tudo o que podemos fazer.
Com o Git 2.24 (quarto trimestre de 2019), " git rebase --rebase-merges
" aprendemos a conduzir diferentes estratégias de mesclagem e a passar opções específicas da estratégia para elas.
Veja commit 476998d (04 Set 2019) por Elijah Newren ( newren
) .
Consulte confirmar e1fac53 , confirmar a63f990 , confirmar 5dcdd74 , confirmar e145d99 , confirmar 4e6023b , cometer f67336d , cometer a9c7107 , cometer b8c6f24 , cometer d51b771 , cometer c248d32 , cometer 8c1e240 , cometer 5efed0e , cometer 68b54f6 , cometer 2e7bbac , cometer 6180b20 , cometer d5b581f (31 Jul 2019) porJohannes Schindelin ( dscho
) .
(Mesclado por Junio C Hamano - gitster
- no commit 917a319 , 18 de setembro de 2019)
Com o Git 2.25 (primeiro trimestre de 2020), a lógica usada para diferenciar as referências locais e do repositório global da árvore de trabalho é corrigida, para facilitar a mesclagem de preservar.
Consulte commit f45f88b , commit c72fc40 , commit 8a64881 , commit 7cb8c92 , commit e536b1f (21 de outubro de 2019) por SZEDER Gábor ( szeder
) .
(Incorporado por Junio C Hamano - gitster
- in commit db806d7 , 10 nov 2019)
path.c
: não chame a match
função sem valor emtrie_find()
Assinado por: SZEDER Gábor
'logs / refs' não é um caminho específico da árvore de trabalho, mas desde que o commit b9317d55a3 (verifique se refs / reescrito / é por worktree, 2019-03-07, v2.22.0-rc0) ' git rev-parse --git-path
' está retornando um caminho falso se um final ' /
' estiver presente:
$ git -C WT/ rev-parse --git-path logs/refs --git-path logs/refs/
/home/szeder/src/git/.git/logs/refs
/home/szeder/src/git/.git/worktrees/WT/logs/refs/
Usamos uma trie
estrutura de dados para decidir com eficiência se um caminho pertence ao diretório comum ou se está trabalhando em uma árvore específica.
Como acontece b9317d55a3 acionou um bug que é tão antigo quanto a trie
própria implementação, adicionado em 4e09cf2acf (" path
: otimizar verificação comum de diretórios", 31/08/2015, Git v2.7.0-rc0 - mesclagem listada no lote # 2 ).
De acordo com o comentário que descreve trie_find()
, ele deve chamar apenas a função de correspondência fornecida 'fn' para um prefixo "/ -ou- \ 0 terminado da chave para a qual o trie contém um valor".
Isso não é verdade: há três lugares em que trie_find () chama a função match, mas um deles está faltando na verificação da existência do valor.
b9317d55a3 adicionou duas novas chaves ao trie
:
- '
logs/refs/rewritten
' e
- '
logs/refs/worktree
', ao lado do já existente ' logs/refs/bisect
'.
Isso resultou em um trie
nó com o caminho 'logs/refs/
', que não existia antes e que não tinha um valor anexado.
Uma consulta para ' logs/refs/
' localiza esse nó e, em seguida, acessa o local de chamada da match
função que não verifica a existência do valor e, portanto, chama a match
função com NULL
como valor.
Quando a match
função check_common()
é invocada com um NULL
valor, ela retorna 0, o que indica que o caminho consultado não pertence ao diretório comum, resultando no final do caminho falso mostrado acima.
Adicione a condição ausente a, trie_find()
para que nunca invoque a função de correspondência com um valor inexistente.
check_common()
não precisará mais verificar se obteve um valor diferente de NULL; portanto, remova essa condição.
Acredito que não existem outros caminhos que possam causar uma saída falsa semelhante.
AFAICT, a única outra chave que resulta na chamada da função de correspondência com um NULL
valor é ' co
' (por causa das chaves 'common
' e ' config
').
No entanto, como eles não estão em um diretório que pertence ao diretório comum, é esperado o caminho específico da árvore de trabalho resultante.
git --rebase-merges
substituirá o antigogit --preserve-merges
. Veja minha resposta abaixo