Reputation: 101
Because of its cheapness, I use commit
to keep my daily WIP snapshots. So my commit history contains many snapshot commits and several key commits mixed together.
Before going to push my work to a remote repository, I may feel like cleaning my history to contain only key commits by melding proceeding snapshot commits into directly following key commits. The date information of key commits is important to me because it recalls me when and what I have changed.
Using squash/fixup commands in git rebase -i
may be of some help but they meld into PREVIOUS commits and their author dates are not what I want.
Re-ordering commits then squashing often results in unnecessary conflicts, which is also what I want to avoid.
So. What is the best practice to satisfy my desire?
[Update] 2016-07-03
Inspired by torek's answer, I came up with a solution: use git rebase -i
as usual, set the target commit that I want previous commits to meld into to edit
, then execute git reset HEAD~n
where n is the number of previous comments that I want to meld. And commit the melded changes with:
% git commit --amend --date "$(grep DATE .git/rebase-merge/author-script | cut -f2 -d '=')"
After that, finish the rest of work by git rebase --continue
.
Upvotes: 10
Views: 1127
Reputation: 809
If you want to "forward-squash" multiple commits, you can do the following:
(optional) If you modified and subsequently untracked a file in the unpushed commits, make sure to stash or clean untracked files first.
Start the rebase:
git rebase -i origin/destination_branch
Mark with fixup
all commits in-between the first and last you want to squash, and place the below exec
command after the target commit:
pick a52ff17 Feature A added
pick b69a7d9 Feature B WIP
fixup da37b5a Feature B WIP
fixup 009557e Feature B WIP
pick 4857cdd Feature B added
exec git reset --soft HEAD^ && git commit --amend -C HEAD@{1}
pick 15ffacb Feature C added
There you go, easy-peasy.
Upvotes: 0
Reputation: 1327184
Using squash/fixup commands in
git rebase -i
may be of some help but they meld into PREVIOUS commits and their author dates are not what I want.
Actually, git rebase -i
should have gained a few options with Git 2.25 (Q1 2020), options that are known by "git rebase
" proper.
But... that was reverted in commit 4d92452, just before 2.25 release.
See "Problems with ra/rebase-i-more-options
- should we revert it?"
Original answer, to illustrate what might come later:
See commit d82dfa7 (21 Nov 2019) by Junio C Hamano (gitster
).
See commit fe28ad8, commit 08187b4, commit 0185c68, commit cbd8db1, commit c068bcc, commit ba51d2f (01 Nov 2019) by Rohit Ashiwal (r1walz
).
(Merged by Junio C Hamano -- gitster
-- in commit 5d9324e, 10 Dec 2019)
rebase
: add--reset-author-date
Signed-off-by: Rohit Ashiwal
The previous commit introduced
--ignore-date
flag to interactive rebase, but the name is actually very vague in context ofrebase -i
since there are two dates we can work with.
Add an alias to convey the precise purpose.
The git rebase
manpage now includes:
--ignore-date:: --reset-author-date::
By default, the author date of the original commit is used as the author date for the resulting commit.
This option tells Git to use the current timestamp instead and implies--force-rebase
.
And:
rebase -i
: support--committer-date-is-author-date
Signed-off-by: Rohit Ashiwal
rebase am
already has this flag to "lie" about the committer date by changing it to the author date.
Let's add the same for interactive machinery.
--committer-date-is-author-date::
Instead of recording the time the rebased commits are created as the committer date, reuse the author date as the committer date.
This implies--force-rebase
.
You even have
rebase -i
: add --ignore-whitespace flagSigned-off-by: Rohit Ashiwal
There are two backends available for rebasing, viz, the
am
and theinteractive
.
Naturally, there shall be some features that are implemented in one but not in the other.One such flag is
--ignore-whitespace
which indicates merge mechanism to treat lines with only whitespace changes as unchanged.
Wire the interactive rebase to also understand the--ignore-whitespace
flag by translating it to-Xignore-space-change
.
Doc:
-ignore-whitespace:
Behaves differently depending on which backend is selected.
- '
am
' backend: When applying a patch, ignore changes in whitespace in context lines if necessary.- '
interactive
' backend: Treat lines with only whitespace changes as unchanged for the sake of a three-way merge.
Upvotes: 1
Reputation: 489233
Here is one way: use rebase as usual. Set the disposition for the fixup-or-squash-commit to edit
. When Git stops to let you edit the fixup, use git reset --soft HEAD^; git commit --amend --reset-author-date
, but be aware that this uses the date of "right now". For more control, and to pick up the date from the squash/fixup, grab the author-date from that commit and use --date
: you will probably want to write a little shell script to do that.
(Use git log --pretty=format:%ad --no-walk
to extract the date from the current commit, before the git reset --soft HEAD^
step. You may want to save the commit message text, or even the entire ID of the commit: the soft-reset leaves the commit in the repository, where it is protected by both the in-progress rebase and the various reflogs, so you can extract whatever you want from it afterwards, as well as before. Of course the author and committer and their dates, and the message, cover everything useful here.)
To squash much further back, you can do multiple rebases, or multiple squashes in the interactive edit, or git reset --soft
further back than HEAD^
.
(I never bother with this: the specific date stamps don't seem that useful here.)
Upvotes: 3