A common technique for feature development with Git is to start with a new feature branch. You’ll continue to add and commit changes into that branch and at some point in time you want to merge the many work-in-progress commits into one. The Git terminology references that as “squash“.
Another common best practice is to use the Git forking model you are probably familiar with on GitHub. You’ll fork your own repository, start adding a new Icinga 2 ITL CheckCommand definition, later on you are adding the missing documentation bits. Then you’ll send a pull request to the upstream repository. Developers will review your changes and probably ask you to rebase and squash your commits into one commit.
Let’s just try that.
michi@mbmif ~/coding/icinga/icinga2 (master) $ git checkout -b feature/itl-checkcommand-13079 <change, add, commit> michi@mbmif ~/coding/icinga/icinga2 (feature/itl-checkcommand-13079) $ git l * ed6a68f- (HEAD -> feature/itl-checkcommand-13079) Docs: Fix phrasing (5 minutes ago) * ce90e16- Add documentation for logfiles (5 minutes ago) * 5a31d9e- Add CheckCommand "logfiles" (6 minutes ago) * dc29924- (origin/master, origin/HEAD, master) Deprecate the client 'bottom up' mode w/ node update-config (2 hours ago) ...
Now I want to merge that feature branch back to the master. Our development guidelines require me to squash the three commits first.
Generally speaking you can use the interactive “git rebase -i” command here. It requires an additional parameter about the commits we will be editing, counting from HEAD back in the history. In my case I want to edit HEAD minus 3 commits.
The interactive mode will open the configured editor, vim in my case.
michi@mbmif ~/coding/icinga/icinga2 (feature/itl-checkcommand-13079) $ git rebase -i HEAD~3 pick 5a31d9e Add CheckCommand "logfiles" pick ce90e16 Add documentation for logfiles pick ed6a68f Docs: Fix phrasing # Rebase dc29924..ed6a68f onto dc29924 (3 commands) # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # d, drop = remove commit # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out
Git informs us about the possible options here. I could just use “edit” which iterates over the commits, stops for amending the commit message and/or the commit changes itself.
I want to squash the commits. This requires the base commit (usually the first one) to stay on “pick” while the others are changed to “squash”.
pick 5a31d9e Add CheckCommand "logfiles" squash ce90e16 Add documentation for logfiles squash ed6a68f Docs: Fix phrasing
This will squash the commits one by one onto the last picked one. Save the changes and continue.
# This is a combination of 3 commits. # This is the 1st commit message: Add CheckCommand "logfiles" # This is the commit message #2: Add documentation for logfiles # This is the commit message #3: Docs: Fix phrasing # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # Date: Wed Nov 23 17:19:55 2016 +0100 # # interactive rebase in progress; onto dc29924 # Last commands done (3 commands done): # squash ce90e16 Add documentation for logfiles # squash ed6a68f Docs: Fix phrasing # No commands remaining. # You are currently editing a commit while rebasing branch 'feature/itl-checkcommand-13079' on 'dc29924'. # # Changes to be committed: # modified: doc/10-icinga-template-library.md # modified: itl/plugins-contrib.d/logmanagement.conf
Edit the commit message (comments starting with “#” will be ignored) and make it a good one (also something you learn in the training session ;)).
Add CheckCommand "logfiles" refs #13079
Save and continue.
[detached HEAD f1445eb] Add CheckCommand "logfiles" Date: Wed Nov 23 17:19:55 2016 +0100 3 files changed, 8 insertions(+) Successfully rebased and updated refs/heads/feature/itl-checkcommand-13079.
Verify the changed commit history.
michi@mbmif ~/coding/icinga/icinga2 (feature/itl-checkcommand-13079) $ git l * f1445eb- (HEAD -> feature/itl-checkcommand-13079) Add CheckCommand "logfiles" (3 minutes ago) * dc29924- (origin/master, origin/HEAD, master) Deprecate the client 'bottom up' mode w/ node update-config (2 hours ago)
When you are updating your remote repository you’ll need to override the remote history with the local history. Be careful when using “-f”!
michi@mbmif ~/coding/icinga/icinga2 (feature/itl-checkcommand-13079) $ git push -f origin feature/itl-checkcommand-13079
Voilà – our Git commit history is now rebased and squashed.
This example works in a similar fashion with a forked repository on GitHub, thus requiring you to update your PR then.
Hint: I’m using Git shell integration as well Git aliases here (“git l”). We’ll dive into these topics in our Git training too 🙂
$ git config --list | grep 'alias.l' alias.l=log --graph --pretty=format:'%Cred%h%Creset-%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative --