Hg: Como fazer um rebase como o rebit do git

207

No Git, eu posso fazer isso:

1. Comece a trabalhar no novo recurso:
$ git co -b newfeature-123 # (uma ramificação local de desenvolvimento de recursos)
faça algumas confirmações (M, N, O)

mestre A --- B --- C
                \
novidade-123 M --- N --- O

2. Puxe novas alterações do mestre upstream:
$ git pull
(mestre atualizado com ff-commits)

mestre A --- B --- C --- D --- E --- F
                \
novidade-123 M --- N --- O

3. Rebase off master para que meu novo recurso 
pode ser desenvolvido com relação às alterações upstream mais recentes:
(from newfeature-123)
$ git rebase master

mestre A --- B --- C --- D --- E --- F
                            \
novidade-123 M --- N --- O


Quero saber como fazer a mesma coisa no Mercurial, e procurei na Web por uma resposta, mas o melhor que pude encontrar foi: git rebase - pode hg fazer isso

Esse link fornece 2 exemplos:
1. Admito que isso: (substituindo as revisões do exemplo por aquelas do meu próprio exemplo)

hg up -CF  
hg branch -f newfeature-123  
hg transplant -a -b newfeature-123 

não é tão ruim, exceto que ele deixa para trás o MNO pré-rebase como um cabeçote imerso e cria três novos commits M ', N', O 'que os representam ramificando a linha principal atualizada.

Basicamente, o problema é que eu acabo com isso:

mestre A --- B --- C --- D --- E --- F
                \ \
novidade-123 \ M '--- N' --- O '
                  \
novidade-123 M --- N --- O

isso não é bom porque deixa para trás compromissos locais indesejados que devem ser descartados.

  1. A outra opção do mesmo link é
hg qimport -r M: O
hg qpop -a
hg up F
hg branch newfeature-123
hg qpush -a
hg qdel -r qbase: qtip

e isso resulta no gráfico desejado:

mestre A --- B --- C --- D --- E --- F
                            \
novidade-123 M --- N --- O

mas esses comandos (todos os 6!) parecem muito mais complicados do que

$ git rebase master

Quero saber se este é o único equivalente em Hg ou se existe alguma outra maneira disponível que seja simples como o Git.

jpswain
fonte
7
"isso não é bom porque deixa para trás compromissos locais indesejados que devem ser descartados". - na verdade, o git faz a mesma coisa. Ele não altera ou remove as confirmações na ramificação original, apenas cria novas que aplicam o mesmo conjunto de alterações na parte superior do mestre. Você ainda pode acessar os antigos usando git refloge eles não desaparecerão totalmente até que sejam coletados o lixo. Se você quiser mantê-los em um ramo nomeado para não precisar usar o reflog, faça-o git branch feature-123_originalantes de fazer o novo rebote .
MatrixFrog
5
Pergunta aleatória: você desenhou as alterações / ramificações sozinho ou existe uma ferramenta que faz isso?
Amir Rachum
2
Eu mesmo os fiz com o TextWrangler definido como "substituir".
jpswain

Respostas:

235

O VonC tem a resposta que você procura , a extensão Rebase. No entanto, vale a pena gastar um ou dois segundos pensando sobre por que nem mq nem rebase são ativados por padrão em mercurial: porque mercurial é tudo sobre conjuntos de alterações indeléveis. Quando trabalho da maneira que você está descrevendo, quase diariamente, eis o padrão que tomo:

1. Start working on a new feature:
$ hg clone mainline-repo newfeature-123
do a few commits (M, N, O)

master A---B---C
                \
newfeature-123   M---N---O

2. Pull new changes from upstream mainline:
$ hg pull

master A---B---C---D---E---F
                \
newfeature-123   M---N---O

3. merge master into my clone so that my new feature 
can be developed against the latest upstream changes:
(from newfeature-123)
$ hg merge F

master A---B---C---D---E---F
                \           \
newfeature-123   M---N---O---P

e isso é realmente tudo o que é necessário. Acabo com um clone newfeature-123 que posso facilmente retornar à linha principal quando estou feliz com isso. Mais importante, porém, nunca mudei a história . Alguém pode olhar para meus csets e ver com o que eles foram originalmente codificados e como eu reagi às alterações na linha principal ao longo do meu trabalho. Nem todo mundo pensa que isso tem valor, mas acredito firmemente que é o trabalho do controle de origem mostrar-nos não o que desejávamos que tivesse acontecido, mas o que realmente aconteceu - todo beco sem saída e todo refatorador devem deixar um rastro indelével e refazê-lo e outras técnicas de edição de história escondem isso.

Agora vá buscar a resposta do VonC enquanto guardo minha caixa de sabão. :)

Ry4an Brase
fonte
18
Nota: é claro que o Git não permite exatamente reescrever a história, apenas criar uma nova facilmente ( utcc.utoronto.ca/~cks/space/blog/tech/GitNewHistory ). Ao adicionar o RebaseExtension, o Mercurial fornece a mesma maneira conveniente e conveniente de substituir um histórico antigo por um novo. Por quê? Porque uma fusão nem sempre é a resposta certa, especialmente quando o seu conjunto de alterações deve ser visto como evoluções no topo de F, e não o inverso (P fundidos em cima de O)
VonC
19
VonC, eu concordo, uma fusão nem sempre é a escolha certa, mas acho que a diferença é o que se quer que a história do VCS possa contar a eles. Eu acho que a história deve sempre ser capaz de responder a perguntas como "Como foi que eu tentei integrá-lo no começo que não deu certo e achei que era inútil na época". Os cientistas mantêm os diários de bordo em caneta com as páginas numeradas, e os engenheiros de software da AFAIC devem economizar todos os bytes digitados. Reescrever o histórico, até mesmo definir a paternidade, mas certamente CollapseExtension, HistEdit, etc. violam isso. É totalmente uma questão de escolha pessoal.
Ry4an Brase
14
+1 para escolha pessoal. Com o Git, consequentemente, uso rebase para divergências triviais e mesclo para não triviais. Isso me permite preservar o histórico de mesclagem onde considero importante, mas mantenha o log limpo e linear na maior parte do tempo.
Kos
16
Além disso, eu faço toneladas de rebotes interativos porque, em primeiro lugar, costumo fazer muitos commits pequenos, depois unir, rotular e limpá-los, depois juntá-los novamente (ou fazer o rebase no topo) do branch principal. Eu gosto de codificar e gerenciar alterações em etapas separadas.
Kos
6
A filosofia "salvar cada byte ao longo do caminho do desenvolvimento" não é realmente apropriada para grandes projetos de código aberto, onde os colaboradores propõem um conjunto de correções e as refazem com base no feedback dos mantenedores. Eventualmente, o repositório principal do projeto possui apenas a versão adequada de todas as alterações, com todas as alterações relacionadas em uma única confirmação. O git Interactive Rebase é realmente bom para limpar as alterações de trabalho em uma sequência de confirmações que não deixam a árvore quebrada, e com as mensagens de confirmação refletindo o que você decide dizer depois de terminar de trabalhar na coisa toda.
Peter Cordes
104

Você pode estar procurando a extensão Rebase . (implementado como parte do SummerOfCode 2008 )

Nesses casos, pode ser útil "desanexar" as alterações locais, sincronizar o repositório com o mainstream e, em seguida, anexar as alterações privadas sobre as novas alterações remotas. Essa operação é chamada rebase.

Obtendo de :

texto alternativo

para:

texto alternativo


Como comentado abaixo por steprobe :

No caso em que você não está realizando as alterações e possui os dois ramos no seu repositório, você pode fazer ( usandokeepbranches ):

hg up newfeature-123 
hg rebase -d master --keepbranches

( --keepbranches: Herda o nome da ramificação original.)

Mojca menciona:

Eu gosto de usar hg rebase --source {L1's-sha} --dest {R2's-sha}, mas não sabia que poderia adicionar --keepbranchesno final.

Como ilustrado abaixo por Jonathan Blackburn :

 hg rebase -d default --keepbranches
VonC
fonte
1
Eu olhei para a extensão Rebase, mas ainda não está claro para mim. Você poderia explicar as etapas para fazer o que descrevi acima?
jpswain
6
No caso em que você não está puxando as mudanças na, e você tem os dois ramos em seu repo, você pode fazer: hg up newfeature-123seguido porhg rebase -d master --keepbranches
steprobe
2
Acredito que nada está errado com o rebase, é apenas uma questão de escolha. O problema é que o rebase é abusado e eu estou com o @ Ry4an nisso, não reescreva o histórico para que você possa saber o que acontece e quando.
Jorge Vargas
@steprobe: obrigado pela dica de uso --keepbranches. Eu gosto de usar hg rebase --source {L1's-sha} --dest {R2's-sha}, mas não sabia que poderia adicionar --keepbranchesno final. Isso é mencionado em outras respostas de classificação inferior, mas seria bom escrevê-lo explicitamente também para essa resposta.
Mojca
@Mojca Sem problemas. Eu editei a resposta de acordo.
VonC 22/02/19
44

Supondo que você tenha uma instalação moderna de Hg, você pode simplesmente adicionar:

[extensions]
rebase = 

para ~ / .hgrc.

Então você pode usar os comandos hg rebase, hg pull --rebaseou hg help rebase.

sblom
fonte
2
Só para adicionar a isso em termos de comando, então você precisa executar seria: hg rebase -s o -d f
Simple-Solution
21

Não acho que as respostas acima atinjam o objetivo do OP, que era manter seu ramo de tarefas, apenas rebatizado em um ponto posterior no ramo pai.

Digamos que eu comece com este gráfico (gerado usando a extensão graphlog. Amor sério por graphlog).

@  9a4c0eb66429 Feature 3 commit 2 tip feature3
|
| o  af630ccb4a80 default againagainagain  
| |
o |  98bdde5d2185 Feature 3 branch commit 1  feature3
|/
o  e9f850ac41da foo   

Se eu estiver no ramo feature3 e quiser refazê-lo novamente do commit novamente, entendo que eu executaria hg rebase -d default. Isso tem o seguinte resultado:

@  89dada24591e Feature 3 commit 2 tip 
|
o  77dcce88786d Feature 3 branch commit 1  
|
o  af630ccb4a80 default againagainagain  
|
o  e9f850ac41da foo  

Missão cumprida? Acho que não. O problema é que, quando as confirmações na ramificação feature3 foram refazidas novamente, a ramificação feature3 foi excluída . Meus commits foram movidos para a ramificação padrão, que era o que eu estava tentando evitar em primeiro lugar.

No Git, o resultado seria assim:

@  9a4c0eb66429 Feature 3 commit 2 tip
|
o  98bdde5d2185 Feature 3 branch commit 1 **feature3**
|
o  af630ccb4a80 default againagainagain
|
o  e9f850ac41da foo

Observe que a ramificação feature3 ainda existe, as duas confirmações ainda estão na ramificação feature3 e não são visíveis no padrão. Sem preservar a ramificação da tarefa, não vejo como isso é funcionalmente diferente de uma mesclagem.

ATUALIZAÇÃO : Descobri a --keepbranchesbandeira suportada pelo hg rebase e fico feliz em informar que tudo está ok. Usando hg rebase -d default --keepbranches, replico exatamente o comportamento do Git que eu desejava. Alguns apelidos depois, e estou me rebatendo como ninguém.

Jonathan Blackburn
fonte
7
Acabei de descobrir a bandeira --keepbranches na rebase. Problema resolvido. Se esse fosse meu código, eu o tornaria o padrão, mas sou apenas eu.
Jonathan Blackburn
1
Eu acho que seria útil se você adicionasse essa informação à sua resposta acima - demorei um pouco para encontrá-la.
HansMari
3

Como algumas pessoas concordaram em dizer que acham que é bom manter todas as iterações de tudo, vou apontar que, para projetos maiores de código aberto, aceitar mudanças cheias de mesclagens e a iteração de desenvolvimento resultaria em um histórico de revisões da linha principal confuso. o histórico de revisões menos útil para ver como a versão atual chegou lá.

Isso funciona bem quando as alterações enviadas são revisadas por pessoas que não as escreveram, antes de serem aceitas; portanto, as alterações que entram na linha principal geralmente são depuradas e funcionando. Então, quando você retorna à origem de uma linha, vê todas as mudanças que a acompanham, e não em algum momento no meio do desenvolvimento da mudança da qual faz parte.

A página de contribuidores do x265 explica como reenviar um conjunto de alterações nas quais você está trabalhando, para prepará-los para o envio ao projeto x265. (Incluindo o uso do TortoiseHG para confirmar algumas, mas nem todas, as alterações em um arquivo individual, como o estágio do diff do estágio / unstage do git gui para commit).

O processo é atualizar o hg para a ponta do upstream e, em seguida, descomprimir todas as suas alterações no diretório de trabalho. Arquive qualquer que não faça parte do que você deseja enviar e divida o restante em quantas confirmações separadas forem apropriadas, com boas mensagens de confirmação.

Eu acho que você copia / cola e edita as mensagens de confirmação de iterações anteriores de um conjunto de patches que você está revisando. Ou talvez você possa enxertar seus commits antigos (escolher cereja no idioma git) e alterá-los um por um, para obter suas mensagens de commit antigas como ponto de partida para a edição.

Peter Cordes
fonte