Let's say my local git log
shows:
739b36d3a314483a2d4a14268612cd955c6af9fb a
...
c42fff47a257b72ab3fabaa0bcc2be9cd50d5c89 x
c4149ba120b30955a9285ed721b795cd2b82dd65 y
dce99bcc4b79622d2658208d2371ee490dff7d28 z
My remote git log
shows:
c4149ba120b30955a9285ed721b795cd2b82dd65 y
dce99bcc4b79622d2658208d2371ee490dff7d28 z
What's the easiest way to get to this (assuming an arbitrarily large number of local commits):
527b5810cfd8f45f18ae807af1fe1e54a0312bce a ... x
c4149ba120b30955a9285ed721b795cd2b82dd65 y
dce99bcc4b79622d2658208d2371ee490dff7d28 z
Sometimes, You Might Not Want to Use Interactive Rebase
If the number of intermediary commits between A through X is relatively small, you could get by just fine by using interactive rebasing:
However, in my personal experience, using interactive rebasing on a large number of commits is slow. I've run an interactive rebase on about a hundred commits at once (on a Windows machine using Git Bash), and it took a long time for msysgit to generate the interactive rebase commit editor that allows you to select which operations you want to run on which commit, because, well, the list ended up being very large.
In such a situation, you have a couple of workarounds.
Alternative #1:
git reset
Mixed and soft resets can be used to modify your working-tree or staging area (respectively) to aggregate/collect all the changes between two commits A and Y, after which you can commit all of the modifications at once as a single commit.
I will only give an example of a soft reset, since it already leaves everything staged for you, while if you use a mixed reset, you'll have to stage the modifications anyways:
Alternative #2: Using Patches
Another option is to simply use patches. Just generate a diff patch of the difference between A to Y, then apply the patch as a new commit on top of Y:
As pointed out by @A-B-B in the comments, this won't quite work if there are binary files involved.
git diff --binary
can be used to output a diff for binary files in addition to the text files, but I'm not sure if those diffs can then be used as patches as well."Easiest" is always a bit tricky. Interactive rebase will let you squash everything, and is "easy" by some measure.
Another "easy" way, which looks a bit complicated, is to use a "squash merge" (which is not an actual merge at all, but does use the same underlying code as
git merge
, so it's done with the same command). Let's say you're on branchdevel
whose upstream isorigin/devel
. First we'll renamedevel
todevel-full
to indicate that it's the one with the full sequence of commits. Then we'll create a newdevel
trackingorigin/devel
, and "squash-merge"devel-full
:You have to separately
git commit
as--squash
inhibits the commit thatgit merge
normally makes.Yet a third easy (?) but slightly scary way is to check out the tip version and commit it. Again, assuming
devel
as before, we move the "full develoment" branch-name out of the way and make a new local branch to make the new commit on. This time instead ofgit merge --squash
, though, we use two commands beforegit commit
:The
git rm -rf .
schedules (in the index/staging-area) every single file to be removed, but then thegit checkout devel-full -- .
tells git to re-populate the index/staging-area with every single file that exists at the tip ofdevel-full
. So this means "make the tree for the next commit, look exactly like the tree for the tip ofdevel-full
".(The remove-and-re-create method works for one case where
merge --squash
does not: specifically, it works for "replacing" the tip of one branch with the tip of another, even if the two branches are not related and hence not merge-able. Otherwisemerge --squash
is one step shorter, and definitely not as scary looking, at least!)Both of these other "easy" (?) ways leave you with a branch with the full development history. If you want it, great! If not, you have to delete it.
One option is
git rebase -i @{u}
. I use this frequently enough that I've aliased it asgit freebase
(since it works on the commits that you can freely rebase).In case you're not familiar,
@{u}
is a shortcut for@{upstream}
, or "the upstream of the current branch".