Mercurial: como alterar o último commit?

211

Estou procurando uma contraparte git commit --amendno Mercurial, ou seja, uma maneira de modificar o commit ao qual minha cópia de trabalho está vinculada. Estou interessado apenas no último commit, não no arbitrário anterior.

Os requisitos para este procedimento de alteração são:

  • se possível, não deve exigir extensões. Ele não deve exigir extensões não padrão , ou seja, extensões que não vêm com uma instalação oficial do Mercurial.

  • se o commit para corrigir for um chefe do meu ramo atual, nenhum novo chefe deverá ser criado. Se o commit não for head, um novo head poderá ser criado.

  • o procedimento deve ser seguro de forma que, se por qualquer motivo a alteração falhar, desejo que a mesma cópia de trabalho e estado do repositório sejam restaurados como antes da alteração. Por outras palavras, se a alteração em si pode falhar, deve haver um procedimento à prova de falhas para restaurar a cópia de trabalho e o estado do repositório. Estou me referindo a "falhas" que estão na natureza do procedimento de alteração (como, por exemplo, conflitos), não a problemas relacionados ao sistema de arquivos (como restrições de acesso, não ser possível bloquear um arquivo para gravação, ... )

Atualização (1):

  • o procedimento deve ser automatizável , para que possa ser executado por um cliente da GUI sem nenhuma interação do usuário necessária.

Atualização (2):

  • os arquivos no diretório de trabalho não devem ser tocados (pode haver bloqueios do sistema de arquivos em certos arquivos modificados). Isso significa especialmente que uma abordagem possível pode em nenhum momento exigir um diretório de trabalho limpo.
mstrap
fonte

Respostas:

289

Com o lançamento do Mercurial 2.2 , você pode usar a --amendopção with hg commitpara atualizar a última confirmação com o diretório de trabalho atual

Na referência da linha de comandos :

O sinalizador --amend pode ser usado para alterar o pai do diretório de trabalho com um novo commit que contém as alterações no pai, além daquelas atualmente relatadas pelo status hg, se houver alguma. A confirmação antiga é armazenada em um pacote de backup em .hg / strip-backup (consulte hg help bundle e hg help unbundle sobre como restaurá-lo).

Mensagem, usuário e data são obtidos da confirmação alterada, a menos que especificado. Quando uma mensagem não é especificada na linha de comando, o editor será aberto com a mensagem da confirmação alterada.

O melhor é que esse mecanismo é "seguro", porque conta com o relativamente novo recurso "Fases" para impedir atualizações que alterariam o histórico já disponibilizado fora do repositório local.

Chris Phillips
fonte
2
Boa resposta! A extensão evoluir experimental permite que você altere com segurança confirmações não principais . O antigo commit será marcado como obsoleto e oculto. Com um servidor que não é de publicação, você pode fazer isso com segurança depois de enviar os conjuntos de alterações.
Martin Geisler
5
Para atualizar uma mensagem no último commit: hg cometer --amend -m "esta é a minha nova mensagem"
Jay Sheth
52

Você tem 3 opções para editar confirmações no Mercurial:

  1. hg strip --keep --rev -1desfazer o último (1) commit (s), para que você possa fazê-lo novamente (consulte esta resposta para obter mais informações).

  2. Usando a extensão MQ , fornecida com o Mercurial

  3. Mesmo que não seja fornecido com o Mercurial, vale a pena mencionar a extensão Histedit

Você também pode dar uma olhada na página Histórico de edições do wiki Mercurial.

Em resumo, o histórico de edição é realmente difícil e desencorajado . E se você já fez suas alterações, quase não há nada que possa fazer, exceto se você tiver controle total de todos os outros clones.

Eu não estou realmente familiarizado com o git commit --amendcomando, mas AFAIK, Histedit é o que parece ser a abordagem mais próxima, mas, infelizmente, ele não é fornecido com o Mercurial. O MQ é realmente complicado de usar, mas você pode fazer quase qualquer coisa com ele.

krtek
fonte
1
Não sei por que perdi a reversão, mas parece fazer (quase) o que quero. O único problema é que, quando um arquivo é removido para o meu commit original e ressuscita para o meu commit alterado: antes da reversão, ele não será revertido, após a reversão, ele será agendado para remoção (mas o arquivo ainda existe em diretório de trabalho)
mstrap 18/11/11
@ Marc Não sei se entendi o seu problema, mas dê uma olhada no comando esquecer, acho que é o que você está procurando.
krtek
Eu não acho que "esquecer" será útil aqui. Aqui está o problema com mais detalhes: (1) Estou na revisão 2 (2) Remova "arquivo" e tenho outras alterações (3) Confirme as alterações, resultando na revisão 3 (4) Agora vou mudar de idéia e decidir "file" não deve ser removido do commit, por isso quero alterar a revisão 3. Por isso, adicionarei novamente "file", que agora não possui versão (5) Agora realizo a reversão: ela redefinirá o dirstate e marcará " arquivo "como removido. (6) Ao executar "hg commit" novamente agora, "file" permanecerá como removido, embora não deva mais ser. Como seria uma correção automatizada para isso?
Mstrap
1
Para a parte automatizada não sei, mas você pode fazer hg revert myfilepara desfazer a exclusão. Talvez adicionar novamente com hg addo arquivo após o rollbacktambém funcionar.
krtek
3
Concordo que o histórico de edição das alterações publicadas deve ser evitado, mas a edição do meu histórico local é um dos destaques de um DVCS. O MQ com seu qimport é pura edição de histórico, AFAICT.
Mstrap
38

GUI equivalente para hg commit --amend:

Isso também funciona na GUI do TortoiseHG (estou usando a versão 2.5):

Passe para a visualização 'Confirmar' ou, na visualização do ambiente de trabalho, selecione a entrada 'diretório de trabalho'. O botão 'Confirmar' tem uma opção chamada 'Alterar revisão atual' (clique na seta suspensa do botão para encontrá-lo).

insira a descrição da imagem aqui

          ||
          ||
          \/

insira a descrição da imagem aqui

Advertência :

Esta opção extra será ativada apenas se a versão comercial for pelo menos 2.2.0 e se a revisão atual não for pública, não for um patch e não tiver filhos. [...]

Clicar no botão chamará 'commit --amend' para 'alterar' a revisão.

Mais informações sobre isso no canal de desenvolvimento THG

Cristian Diaconescu
fonte
Muito útil, obrigado. O THG é inteligente o suficiente para padronizar a mensagem de confirmação (emendar) da mensagem da confirmação anterior - exatamente o que eu queria.
UuDdLrLrSs
7

Estou sintonizando o que a krtek escreveu. Mais especificamente, solução 1:

Premissas:

  • você comprometeu um (!) changeset, mas ainda não o pressionou
  • você deseja modificar este conjunto de alterações (por exemplo, adicionar, remover ou alterar arquivos e / ou a mensagem de confirmação)

Solução:

  • use hg rollbackpara desfazer a última confirmação
  • comprometa-se novamente com as novas mudanças em vigor

A reversão realmente desfaz a última operação. Sua maneira de trabalhar é bastante simples: operações normais no HG serão anexadas apenas aos arquivos; isso inclui uma confirmação. O Mercurial controla os comprimentos dos arquivos da última transação e, portanto, pode desfazer completamente uma etapa truncando os arquivos de volta aos comprimentos antigos.

Lucero
fonte
1
Obrigado pela solução de ajuste (1); resta apenas um pequeno problema com a reversão, consulte meu comentário na solução da krtek.
Mstrap
8
Uma coisa a enfatizar na reversão, porque chama a atenção das pessoas, é que é a última transação no repo que é revertida, não a última confirmação. Portanto, se outra coisa tiver causado uma gravação no repositório, a reversão não ajudará. É uma coisa sutil, mas importante de lembrar. O MQ e o histedit podem ajudar depois que a janela de reversão for fechada, mas ainda apenas até um certo ponto.
Paul S
7

Supondo que você ainda não tenha propagado suas alterações, eis o que você pode fazer.

  • Adicione ao seu .hgrc:

    [extensions]
    mq =
    
  • No seu repositório:

    hg qimport -r0:tip
    hg qpop -a
    

    É claro que você não precisa começar com a revisão zero ou abrir todos os patches; nos últimos apenas um pop ( hg qpop) é suficiente (veja abaixo).

  • remova a última entrada no .hg/patches/seriesarquivo ou os patches que você não gosta. Também é possível reordenar.

  • hg qpush -a; hg qfinish -a
  • remova os .diffarquivos (patches não aplicados) ainda em .hg / patches (deve ser um no seu caso).

Se você não quer para ter de volta tudo de sua correção, você pode editá-lo usando hg qimport -r0:tip(ou similar), em seguida, editar o material e uso hg qrefreshde mesclar as alterações no remendo superior em seu stack. Leia hg help qrefresh.

Ao editar .hg/patches/series, você pode até remover vários patches ou reordenar alguns. Se sua última revisão for 99, você pode apenas usá-lo hg qimport -r98:tip; hg qpop; [edit series file]; hg qpush -a; hg qfinish -a.

Obviamente, esse procedimento é altamente desencorajado e arriscado . Faça um backup de tudo antes de fazer isso!

Como nota de rodapé, eu já fiz isso milhões de vezes em repositórios particulares.

hochl
fonte
Eu também tinha considerado o uso da extensão mq, no entanto, requer muitas operações para as quais algumas delas podem falhar (por exemplo, se arquivos binários estiverem envolvidos). Além disso, ter que editar .hg / patch / série não será aceitável, já que este procedimento deve ser usado dentro de um cliente GUI (Eu atualizei requisitos acima)
mstrap
Hmmm, desculpe que isso não aconteça para você, em um repositório privado isso realmente é muito bom (com backups - eu já destruí um representante com o fadfinger ^^). É bem legal para combinar os patches em um antes de empurrar as mudanças locais que utilizam hg qfold, btw
hochl
+1 por usar o MQ, mas acho que você exagerou. Ele está apenas perguntando sobre como alterar o último commit. Além disso, essa importação irá cair assim que atingir uma mesclagem. 'qimport -r tip; <editar coisas>; qrefresh -e; qfin -a' vai fazer o trabalho (-e para editar a mensagem de commit)
Paul S
verdade, mesclagens são um problema, eu normalmente apenas uso um patch hg import -r<prev>:tip. Uma pena que não há atalho para a versão anterior, como no subversion.
hochl
2

Versões recentes do Mercurial incluem a evolveextensão que fornece o hg amendcomando. Isso permite alterar uma confirmação sem perder o histórico de pré-alterações no seu controle de versão.

hg alterar [OPÇÃO] ... [ARQUIVO] ...

aliases: atualização

combine um changeset com atualizações e substitua-o por um novo

Commits a new changeset incorporating both the changes to the given files
and all the changes from the current parent changeset into the repository.

See 'hg commit' for details about committing changes.

If you don't specify -m, the parent's message will be reused.

Behind the scenes, Mercurial first commits the update as a regular child
of the current parent. Then it creates a new commit on the parent's
parents with the updated contents. Then it changes the working copy parent
to this new combined changeset. Finally, the old changeset and its update
are hidden from 'hg log' (unless you use --hidden with log).

Vejo https://www.mercurial-scm.org/doc/evolution/user-guide.html#example-3-amend-a-changeset-with-evolve para obter uma descrição completa da evolveextensão.

Karl Bartel
fonte
Reutilizar a mesma mensagem de confirmação é um recurso interessante!
MJ #
1

Pode não resolver todos os problemas da pergunta original, mas como esse parece ser o post de fato sobre como o mercurial pode ser alterado para o commit anterior, adicionarei meus 2 centavos de informação.

Se você é como eu, e só deseja modificar a mensagem de confirmação anterior (corrija um erro de digitação, etc.) sem adicionar nenhum arquivo, isso funcionará

hg commit -X 'glob:**' --amend

Sem nenhum padrão de inclusão ou exclusão hg commit, por padrão, todos os arquivos do diretório ativo serão incluídos. A aplicação de padrão -X 'glob:**'excluirá todos os arquivos possíveis, permitindo apenas modificar a mensagem de confirmação.

Funcionalmente, é o mesmo que git commit --amendquando não há arquivos no índice / estágio.

Kaskelotti
fonte
0

Outra solução pode ser usar o uncommitcomando para excluir um arquivo específico da confirmação atual.

hg uncommit [file/directory]

Isso é muito útil quando você deseja manter a confirmação atual e desmarcar alguns arquivos da confirmação (especialmente útil porque files/directoriesforam excluídos).

Sam Liao
fonte