Let's say I have this series of commits:
A -> B -> C -> D -> E
With A being the oldest one and E the newest one. I want to squash A and B, but that would need to recommit everything and then all the timestamps of also C, D and E would be rewritten by the current time. How to squash A and B without touching C, D and E timestamp?
First of all, just a tip: when you ask something about git, draw the arrows from the child commit to its parent (or parents, if it's a merge), as it is how git works. I'm also including the parent of A:
parent <- A <- B <- C <- D
What you can do here is an interactive rebase:
git rebase -i <hash of the parent of A>
It will open a text editor (set on EDITOR environment variable, typically vim or nano, but you can change to vscode, emacs, whatever you want). It will show something like this (the hashes will be different):
pick f6e81ab A
pick 282939d B
pick acb8411 C
pick bb08045 D
pick in B to
squash (in the bottom of the opened file there's an explanation about this, I strongly recommend you to read to know what you're doing, and to know the other options). Now, save and close this file.
Now, git will open the editor again and will ask you to type the new commit message. You can keep its suggestion, or write a new one. I changed it to
squashed. Save and close this file.
The new commit history is the following:
parent <- squashed <- C <- D
If you run
git log, you'll see that original timestamp is kept. But there's a detail about it. If you run
git log --pretty=fuller, you'll see that there's an
AuthorDate and a
CommitDate. By default, they are the same for new commits, but when you're cherry-picking (with
git cherry-pick or
git rebase), the
AuthorDate is kept, but
CommitDate is updated with the timestamp of the rebase. This way git tracks both commit creations but, by default, it shows the original commit timestamp.
I suggest you to take a look at https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History, I hope it helps you!