Changing the commit history of your branch is usually not the best idea. It
should be done only in very specific cases. First of all by changing the
history you will cause a great headache for whoever has changes in a branch branched
off of the branch you are changing. My case was very specific. I had to squash
old commits in the develop branch, which was used earlier by pretty much just a
single developer. The effort was to create a demo application, which prompted
the thought to just hide all these commits behind one "demo
preparation" commit. Here are the steps I've taken to perform the squash
on selected commits of the branch. I had a freshly installed git on a windows
machine at my disposal.
1.
I've branched off of develop and created
develop-squash, a branch I could work on
2.
I've cloned the same repository in a separate
location - I like to have an option to manually check different commits in the
other repository, while I'm working on a rebase in my initial repo
3.
I've set the default git text editor to
Notepad++:
git
config --global core.editor "'C:/Program Files
(x86)/Notepad++/notepad++.exe' -multiInst -notabbar -nosession -noPlugin"
4.
I've launched interactive rebase to
change the whole history of the branch:
git
rebase -i --root
What
it will do is it will open the interactive rebase file in Notepad++. We will
get a list of all commits in the branch. --root makes it list all commits from
the very first one. We can set a specific hash instead of root, if we want to
change the commit history from specific commit and not the first one. The list
will not contain merge commits from other branches.
Each
commit in the list is set to "pick" which means the commit will be
picked in the rebase operation. If we want to squash the given commit we need
to change to "squash" or "s" for shortcut. (we can also
drop specific commits or reorder them as we see fit).
5.
After we've finished preparing the
rebase, we can close the notepad document. Git will see it and execute the
rebase, which will start going one by one. Each time there's an issue git will
stop the rebase and allow us to act.
6.
Whenever the rebase is stopped we
get a chance at seeing what exactly went wrong and solve it. What usually
happens is git tries to flatten the history behind the sub-branches of the
branch we're squashing. In this case it will require you to resolve all
conflicts. After you've resolved the conflicts with one of many diff tools, you
can continue the rebase.
7.
Continue the rebase by executing:
git
rebase --continue
Or
abort it:
git rebase --abort
8.
In my case quite often, to get sure
I'm merging correctly I was checking a later point in the commit history, in
the second repository I've cloned earlier.
9.
After the rebase finished I've
switched the names of the branches, created develop-legacy out of the initial
develop, and develop branch out of the
develop-squash branch like this:
git
branch -m develop develop-legacy
git
branch -m develop-squash develop
10.
Sort out master branch and HEAD:
I've
deleted the remote develop branch and pushed the new one in. Then I've
overwritten the remote master branch with my develop as well:
git push -f origin develop:master
The above will also set the HEAD index onto this new master, which will leave our branch structure how it should be.
git push -f origin develop:master
The above will also set the HEAD index onto this new master, which will leave our branch structure how it should be.
11. Cleanup
At
this moment we still heave our develop-legacy branch, which is the old branch
with all our old commits. If we remove it we can pretty much remove all the
associated tags and sub branches. The reason being - we won't need this history
anymore, since we've already rewritten it.
If
we'll only delete the develop-legacy branch - our old commits will still be
hanging, being kept by either old tags or sub branches. Once there are no
branches and tags associated with it, git garbage collector should remove the
orphaned commits (we can also force run it as well).
All in all, there are most probably better ways of doing it. This was something I've cobbled together. I'd be happy to learn about other options which I'm sure git has in stock.