Mercurial: come modificare l'ultimo commit?


211

Sto cercando una controparte di git commit --amendMercurial, ovvero un modo per modificare il commit a cui è collegata la mia copia di lavoro. Sono interessato solo all'ultimo commit, non a un commit precedente arbitrario.

I requisiti per questa procedura di modifica sono:

  • se possibile, non dovrebbe richiedere alcuna estensione. Non deve richiedere estensioni non predefinite , ovvero estensioni non fornite con un'installazione Mercurial ufficiale.

  • se l'impegno di modificare è un capo del mio ramo attuale, non dovrebbe essere creato alcun nuovo capo . Se il commit non è head, è possibile creare un nuovo head.

  • la procedura dovrebbe essere sicura in modo tale che se per qualsiasi motivo la modifica fallisce, voglio avere lo stesso stato di copia e di repository di lavoro ripristinato come prima della modifica. In altre parole, se la modifica stessa può fallire, dovrebbe esserci una procedura fail-safe per ripristinare lo stato della copia di lavoro e del repository. Mi riferisco a "guasti" che si trovano nella natura della procedura di modifica (come ad esempio conflitti), non a problemi relativi al file system (come restrizioni di accesso, non essere in grado di bloccare un file per la scrittura, ... )

Aggiornamento (1):

  • la procedura deve essere automatizzabile , quindi può essere eseguita da un client GUI senza alcuna interazione da parte dell'utente.

Aggiornamento (2):

  • i file nella directory di lavoro non devono essere toccati (potrebbero esserci blocchi del file system su alcuni file modificati). Ciò significa in particolare che un possibile approccio non può mai richiedere una directory di lavoro pulita.

Risposte:


289

Con il rilascio di Mercurial 2.2 , è possibile utilizzare l' --amendopzione con hg commitper aggiornare l'ultimo commit con la directory di lavoro corrente

Dal riferimento della riga di comando :

Il flag --amend può essere usato per modificare il genitore della directory di lavoro con un nuovo commit che contiene le modifiche nel genitore oltre a quelle attualmente riportate dallo stato di hg, se ce ne sono. Il vecchio commit è archiviato in un bundle di backup in .hg / strip-backup (consultare hg help bundle e hg help unbundle su come ripristinarlo).

Il messaggio, l'utente e la data sono presi dal commit modificato se non diversamente specificato. Quando un messaggio non viene specificato nella riga di comando, l'editor si aprirà con il messaggio del commit modificato.

Il bello è che questo meccanismo è "sicuro", perché si basa sulla relativamente nuova funzione "Fasi" per impedire aggiornamenti che potrebbero cambiare la cronologia che è già stata resa disponibile al di fuori del repository locale.


2
Buona risposta! L' estensione sperimentale evoluta consente di modificare in modo sicuro i commit non head . Il vecchio commit verrà contrassegnato come obsoleto e nascosto. Con un server non di pubblicazione, puoi persino farlo in modo sicuro dopo aver inviato le modifiche.
Martin Geisler,

5
Per aggiornare il messaggio sull'ultimo commit: hg commit --amend -m "questo è il mio nuovo messaggio"
Jay Sheth,

52

Hai 3 opzioni per modificare i commit in Mercurial:

  1. hg strip --keep --rev -1annulla gli ultimi (1) commit (s), in modo da poterlo ripetere (vedi questa risposta per maggiori informazioni).

  2. Utilizzando l' estensione MQ , fornita con Mercurial

  3. Anche se non viene fornito con Mercurial, l' estensione Histedit merita di essere menzionata

Puoi anche dare un'occhiata alla Cronologia delle modifiche pagina delle modifiche del wiki Mercurial.

In breve, la cronologia delle modifiche è davvero difficile e scoraggiata . E se hai già spinto le tue modifiche, non c'è quasi niente che puoi fare, tranne se hai il controllo totale di tutti gli altri cloni.

Non ho molta familiarità con il git commit --amendcomando, ma AFAIK, Histedit è quello che sembra essere l'approccio più vicino, ma purtroppo non viene fornito con Mercurial. MQ è davvero complicato da usare, ma puoi farci quasi tutto.


1
Non sono sicuro del motivo per cui ho perso il rollback, ma sembra fare (quasi) quello che voglio. L'unico problema è che quando un file è stato rimosso per il mio commit originale ed è stato resuscitato per il mio commit modificato: prima del rollback verrà annullato, dopo il rollback verrà pianificato per la rimozione (ma il file esiste ancora in la directory di lavoro)
mstrap,

@Marc Non sono sicuro di aver capito il tuo problema, ma dai un'occhiata al comando di dimenticanza, penso sia quello che stai cercando.
Krtek,

Non credo che "dimenticare" sarà utile qui. Ecco il problema in modo più dettagliato: (1) Sono alla revisione 2 (2) Rimuovi "file" e ho alcune altre modifiche (3) Conferma modifiche, con conseguente revisione 3 (4) Ora cambierò idea e deciderò "file" non dovrebbe essere rimosso dal commit, quindi voglio modificare la revisione 3. Quindi, aggiungerò di nuovo "file" che ora è senza revisione (5) Ora eseguo il rollback: ripristinerà il dirstate e segnerà " file "come rimosso. (6) Quando esegui di nuovo "hg commit" ora, "file" rimarrà come rimosso, anche se non dovrebbe essere più. Come potrebbe apparire una correzione automatica per quella?
mstrap,

1
Per la parte automatizzata non lo so, ma puoi fare hg revert myfileper annullare la cancellazione. Forse aggiungere nuovamente hg addil file dopo che rollbackfunziona anche.
Krtek,

3
Concordo sul fatto che la cronologia delle modifiche delle modifiche pubblicate dovrebbe essere evitata, ma la modifica della mia cronologia locale è uno dei punti salienti di un DVCS. MQ con il suo qimport è puro editing della storia, AFAICT.
mstrap,

38

Equivalente GUI per hg commit --amend:

Questo funziona anche dalla GUI di TortoiseHG (sto usando la v2.5):

Passare alla vista "Commit" o, nella vista workbench, selezionare la voce "directory di lavoro". Il pulsante "Commit" ha un'opzione denominata "Modifica la revisione corrente" (fare clic sulla freccia a discesa del pulsante per trovarla).

inserisci qui la descrizione dell'immagine

          ||
          ||
          \/

inserisci qui la descrizione dell'immagine

Avvertimento :

Questa opzione aggiuntiva sarà abilitata solo se la versione mercurial è almeno 2.2.0 e se la revisione corrente non è pubblica, non è una patch e non ha figli. [...]

Facendo clic sul pulsante si chiamerà 'commit --amend' per 'modificare' la revisione.

Maggiori informazioni al riguardo sul canale di sviluppo THG


Molto utile, grazie. THG è abbastanza intelligente da preimpostare il messaggio di commit (modifica) sul messaggio dal commit precedente - proprio quello che volevo.
UuDdLrLrSs

7

Mi sto sintonizzando su ciò che ha scritto Krtek. Più specificamente soluzione 1:

ipotesi:

  • hai commesso un (!) changeset ma non l'hai ancora fatto
  • si desidera modificare questo changeset (ad es. aggiungere, rimuovere o cambiare file e / o il messaggio di commit)

Soluzione:

  • utilizzare hg rollbackper annullare l'ultimo commit
  • impegnarsi nuovamente con le nuove modifiche in atto

Il rollback annulla davvero l'ultima operazione. Il suo modo di lavorare è abbastanza semplice: le normali operazioni in HG si aggiungono solo ai file; questo include un commit. Mercurial tiene traccia delle lunghezze dei file dell'ultima transazione e può quindi annullare completamente un passaggio troncando i file alle loro lunghezze precedenti.


1
Grazie per la soluzione di ottimizzazione (1); c'è solo un piccolo problema rimasto con il rollback, per favore vedi il mio commento sulla soluzione di Krtek.
mstrap,

8
Una cosa da sottolineare sul rollback, perché attira le persone, è che è l'ultima transazione sul repository che viene ripristinata, non l'ultimo commit. Quindi, se qualcos'altro ha causato una scrittura nel repository, il rollback non sarà di aiuto. È una cosa sottile ma importante da ricordare. MQ e histedit possono essere d'aiuto una volta chiusa la finestra di rollback, ma solo fino a un certo punto.
Paul S,

7

Supponendo che non hai ancora propagato le modifiche, ecco cosa puoi fare.

  • Aggiungi al tuo .hgrc:

    [extensions]
    mq =
    
  • Nel tuo repository:

    hg qimport -r0:tip
    hg qpop -a
    

    Ovviamente non è necessario iniziare con la revisione zero o pop tutte le patch, per l'ultimo è sufficiente solo un pop ( hg qpop) (vedi sotto).

  • rimuovi l'ultima voce nel .hg/patches/seriesfile o le patch che non ti piacciono. Anche il riordino è possibile.

  • hg qpush -a; hg qfinish -a
  • rimuovi i .difffile (patch non applicate) ancora in .hg / patches (dovrebbe essere uno nel tuo caso).

Se non si vuole per riprendere tutto della vostra patch, è possibile modificarlo utilizzando hg qimport -r0:tip(o simili), quindi modificare roba e l'uso hg qrefreshper unire le modifiche nella patch più in alto sul tuo stack. Leggi hg help qrefresh.

Modificando .hg/patches/series, è anche possibile rimuovere diverse patch o riordinarne alcune. Se la tua ultima revisione è 99, puoi semplicemente usare hg qimport -r98:tip; hg qpop; [edit series file]; hg qpush -a; hg qfinish -a.

Naturalmente, questa procedura è altamente scoraggiata e rischiosa . Fai un backup di tutto prima di farlo!

Come sidenote, l'ho fatto miliardi di volte su repository solo privati.


Avevo anche preso in considerazione l'uso di mq-extension, tuttavia richiede molte operazioni per le quali alcuni potrebbero non riuscire (ad esempio se sono coinvolti file binari). Inoltre, dover modificare .hg / patch / series non sarà accettabile, poiché questa procedura dovrebbe essere utilizzata all'interno di un client GUI (ho aggiornato i requisiti sopra)
mstrap

Hmmm, mi dispiace che questo non faccia per te, su un repository privato questo ti dà davvero un calcio (con i backup - ho già distrutto un rappresentante con esso ^^). È abbastanza bello combinare le patch in una prima di hg qfold
inviare

+1 per l'utilizzo di MQ, ma penso che tu abbia esagerato. Sta solo chiedendo di modificare l'ultimo commit. Inoltre, tale importazione si interromperà non appena raggiungerà un'unione. 'qimport -r tip; <modifica roba>; qrefresh -e; qfin -a 'farà il lavoro (-e per modificare il messaggio di commit)
Paul S

vero, le fusioni sono un problema, di solito pop solo una patch e uso hg import -r<prev>:tip. Un peccato che non ci siano scorciatoie per la versione precedente, come nella sovversione.
Hochl,

2

Le versioni recenti di Mercurial includono l' evolveestensione che fornisce il hg amendcomando. Ciò consente di modificare un commit senza perdere la cronologia di pre-modifica nel controllo della versione.

hg modifica [OPZIONE] ... [FILE] ...

alias: aggiorna

combinare un changeset con gli aggiornamenti e sostituirlo con uno nuovo

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).

Vedi https://www.mercurial-scm.org/doc/evolution/user-guide.html#example-3-amend-a-changeset-with-evolve per una descrizione completa evolvedell'estensione.


Riutilizzare lo stesso messaggio di commit è una bella funzionalità!
mpen

1

Potrebbe non risolvere tutti i problemi della domanda originale, ma dal momento che questo sembra essere il post di fatto su come il mercuriale può modificare il precedente impegno, aggiungerò i miei 2 centesimi di informazioni.

Se sei come me e desideri solo modificare il precedente messaggio di commit (correggi un errore di battitura, ecc.) Senza aggiungere alcun file, funzionerà

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

Senza alcun modello di inclusione o esclusione hg commit, per impostazione predefinita verranno inclusi tutti i file nella directory di lavoro. L'applicazione del modello -X 'glob:**'esclude tutti i file possibili, consentendo solo di modificare il messaggio di commit.

Funzionalmente è lo stesso di git commit --amendquando non ci sono file nell'indice / fase.


0

Un'altra soluzione potrebbe essere utilizzare il uncommitcomando per escludere un file specifico dal commit corrente.

hg uncommit [file/directory]

Questo è molto utile quando si desidera mantenere il commit corrente e deselezionare alcuni file dal commit (particolarmente utile per files/directoriesessere stato eliminato).

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.