Monday, November 18, 2013

Blender Git Migration - Productivity Tips for using Git Gui

As I've written about before, IMO the "Git Gui" frontend is one of the best tools around. Sure, it looks butt ugly - like some nasty 90's relic when run on anything other than Windows (surprisingly!) - but what it lacks it more than makes up for in terms of being 1) flexible and easily extensible, and 2) it very clearly exposes you to the Git tools and workflow as it was meant to be done.

For all the pretty gloss and extra OS integration that all the others offer, you'll find that they fall down miserably on these fronts, and eventually end up just complicating things a bit.

Getting It
  • Windows - "Git Gui" is available as a default part of the msysgit package for Windows
  • Linux - Install the "git-gui" package using your package manager. It is also theoretically possible to compile from "source", though personally I haven't had success doing this, as it needs a Tcl/Tk environment set up, and a few additional index files compiled.
Using It
  • Windows: Navigate to your Git repo, right-click and select "Git Gui" from the context menu. 
  • Linux: In a terminal (preferably starting from the folder where your repo is, to save some typing), type:
    $ git gui
(NOTE: it is possible to also create a launcher/shortcut for this command to make things easier. Also, this also works on Windows, but the git-cheetah Win Explorer extensions mean that this isn't necessary)

Why is it good?
In terms of flexibility/customisability, you'll find that most frontends don't really give you any easy way to create a bunch of shortcuts or custom commands. As a newbie learning to use Git, you'll quickly learn that there are bazillions of different options and settings attached to every single command, and that subtle variations in command strings can have very different results. Heck, even relatively "simple" typos/mistakes on the command-line can give vastly different results (something you'll probably eventually encounter at some point if you persist in sticking with masochistically soldering on with the commandline here), and at some point, you'll probably start wearing some battle scars to show for it. This is perhaps one of the major stumbling blocks for newbies transitioning over from other systems.

To save yourself the hassle and worry, you'll eventually want to start sticking to a "safe" subset of commands/recipes which get the job done when there's a specific thing you want to do. But, clearly, even reliably looking up and typing these in each time you need to use them can still backfire on you (especially in times of stress/hurry/excitement during natural software development). Thus, this is why you want to have the ability to create a bunch of shortcuts for executing these recipes. Of course, from time to time, you'll need to use the commandline to do one-off things that are a bit more "adventurous" (in consultation with various online resources, naturally!).

The thing to remember with Git is that it gives you a bag of tricks for building your own workflow around the data storage model it uses. This is in contrast to older style version control systems such as SVN which only provide a "single possible workflow". As such, you get a lot more freedom and flexibility to manipulate your versioning history in various ways, allowing for many different non-linear development strategies as needed.

However, to support this very power, it is also necessary to also remember that Git is very much NOT Subversion! Many of the frontends out there try to pretend that this isn't the case, and end up masking a lot of functionality or trying to shoehorn various concepts into SVN-esque terminology. As can be seen in the screenshot above however, Git Gui is all business: the main window is orientated around helping you see at a glance the status of your working copy, and reviewing and committing those changes.

Using the Main Window - Basics of Daily Workflow
1) Making a Commit
Firstly, you can see at a glance what "files" you've changed (unstaged) and which "files" you've changed and are going to include in the current commit (staged). Simply click on an item in the unstaged list to add it to the staged list, and vica versa. Also, the Ctrl-T shortcut can be used to quickly stage all changes to files already in the repo. Newly added/removed files still need manual handling though (i.e. you'll need to manually click on each), unless you use the Ctrl-I shortcut instead to add everything.

You may have noticed that I put "files" in ""'s. An important difference between Git and SVN (and why we have this who "staging" business), is that it is possible to pick and choose individual line-by-line changes to include in a commit (instead of just including the whole file). That's why there's a diff viewer integrated into this view in the top right corner: it is there so that you can go through your set of changes, and confirm that the changes are the ones you want.

Now, if you find that you've actually got more than one thing going on at once (which you want in separate commits - or perhaps some of those turned out to be unintended changes from a search and replace gone wrong!), simply go through the diffs, right-click on a changed line you want to include, and click "Stage Line For Commit". Repeat for each change you want to include in the commit. If a whole block of changes is fine (each block starts with @@ and a function name), you can simply choose "Stage Hunk For Commit" instead to save some clicking.

Once you've selected all the changes you want to include, it is time to make a commit. The box in the lower right corner gives you a text box to specify log message for your commit.
  • As a matter of convention, the first line of your commit log should be a short 1-line description of what the commit does (official docs prescribe an 72-char limit on this, but IMO that's fine to ignore), followed by more detailed text about the change in separate paragraphs following this.
  • Unlike SVN, Git forces you to create a log message. It's just plain good practice to do so!
Finally, when that's all done, you can click on the "Commit" button to commit this to your local repository's history graph. I repeat, when you "commit", you're adding the change to your local repository only. You can make as many commits as you like locally like this, but they won't be visible to anyone else until you "Push" to a publicly visible repository. (Note: As before, only official Blender developers have "push" or write access to the official repositories on

2) Fixing SlipUp's and "Whoopsies"
As those who've developed code for a while using SVN know (and also for those who follow our SVN commit logs), quite often, we end up making typos in the commit logs which we only notice AFTER we press the magic red button. Or perhaps you've accidentally either left out a file or added one too many into your commit.

Well, in Git, there's a solution to this (* some strings attached): Immediately after realising your error, click the "Amend Last Commit" radio button (above the commit log). This brings back the last commit made at the "tip" of the branch you're currently on, allowing you to edit the commit log and/or modify what changes you included in that commit. When you're done, simply press "Commit" again to commit this 'fixed' commit.

Now, there are some limitations on when you can/should do this. Repeat after me: "I shall only revise commits I have not pushed". That is, as long as the commits you want to change exist ONLY in your local repository (and not in anyone else's repository anywhere), you're free to fix up as many typos/slipups/etc. as you like. Heck, you can even fully rewrite the development of your feature (perhaps in a separate branch where you manually pull back in the "good" changes, and try to make it look like you did things in a fully sane way) locally, or roll back a whole bunch of bad commits (though I'd recommend creating a new branch/tag on the commit at the tip of that chain before rolling back via the "gitk" history view using the "Reset <branchname> branch to here" RMB-menu option, just in case you change your mind). Anything goes, as long as you're only still developing and/or making these changes locally.

Once you've pushed though, you're going to have to live with your changes/mistakes. Technically, Git does allow you to overwrite history after you've already pushed stuff (as I mention in this post). However, for understandable reasons, it is not something that you really want to be doing for any old reason (at least not while working on Blender, when this sledgehammer will only really be used by admins if and when catastrophe strikes. But for sure, it's not something you do if you made a 1 character typo in a comment or commit log! Thousands of users and developers will curse you if you do! You've been warned! 

3) How to Revert Unwanted Changes
Now in addition to selectively committing, sometimes you screw up a change in your current working tree, or perhaps you've got some "leftover" changes you don't want to commit.
  • To get rid of these changes, with the offending files selected in the "Unstaged" list, click on Commit -> Revert to get rid of these changes. 
  • If you get a dialog with Soft/Normal/Hard, you'll generally want to select Hard 
Warning: For safety though, make sure you finish off making whatever commit you were making before doing this.

4) Other bits and pieces
  • You need to set up your name and email details before making any commits. To do this, go to Edit -> Options.

  • Unless you've got a SSH Key set up already, Git Gui provides you with a built-in way to make this easy to set up. Simply go to Help -> SSH Keys -> Generate Key to get a key set up for your machine (this key will be remembered for use in Git Gui from herein across all repos). 
    • To register this key with your Git hosting provider, simply click the "Copy to Clipboard" button to copy the SSH public key it generated, and paste this into the relevant place on a web form somewhere on your Git host's page.
    • You practically need to have a SSH key before you can push to any repos anywhere.
  • To view the history log, use "Repository -> Visualise <BranchName>/All Branch History"

Customisations for Easier Blender Development
Now, earlier on I mentioned that Git Gui makes it easy to customise it to add in shortcuts. This can be done via "Tools -> Add Tool", where you can specify a name for your custom shortcut and the corresponding command that that runs. Sure it's crude (I'd really appreciate if it could get more powerful regarding how it lets you specify what arguments it needs to grab, and how those are then piped into your template command), but for a lot of situations it fits the bill perfectly.

To save you some effort, copy the following code/settings snippet to the end of the ".git/config" file. This snippet defines a bunch of shortcuts derived from the various commands on the Blender Wiki, and was generated by Git Gui when I went to add tools for these in my config.

[guitool "Blender/Update All"]
cmd = "git pull --rebase && git submodule update --recursive"
[guitool "Blender/Update"]
cmd = git rebase --pull
[guitool "Blender/Update Extras"]
cmd = git submodule update --recursive
[guitool "Blender/Resume Update (After Fixing Conflict)"]
cmd = git rebase --continue
[guitool "Blender/Rebase Branch"]
cmd = git rebase master
[guitool "Blender/Commit Patch (Author Name <email xy.z>)"]
cmd = git commit --author $ARGS
argprompt = yes

  • The "Update All" one doesn't seem to be quite working yet. For now, it is probably safer to still do a "Update" followed by a "Update Extras" until I can confirm that it's all working correctly
  • To run these go to the Tools -> Blender menu. In the snippet above (and when creating these, include a forward slash in the name creates a sub-menu).
  • I ended up creating these since the commands and policies for working with Blender's repos differ from what I usually do in my own ones. Notably, I personally never use rebase anymore as it turns out to be more trouble than what its worth IMO. Besides, it's a more accurate representation of version history when all the changes are chronologically ordered, especially when you make sure that any dev you do in separate branches "stays" in those branches by doing "--no-ff" merges for a more accurate representation of the development history of a feature.
Generic Customisations
The following are a set of customisations that I've set up for all my repositories

Code snippets (for ~/.gitconfig):

[guitool "Git Pull"]
cmd = git pull
[guitool "Push New Branch (BranchName)"]
cmd = git push -u $ARGS
argprompt = yes
[guitool "Merge Back Branch (branchname)"]
cmd = git merge --no-ff $ARGS
argprompt = yes

Also, to disable the very annoying "Too many loose objects" warning in Git Gui, run the following command to disable it (Git 1.7.9+):
$ git config --global gui.gcwarning false

Closing Words
Hopefully this starter intro to working with Git using Git Gui has been helpful. If there's demand, I'll try to write a followup with details on some other workflow stuff.

1 comment:

  1. Lawrence D’OliveiroMarch 18, 2015 at 1:55 PM

    I’m not so sure git-pull is a good idea. Use git-fetch instead, then separately rebase local tracking branches.

    Or, in newer versions of Git, git pull --ff-only seems safe enough.