A.Miya
A.Miya

Reputation: 101

The best practice for melding into NEXT commit in git rebase -i?

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

Answers (3)

AgentRev
AgentRev

Reputation: 809

If you want to "forward-squash" multiple commits, you can do the following:

  1. (optional) If you modified and subsequently untracked a file in the unpushed commits, make sure to stash or clean untracked files first.

  2. Start the rebase:

    git rebase -i origin/destination_branch
    
  3. 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

VonC
VonC

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 of rebase -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.

Documentation:

--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 flag

Signed-off-by: Rohit Ashiwal

There are two backends available for rebasing, viz, the am and the interactive.
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

torek
torek

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

Related Questions