git rebase senza cambiare i timestamp di commit


157

Avrebbe senso esibirsi git rebasepreservando i timestamp di commit?

Credo che una conseguenza sarebbe che la nuova filiale non dovrà necessariamente impegnare le date in ordine cronologico. È teoricamente possibile? (es. usando i comandi idraulici; solo curioso qui)

Se è teoricamente possibile, allora è possibile in pratica con rebase, non cambiare i timestamp?

Ad esempio, supponiamo che io abbia il seguente albero:

master <jun 2010>
  |
  :
  :
  :     oldbranch <feb 1984>
  :     /
oldcommit <jan 1984>

Ora, se io rebase oldbranchsu master, alla data di commit delle modifiche da febbraio 1984 al giugno 2010. E 'possibile modificare l'azione in modo che il commit timestamp non è cambiato? Alla fine otterrei così:

      oldbranch <feb 1984>
      /
 master <jun 2010>
    |
    :

Avrebbe senso a tutti? È anche permesso a Git di avere una cronologia in cui un vecchio commit ha un commit più recente come genitore?


3
È strano che la risposta alla domanda sia davvero "non devi fare nulla - è così che funziona come predefinito". Ma ora supponiamo che tu voglia che il commit sia ordinato nell'ordine corretto della data mentre fai rebase (che è uno scenario abbastanza naturale se ci pensi). Ora, io non sono riuscito a trovare il modo per ottenere questo, e inviato il mio q come stackoverflow.com/questions/12270357/really-flatten-a-git-merge
pfalcon

1
David cita un'altra opzione per reimpostare la data committer: git rebase --committer-date-is-author-date SHA. Vedi la mia risposta modificata di seguito
VonC

Ho appena scritto una risposta esauriente su una domanda simile il cui autore ha provato le risposte spiegate qui e non ha potuto applicarle in modo soddisfacente.
axiac,

Risposte:


149

Aggiornamento giugno 2014: David Fraser menziona nei commenti una soluzione dettagliata anche in " Modifica i timestamp durante il rebasing del ramo git ", utilizzando l'opzione --committer-date-is-author-date(introdotta inizialmente nel gennaio 2009 in commit 3f01ad6

Si noti che l' --committer-date-is-author-dateopzione sembra lasciare il timestamp dell'autore e impostare il timestamp del committente in modo che corrisponda al timestamp dell'autore originale, che è ciò che voleva l' OP Olivier Verdier .

Ho trovato l'ultimo commit con la data corretta e ho fatto:

git rebase --committer-date-is-author-date SHA

Vedi git am:

--committer-date-is-author-date

Per impostazione predefinita, il comando registra la data dal messaggio di posta elettronica come data dell'autore del commit e utilizza l'ora della creazione del commit come data del committer.
Ciò consente all'utente di mentire sulla data del committer utilizzando lo stesso valore della data dell'autore .


(Risposta originale, giugno 2012)

Potresti provare, per un rebase non interattivo

git rebase --ignore-date

(da questa risposta SO )

Questo viene passato a git am, che menziona:

 --ignore-date

Per impostazione predefinita, il comando registra la data dal messaggio di posta elettronica come data dell'autore del commit e utilizza l'ora della creazione del commit come data del committer.
Ciò consente all'utente di mentire sulla data dell'autore utilizzando lo stesso valore della data del committer.

Per git rebase, questa opzione è "Incompatibile con l'opzione --interactive".

Dal momento che puoi modificare a piacimento il timestamp della vecchia data di commit (con git filter-branch), suppongo che tu possa organizzare la tua cronologia Git con qualsiasi ordine di data di commit tu voglia / desideri, anche impostarlo sul futuro! .


Come menziona Olivier nella sua domanda, la data dell'autore non è mai cambiata da un ribasso;
Dal libro Pro Git :

  • L'autore è la persona che ha originariamente scritto l'opera,
  • mentre il committer è la persona che ha applicato l'ultima volta il lavoro.

Pertanto, se invii una patch a un progetto e uno dei membri principali applica la patch, entrambi riceverai credito.

Per essere più chiari, in questo caso, come commenta Olivier:

la --ignore-datefa il contrario di quello che stavo cercando di realizzare !
Vale a dire, cancella il timestamp dell'autore e li sostituisce con i timestamp di commit!
Quindi la risposta giusta alla mia domanda è:
non fare nulla, poiché in git rebase realtà non cambia i timestamp degli autori per impostazione predefinita.



1
Interessante le date arbitrarie da impegnare. Tuttavia, git rebase --ignore-datenon funziona. Cambia le date degli commit rinnovati.
Olivier Verdier,

@Olivier: strano: hai fatto un rebase non interattivo? E tra la data dell'autore e la data del committer, siamo sicuri di monitorare la data "giusta"?
VonC,

1
Grazie VonC, la differenza tra data e ora dell'autore e del committer, è ciò che rende tutto chiaro all'improvviso. Ho scritto la risposta alla mia domanda nel mio post, ma sentiti libero di adattare la tua risposta per riflettere ciò.
Olivier Verdier,

4
per essere più precisi: --ignore-datefa il contrario di quello che stavo cercando di ottenere! Vale a dire, cancella il timestamp dell'autore e li sostituisce con i timestamp di commit! Quindi la risposta giusta alla mia domanda è: non fare nulla, dal momento git rebaseche in realtà non cambia i timestamp degli autori per impostazione predefinita.
Olivier Verdier,

5
Si noti che l' --committer-date-is-author-dateopzione sembra lasciare il timestamp dell'autore e impostare il timestamp del committer in modo che corrisponda al timestamp dell'autore originale, che è ciò che Olivier voleva ...
David Fraser,

118

Se hai già rovinato le date di commit (magari con un rebase) e vuoi ripristinarle alle date degli autori corrispondenti, puoi eseguire:

git filter-branch --env-filter 'GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE; export GIT_COMMITTER_DATE'


1
Ho appena provato questo, ma nessun effetto. Ho avuto l'output folowing: WARNING: Ref 'refs/heads/master' is unchanged. Sto usando git versione 1.7.9.5 su Linux (64 bit)
Markus N.

20
Vorrei aggiungere un altro approccio se hai già rovinato ma non vuoi scorrere l'intera storia: in git rebase --committer-date-is-author-date <base_branch> questo modo, git ripristinerà la data di commit solo per i commit applicati su <base_branch> (che è probabilmente il stesso nome di ramo che hai usato quando hai rovinato).
speakman,

La risposta accettata non ha funzionato nel 2016, ma la risposta di @ speakman ha funzionato!
Theodore R. Smith,

2
La risposta di @ speakman non ha funzionato nell'ottobre 2016, ma Andy ha fatto!
Amedee Van Gasse,

2
Questo non funziona su Windows. Sono stato in grado di farlo funzionare con Windows Bash.
vaindil,

33

Una domanda cruciale di Von C mi ha aiutato a capire cosa sta succedendo: quando il tuo rebase, il timestamp del committer cambia, ma non il timestamp dell'autore , che all'improvviso ha senso. Quindi la mia domanda non era in realtà abbastanza precisa.

La risposta è che rebase in realtà non cambia i timestamp dell'autore (non è necessario fare nulla per quello), il che mi si adatta perfettamente.


3
+1 - Ho un alias git in atto ( coderwall.com/p/euwpig/a-better-git-log ) che apparentemente usa il timestamp del committer, che mi stava confondendo. Gitk e git log mostrano entrambi il timestamp dell'autore.
1615903,

15

Per impostazione predefinita, git rebase imposterà il timestamp del committer sul momento in cui viene creato il nuovo commit, ma manterrà intatto il timestamp dell'autore. Il più delle volte, questo è il comportamento desiderato, ma in alcuni scenari, non vogliamo nemmeno cambiare il timestamp del committer. Come possiamo realizzarlo? Bene, ecco il trucco che faccio di solito.

Innanzitutto, assicurati che ciascuno dei commit che stai per riformulare abbia un messaggio di commit unico e un timestamp dell'autore (Qui è dove il trucco ha bisogno di miglioramenti, ma al momento è adatto alle mie esigenze).

Prima del rebase, registra il timestamp del committer, il timestamp dell'autore e il messaggio di commit di tutti i commit che verranno riassegnati in un file.

#NOTE: BASE is the commit where your rebase begins
git log --pretty='%ct %at %s' BASE..HEAD > hashlog

Quindi, lascia che abbia luogo il rebase effettivo.

Infine, sostituiamo il timestamp del committer corrente con quello registrato nel file se il messaggio di commit è lo stesso usando git filter-branch.

 git filter-branch --env-filter '__date=$(__log=$(git log -1 --pretty="%at %s" $GIT_COMMIT); grep -m 1 "$__log" ../../hashlog | cut -d" " -f1); test -n "$__date" && export GIT_COMMITTER_DATE=$__date || cat'

Se qualcosa va storto, basta fare il checkout git reflogo tutti gli refs/original/ref.

Inoltre, puoi fare qualcosa di simile al timestamp dell'autore.

Ad esempio, se il timestamp di alcuni commit dell'autore è fuori servizio e senza riorganizzare questi commit, vogliamo solo che il timestamp dell'autore venga visualizzato in ordine, quindi i seguenti comandi aiuteranno.

git log --pretty='%at %s' COMMIT1..COMMIT2 > hashlog
join -1 1 -2 1 <(cat hashlog | cut -f 1 | sort -nr | awk '{ print NR" "$1 }') <(cat hashlog | awk '{ print NR" "$0 }') | cut -d" " -f2,4- > hashlog_
mv hashlog_ hashlog
git filter-branch --env-filter '__date=$(__log=$(git log -1 --pretty="%s" $GIT_COMMIT); grep -m 1 "$__log" ../../hashlog | cut -d" " -f1); test -n "$__date" && export GIT_AUTHOR_DATE=$__date || cat'

Questo è un grande trucco! Mi ha permesso di riscrivere solo 75 commit anziché 1100+ dall'uso delle altre risposte.
audun,

È fantastico! C'è un modo per modificare lo script per preservare anche il committer originale?
David DeMar,

@DavidDeMar dovrebbe essere lo stesso, basta cambiare il registro git --pretty per registrare l'e-mail originale e modificare lo script di conseguenza.
weynhamz,
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.