git rebase tanpa mengubah stempel waktu komit

157

Apakah masuk akal untuk melakukan git rebasesambil menjaga stempel waktu komit?

Saya percaya konsekuensinya adalah bahwa cabang baru tidak harus memiliki tanggal secara kronologis. Apakah secara teori itu mungkin? (mis. menggunakan perintah pipa ledeng; hanya ingin tahu di sini)

Jika secara teori memungkinkan, maka apakah mungkin dalam praktik dengan rebase, bukan untuk mengubah cap waktu?

Misalnya, anggap saya memiliki pohon berikut:

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

Sekarang, jika saya oldbranchmulai lagi master, tanggal komit berubah dari Februari 1984 menjadi Juni 2010. Apakah mungkin mengubah perilaku itu sehingga stempel waktu komit tidak berubah? Pada akhirnya saya akan mendapatkan:

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

Apakah itu masuk akal? Apakah bahkan diizinkan di git untuk memiliki riwayat di mana komit lama memiliki komit yang lebih baru sebagai orangtua?

Olivier Verdier
sumber
3
Memang funky bahwa jawaban untuk pertanyaan itu memang "Anda tidak perlu melakukan apa pun - itu cara kerjanya menjadi default". Tapi sekarang anggaplah Anda ingin komit untuk disortir dalam urutan tanggal yang tepat saat melakukan rebase (yang merupakan skenario yang cukup alami jika Anda memikirkannya). Sekarang, saya tidak dapat menemukan cara untuk mencapai itu, dan memposting q saya sebagai stackoverflow.com/questions/12270357/really-flatten-a-git-merge
pfalcon
1
David menyebutkan pilihan lain untuk mengatur ulang tanggal committer: git rebase --committer-date-is-author-date SHA. Lihat jawaban saya yang diedit di bawah
VonC
Saya hanya menulis jawaban yang luas pada pertanyaan serupa yang penulisnya mencoba jawaban yang dijelaskan di sini dan tidak dapat menerapkannya dengan cara yang memuaskan.
Aksioma

Jawaban:

149

Pembaruan Juni 2014: David Fraser menyebutkan dalam komentar sebuah solusi yang juga dirinci dalam " Ubah stempel waktu saat rebasing cabang git ", menggunakan opsi --committer-date-is-author-date(diperkenalkan awalnya pada Januari 2009 di komit 3f01ad6

Perhatikan bahwa --committer-date-is-author-dateopsi tersebut tampaknya meninggalkan stempel waktu penulis, dan setel stempel waktu pengganti menjadi sama dengan stempel waktu penulis asli, yang diinginkan OP Olivier Verdier .

Saya menemukan komit terakhir dengan tanggal yang benar dan melakukan:

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

Lihat git am:

--committer-date-is-author-date

Secara default, perintah mencatat tanggal dari pesan email sebagai tanggal pembuat komit, dan menggunakan waktu pembuatan komit sebagai tanggal komiter.
Ini memungkinkan pengguna untuk berbohong tentang tanggal committer dengan menggunakan nilai yang sama dengan tanggal penulis .


(Jawaban asli, Juni 2012)

Anda dapat mencoba, untuk rebase non-interaktif

git rebase --ignore-date

(dari jawaban SO ini )

Ini diteruskan ke git am, yang menyebutkan:

 --ignore-date

Secara default, perintah mencatat tanggal dari pesan email sebagai tanggal pembuat komit, dan menggunakan waktu pembuatan komit sebagai tanggal komiter.
Ini memungkinkan pengguna untuk berbohong tentang tanggal penulis dengan menggunakan nilai yang sama dengan tanggal committer.

Sebab git rebase, opsi ini "Tidak kompatibel dengan opsi --interaktif."

Karena Anda dapat mengubah kapan saja cap waktu komit lama (dengan git filter-branch), saya kira Anda dapat mengatur riwayat Git Anda dengan urutan tanggal komit apa pun yang Anda inginkan / butuhkan, bahkan atur ke masa depan! .


Seperti Olivier menyebutkan dalam pertanyaannya, tanggal penulis tidak pernah diubah oleh rebase;
Dari Buku Pro Git :

  • Penulis adalah orang yang awalnya menulis karya tersebut,
  • sedangkan committer adalah orang yang terakhir mengaplikasikan pekerjaan.

Jadi, jika Anda mengirim tambalan ke proyek dan salah satu anggota inti menerapkan tambalan, Anda berdua mendapatkan kredit.

Untuk lebih jelas, dalam hal ini, seperti komentar Olivier:

yang --ignore-datemelakukan kebalikan dari apa yang saya capai !
Yaitu, menghapus stempel waktu penulis dan menggantinya dengan stempel waktu komitmen!
Jadi jawaban yang tepat untuk pertanyaan saya adalah:
Jangan lakukan apa-apa, karena git rebase sebenarnya tidak mengubah stempel waktu penulis secara default.


VONC
sumber
1
Menarik tentang tanggal sewenang-wenang untuk dilakukan. Namun, git rebase --ignore-datetidak berhasil. Itu mengubah tanggal dari komitmen yang diubah.
Olivier Verdier
@Olivier: aneh: apakah Anda membuat rebase non- interaktif? Dan antara Tanggal Penulis dan Tanggal Committer, apakah kami yakin akan memonitor tanggal "benar"?
VonC
1
Terima kasih VonC, perbedaan antara cap waktu pengarang dan pengendara, itulah yang membuat semuanya tiba-tiba menjadi jelas. Saya menulis jawaban untuk pertanyaan saya di posting saya, tetapi merasa bebas untuk menyesuaikan jawaban Anda untuk mencerminkan itu.
Olivier Verdier
4
untuk lebih tepatnya: yang --ignore-datemelakukan kebalikan dari apa yang saya capai! Yaitu, menghapus stempel waktu penulis dan menggantinya dengan stempel waktu komitmen! Jadi jawaban yang tepat untuk pertanyaan saya adalah: jangan lakukan apa-apa, karena git rebasesebenarnya tidak mengubah stempel waktu penulis secara default.
Olivier Verdier
5
Perhatikan bahwa --committer-date-is-author-dateopsi tampaknya meninggalkan timestamp penulis, dan mengatur cap waktu committer menjadi sama dengan cap waktu penulis asli, yang diinginkan Olivier ...
David Fraser
118

Jika Anda telah mengacaukan tanggal komit (mungkin dengan rebase) dan ingin mengatur ulangnya ke tanggal penulis yang sesuai, Anda dapat menjalankan:

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

Andy
sumber
1
Saya baru saja mencoba ini, tetapi tidak berpengaruh. Aku punya output folowing: WARNING: Ref 'refs/heads/master' is unchanged. Saya menggunakan git versi 1.7.9.5 di Linux (64 bit)
Markus N.
20
Saya ingin menambahkan pendekatan lain jika Anda sudah mengacaukan tetapi tidak ingin mengulangi seluruh sejarah: git rebase --committer-date-is-author-date <base_branch> Dengan cara ini, git akan mengatur ulang tanggal komit hanya untuk komit yang diterapkan pada <base_branch> (yang mungkin merupakan nama cabang yang sama yang Anda gunakan saat Anda mengacaukan).
Pembicara
Jawaban yang diterima tidak berfungsi pada tahun 2016, tetapi jawaban @ pembicara itu berhasil!
Theodore R. Smith
2
Jawaban @ pembicara tidak bekerja pada Oktober 2016, tetapi Andy berhasil!
Amedee Van Gasse
2
Ini tidak berfungsi di Windows. Saya bisa membuatnya bekerja menggunakan Windows Bash.
vaindil
33

Sebuah pertanyaan penting Von C membantu saya memahami apa yang terjadi: ketika rebase Anda, committer ini perubahan timestamp, tapi bukan penulis timestamp, yang tiba-tiba semua masuk akal. Jadi pertanyaan saya sebenarnya tidak cukup tepat.

Jawabannya adalah rebase sebenarnya tidak mengubah stempel waktu penulis (Anda tidak perlu melakukan apa pun untuk itu), yang sangat cocok untuk saya.

Olivier Verdier
sumber
3
+1 - Saya memiliki alias git di tempatnya ( coderwall.com/p/euwpig/a-better-git-log ) yang tampaknya menggunakan cap waktu committer, yang membingungkan saya. Gitk dan git log keduanya menunjukkan stempel waktu penulis.
1615903
15

Secara default, git rebase akan mengatur stempel waktu committer ke waktu ketika komit baru dibuat, tetapi menjaga stempel waktu penulis tetap utuh. Sebagian besar waktu, ini adalah perilaku yang diinginkan, tetapi pada beberapa skenario, kami juga tidak ingin mengubah cap waktu komiter. Bagaimana kita bisa mencapai itu? Nah, inilah trik yang biasa saya lakukan.

Pertama, pastikan setiap komit yang akan Anda rebase memiliki pesan komit unik dan cap waktu penulis (Di sinilah trik membutuhkan perbaikan, saat ini sesuai dengan kebutuhan saya).

Sebelum rebase, catat stempel waktu pengganti, stempel waktu penulis dan komit pesan dari semua komit yang akan diubah menjadi file.

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

Kemudian, biarkan rebase yang sebenarnya terjadi.

Akhirnya, kami mengganti timestamp committer saat ini dengan yang dicatat dalam file jika pesan komit sama dengan menggunakan 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'

Jika terjadi kesalahan, checkout saja git reflogatau semua refs/original/referensi.

Selain itu, Anda dapat melakukan hal yang mirip dengan stempel waktu penulis.

Misalnya, jika stempel waktu penulis dari beberapa komit tidak sesuai, dan tanpa mengatur ulang komit ini, kami hanya ingin stempel waktu penulis ditampilkan secara berurutan, maka perintah berikut akan membantu.

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
sumber
Ini trik yang bagus! Itu memungkinkan saya untuk hanya menulis ulang 75 komit daripada 1100+ dari menggunakan jawaban lain.
audun
Ini fantastis! Apakah ada cara untuk memodifikasi skrip untuk juga menjaga committer asli juga?
David DeMar
@ DavidvidMar harus, sama, hanya mengubah log git --pretty untuk merekam email asli, dan memodifikasi skrip yang sesuai.
weynhamz