git rebase bez zmiany sygnatur czasowych zatwierdzenia

157

Czy miałoby sens wykonanie operacji z git rebasezachowaniem sygnatur czasowych zatwierdzenia?

Uważam, że konsekwencją byłoby to, że nowa gałąź niekoniecznie będzie miała chronologicznie daty zatwierdzeń. Czy to w ogóle teoretycznie możliwe? (np. użycie poleceń hydraulicznych; tutaj tylko ciekawy)

Jeśli jest to teoretycznie możliwe, to czy w praktyce można przy rebase nie zmieniać znaczników czasu?

Na przykład załóżmy, że mam następujące drzewo:

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

Teraz, gdybym rebase oldbranchna master, data zatwierdzenia zmian od lutego 1984 do Jun 2010. Czy jest możliwe, aby zmienić to zachowanie, tak aby popełnić timestamp nie zmieniło? Ostatecznie uzyskałbym w ten sposób:

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

Czy to w ogóle miałoby sens? Czy jest w ogóle dozwolone w git mieć historię, w której stary commit ma nowszy commit jako rodzic?

Olivier Verdier
źródło
3
To zabawne, że odpowiedź na to pytanie rzeczywiście brzmi „nie musisz nic robić - tak to działa domyślnie”. Ale teraz przypuśćmy, że chcesz, aby zatwierdzenie zostało posortowane w odpowiedniej kolejności podczas wykonywania rebase (co jest całkiem naturalnym scenariuszem, jeśli się nad tym zastanowić). Teraz nie byłem w stanie znaleźć sposobu na osiągnięcie tego i opublikowałem swój q jako stackoverflow.com/questions/12270357/really-flatten-a-git-merge
pfalcon
1
David wspomina inną opcję, aby zresetować committer data: git rebase --committer-date-is-author-date SHA. Zobacz moją zredagowaną odpowiedź poniżej
VonC
Właśnie napisałem obszerną odpowiedź na podobne pytanie, którego autor wypróbował wyjaśnione tutaj odpowiedzi i nie mógł ich zastosować w zadowalający sposób.
Axiac

Odpowiedzi:

149

Aktualizacja z czerwca 2014 r .: David Fraser wspomina w komentarzach o rozwiązaniu opisanym również w sekcjiZmień sygnatury czasowe podczas ponownego bazowania gałęzi git ”, używając opcji --committer-date-is-author-date(wprowadzonej początkowo w styczniu 2009 r. W zatwierdzeniu 3f01ad6

Zwróć uwagę, że --committer-date-is-author-dateopcja wydaje się pozostawić sygnaturę czasową autora i ustawić sygnaturę czasową osoby wysyłającej wiadomość na taką samą, jak oryginalna sygnatura czasowa autora, czego chciał OP Olivier Verdier .

Znalazłem ostatnie zatwierdzenie z poprawną datą i:

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

Zobacz git am:

--committer-date-is-author-date

Domyślnie komenda rejestruje datę z wiadomości e-mail jako datę autora zatwierdzenia i używa czasu utworzenia zatwierdzenia jako daty osoby zatwierdzającej.
Dzięki temu użytkownik może kłamać na temat daty autora, używając tej samej wartości, co data autora .


(Oryginalna odpowiedź, czerwiec 2012)

Możesz spróbować, aby uzyskać nieinteraktywną rebase

git rebase --ignore-date

(z tej odpowiedzi SO )

To jest przekazywane do git am, który wspomina:

 --ignore-date

Domyślnie komenda rejestruje datę z wiadomości e-mail jako datę autora zatwierdzenia i używa czasu utworzenia zatwierdzenia jako daty osoby zatwierdzającej.
Pozwala to użytkownikowi kłamać na temat daty autora, używając tej samej wartości, co data autora.

W przypadku git rebasetej opcji jest to „Niezgodna z opcją --interactive”.

Ponieważ możesz dowolnie zmieniać znacznik czasu starej daty zatwierdzenia (za pomocą git filter-branch), przypuszczam, że możesz uporządkować historię Git z dowolną kolejnością dat zmian , jakiej chcesz / potrzebujesz, a nawet ustawić ją na przyszłość! .


Jak wspomina Olivier w swoim pytaniu, data autora nigdy nie jest zmieniana przez ponowne bazy danych;
Z książki Pro Git :

  • Autorem jest osoba, która pierwotnie napisała pracę,
  • podczas gdy zatwierdzającym jest osoba, która ostatnio zastosowała pracę.

Tak więc, jeśli wyślesz łatkę do projektu i jeden z głównych członków założy ją, oboje otrzymacie uznanie.

Aby było jasne, w tym przypadku, jak komentuje Olivier:

--ignore-daterobi przeciwieństwo tego, co starałem się osiągnąć !
Mianowicie usuwa sygnaturę czasową autora i zastępuje je znacznikami czasu zatwierdzeń!
Zatem prawidłowa odpowiedź na moje pytanie brzmi:
nie rób nic, ponieważ git rebase tak naprawdę nie zmienia domyślnie sygnatur czasowych autorów.


VonC
źródło
1
Ciekawe o arbitralnych datach popełnienia. Jednak git rebase --ignore-datenie działa. Zmienia daty ponownych zatwierdzeń.
Olivier Verdier
@Olivier: dziwne: nie popełniłeś non -interactive rebase? A między datą autora a datą zatwierdzającego, czy na pewno będziemy monitorować „właściwą” datę?
VonC
1
Dzięki, VonC, różnica między sygnaturą czasową autora i autora, dzięki temu wszystko staje się jasne. Napisałem odpowiedź na moje pytanie w moim poście, ale możesz dostosować swoją odpowiedź, aby to odzwierciedlić.
Olivier Verdier,
4
dokładniej: --ignore-datedziała odwrotnie niż ja próbowałem osiągnąć! Mianowicie usuwa sygnaturę czasową autora i zastępuje je znacznikami czasu zatwierdzeń! Tak więc prawidłowa odpowiedź na moje pytanie brzmi: nie rób nic, ponieważ git rebasetak naprawdę nie zmienia domyślnie sygnatur czasowych autorów.
Olivier Verdier
5
Zwróć uwagę, że --committer-date-is-author-dateopcja wydaje się pozostawić sygnaturę czasową autora i ustawić sygnaturę czasową autora, aby była taka sama, jak oryginalna sygnatura czasowa autora, czego chciał Olivier ...
David Fraser
118

Jeśli już schrzaniłeś daty zatwierdzenia (być może z rebase) i chcesz zresetować je do odpowiednich dat autorów, możesz uruchomić:

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

Andy
źródło
1
Właśnie tego próbowałem, ale bez efektu. Mam wyjście folowing: WARNING: Ref 'refs/heads/master' is unchanged. Używam git w wersji 1.7.9.5 w systemie Linux (64-bitowy)
Markus N.
20
Chciałbym dodać inne podejście, jeśli już schrzaniłeś, ale nie chcesz iterować przez całą historię: w git rebase --committer-date-is-author-date <base_branch> ten sposób git zresetuje datę zatwierdzenia tylko dla zatwierdzeń zastosowanych w <base_branch> (co jest prawdopodobnie ta sama nazwa gałęzi, której użyłeś, kiedy schrzaniłeś).
speakman
Zaakceptowana odpowiedź nie zadziałała w 2016 roku, ale odpowiedź @ speakman zadziałała!
Theodore R. Smith
2
Odpowiedź @ speakman nie zadziałała w październiku 2016, ale Andy's tak!
Amedee Van Gasse
2
To nie działa w systemie Windows. Udało mi się go uruchomić za pomocą Windows Bash.
vaindil
33

Kluczową kwestią Von C pomógł mi zrozumieć, co się dzieje: gdy rebase tego, committer za zmiany datownika, ale nie autora datownik, który nagle wszystko ma sens. Więc moje pytanie nie było wystarczająco precyzyjne.

Odpowiedź jest taka, że ​​rebase w rzeczywistości nie zmienia sygnatur czasowych autora (nie musisz w tym celu nic robić), co mi odpowiada.

Olivier Verdier
źródło
3
+1 - mam na miejscu alias git ( coderwall.com/p/euwpig/a-better-git-log ), który najwyraźniej używa sygnatury czasowej osoby zatwierdzającej, co mnie wprawiało w zakłopotanie. Gitk i git log pokazują sygnaturę czasową autora.
1615903
15

Domyślnie git rebase ustawi sygnaturę czasową osoby zatwierdzającej na czas utworzenia nowego zatwierdzenia, ale pozostawi nienaruszoną sygnaturę czasową autora. W większości przypadków jest to pożądane zachowanie, ale w niektórych scenariuszach nie chcemy również zmieniać sygnatury czasowej komendanta. Jak możemy to osiągnąć? Cóż, oto sztuczka, którą zwykle robię.

Po pierwsze, upewnij się, że każde z zatwierdzeń, które zamierzasz przebudować, ma unikalny komunikat o zatwierdzeniu i znacznik czasu autora (w tym miejscu sztuczka wymaga ulepszeń, ale obecnie odpowiada moim potrzebom).

Przed rebase zapisz sygnaturę czasową autora, znacznik czasu autora i informację o zatwierdzeniu wszystkich zatwierdzeń, które zostaną przeniesione do pliku.

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

Następnie pozwól, aby rebase nastąpił.

Na koniec zamieniamy sygnaturę czasową bieżącego autora na ten zapisany w pliku, jeśli komunikat zatwierdzenia jest taki sam przez using 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'

Jeśli coś pójdzie nie tak, po prostu przejdź do kasy git refloglub wszystkie refs/original/referencje.

Furthormore, możesz zrobić to samo ze znacznikiem czasu autora.

Na przykład, jeśli sygnatura czasowa autora niektórych zatwierdzeń jest poza kolejnością i bez zmiany kolejności tych zatwierdzeń, po prostu chcemy, aby sygnatura czasowa autora była wyświetlana w kolejności, pomocne będą następujące polecenia.

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'
weynhamz
źródło
To świetna sztuczka! Pozwoliło mi to przepisać tylko 75 zatwierdzeń zamiast 1100+ z innych odpowiedzi.
audun
To jest fantastyczne! Czy istnieje sposób na zmodyfikowanie skryptu tak, aby zachował również oryginalny committer?
David DeMar
@DavidDeMar powinien być taki sam, po prostu zmień dziennik git --pretty, aby nagrać oryginalny e-mail i odpowiednio zmodyfikuj skrypt.
weynhamz,