Everybody that uses Git is meant to know the command 'git rebase -i' on which you can rewrite your git repository history into a better way. Sometimes you need to do that to have your repository better organized, documented, or simply better structured. It is also good to shrink the multiple modifications happened in the files to their final wanted result contents.
In my case, I needed to update a forked project on which I made a lot of work, but since I made many commits on it its a mess, for example I don't know to 'which' feature is related to each commit, and there's many 'fixes' of own changes that should be directly included in the main feature. 2,5 years after I'm updating the repository with their upstream changes, and since everything changed so much I really need to make sure what I'm importing as own changes and what happens when the rebase commits will fail (which is going to happen after so many changes)
Prepare your environment
The first suggested thing is to make a backup of your actual git repository, just in case you mess up something and you want to recover it back, for that, we will use our favorite tool included in Elive which will do incremental snapshoted backups and we can recover to any previous state:
bkp save
Next, since this is a rewrite of the branch on which we worked, we will duplicate it so that our changes will only exist in the new one:
git checkout elive-buster
git checkout -b elive-buster-rewrite
Start selecting your commits
Assuming that all our changes happened since its branch 'master' on which diverged ours, with our changes, we can run an interactive rebase like this:
git rebase -i master
If we go to the end we can see a small description, and the last commit is the last change we made, for example we have:
The last commit is just a rewording of the previous-previous one, and the one in the middle is a different topic work, so in my case I want to put these 2 ones in a same commit and leave the other one as after these, so i just cut the line and put it after. Then, I set the 'wording' commit to squash, in order to be meld into the previous one, then it should look like this:
the possible options to do with the commits are listed in the commands list
Then save the file and exit, you will be able to edit the description which may be fine by default, and then you have your first history rewrited:
As we can see, in our diverged new branch we have only 2 commits where it should be 3, the 'wording' one is not included, but if we run 'git show a9a42072c' we will see that it includes all the changes of both commits that we merged into one.
Good! let's save a new snapshoted state now, so we can have another backup that we can recover in any moment:
bkp save
1 commit later...
uh, oh! a conflict happened trying to rewrite our history, we can try 'git mergetool' to compare the code and fix it manually but we don't want to lose time on this or mess anything, so let's just recover the last state:
bkp get
Cleanup reverts
You may have probably made some reverted commits, they are ugly in our history and consumes our time reading it, since a revert is a full undo of a specific commit, you can simply delete the lines containing the revert and the line containing the original commit which has been reverted, for example we can see both of these here:
Continue with more
Run again 'git rebase -i master' and in another terminal, you can run your preferred command to visualize the full modifications happened in each commit (like gitk), so you can organize your commits moving their positions in the history, and maybe squash some of them. Do that until you are satisfied with the linear history and contents of your commits.
In the meantime, to make sure that the final result of both branches are the same (since we are only rewriting the order of the history and their descriptions, so nothing is changing), you can run in any moment this command to show any possible differences:
git diff elive-buster..elive-buster-rewrite
TIP: to avoid conflicts and difficult situations, when you squash your commits, you must keep them in the same topic and files modified, if you add a commit in your squashing with a file that is not related, it could give conflicts later