Implementing Advanced Git Operations in Gitg
gitg is a Git Graphical User Interface (GUI) client written for GNOME. The program makes it easy for newcomers to visualize git actions and for expert users a usable interface to quickly perform routine operations such as commiting, rebase and so on. Internally, gitg it uses libgit2-glib library. Glib (pronounced 'gee-lib', are bindings for an externally managede C-based library called libgit2). The GUI is powered by GTK+ and keeping it all together is Vala.
Implementing Advanced Git Operations in Gitg
- Development Environment
- gitg jargon for UI
- Format-patch for gitg
- Interactive Rebase for gitg
- Progress Reports
- Other bug fixes (pre/during SoC)
gitg jargon for UI
These screenshots indicate the different parts of Gitg as referred to in this document.
Format-patch for gitg
The git command to generate a patch file for a commit is git format-patch HEAD~N where N indicates the number of commits for which the user would like patch files to be generated. In keeping with this behaviour, I added a button in the Diff View. This work was committed to master in 8bcc579.
It was intuitive design to have a button that allowed a user to save the patch file for a selected commit meant that the design of the feature was simple, accomplishable in first half of the internship. gitg has History View which is constructed by the GTK+ widget TreeView. The widget that displays the diff of a commit is rendered by Webkit library. The unique technical detail of this patch is that the communication so far was one-way between History View to Diff View, the other way around was not done or explored previously. This was done by:
- Idenitfying where existing requests for generating the diff was called for.
- Adding a new request type called "patch" and then correspondingly creating gitg-diff-view-request-patch.vala
- Creating new class and filling it in with File utility functions that will handle creation of patch, saving to disk etc.
- Calling the run_after async() from where the diff view requests were handled. The Diff View Request were handled in a seperate thread running outside the Gtk Main loop. run_after_async() is a hook ensuring a request can be handled inside the Gtk Main loop.
- Overriding the run_after_async().
- Implementing the run_after_async() function with code for pre-populating the filename with sanitized characters when the patch was generated and was asked to be saved by user.
Interactive Rebase for gitg
Git allows to rewrite git history. This is useful when one is trying to clean up the commit log to be replayed on top of another branch. Git interactive rebase behaves differently based on arguments passed to it. Here is a brief summary of arguments and the resultant behaviour:
git rebase -i
Git will check for the current branch's tracking branch. If tracking branch is not set, then instructions are shown to do so, without which rebase won't be started`.
git rebase -i <branch-name>
Git will include all commits of the current branch to be replayed on top of the given <branch-name>.
git rebase -i HEAD~N
Git will include commits from that which HEAD is pointing to till N, where N is a positive number not exceeding the total number of commits available in the current branch.
What happens during an interactive rebase?
The git interactive rebase is held together in a shell script. Roughly this is what happens behind the scenes when you initiate an interactive rebase...
- ../.git/rebase-merge directory is created.
- ../.git/rease-merge is populated with the files:
author-script git-rebase-todo interactive onto quiet done git-rebase-todo.backup message orig-head rewritten-list end head-name msgnum patch stopped-sha
- Git looks for values set for the environment variable $GIT_SEQUENCE_EDITOR and $GIT_EDITOR. If set, it executes the command for these variables.
- Git presents the user with a text file that has been populated with commits in the order of oldest to recent. Each line in the file begins with the default 'pick' action set against it followed by the short commit hash and the commit message subject. The lines preceeded by a '#' character indicates comments and hence are any text set so will be ignored by git when processing this text file.
- User has the following options for actions to make on commits:
p, pick = use commit. keep commit as is. no change. 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
User is required to set an action against each commit line. To delete a commit, one has to simply remove the commit line entirely. User saves the file.
- Depending on the action, git mainly either pops up the editor to allow the user to edit the commit message or falls to shell to allow user to amend the files. When it falls to shell, this indicates a detached HEAD.
- Once all commits which have been queued for an action (other than pick) have been iterated through, git replays the re-written history.
- Unfortunately, libgit2 has no functions that allow me to implement interactive rebase.
- Recreate the ../.git/rebase-merge directory before user even initiates a rebase: This approach meant that I could simply put the ../git/rebase-merge/git-rebase-todo file pre-populated but this failed because git recognised an existing ../.git/rebase-merge directory and refused to continue.
Building the solution
Very few existing FLOSS projects have implemented interactive rebase, qgrit is one such that uses QT and C++. The idea is to set the environment variables $GIT_SEQUENCE_EDITOR and $GIT_EDITOR to gitg in a special editor mode. qgrit helped me fine tune my ideas for the UI.
In order to keep my first iteration of this feature simple and to get the working prototype fast, I made the following decisions for what my interactive rebase feature can do:
- Initate a rebase against HEAD of the current branch till N, value for 'N' will an input from the user via the UI.
- The rebase actions squash and reword will be supported. As other actions such as edit, fixup, exec and deleting a commit meant that git would drop to shell and wait for the user to amend the conflicts. Implementing UI for these is an extensive process and hence was not included in this iteration of my work.
Code is available at
The only cost that the interactive rebase feature has upon gitg is that gitg will now have to ship with 'git' as a dependency during installation.
A summary of how my work took shape
Add a command line argument --rebase-editor. Starting gitg thus will invoke git rebase -i M~N on the currently checkedout branch for the current repository, where N is derived from the row that was selected before interactive rebase was initiated. M currently is fixed to be HEAD.
- Add a parser that parses the ../.git/rebase-merge/rebase-merge-todo file. This made me learn about string arrays and functions to manipulate file content.
Add a GTK+ window with the ListBox widget. Populate this widget with contents parsed from the ../.git/rebase-merge/rebase-merge-todo file. Thus I learnt, about working with ListBox and ComboBox widgets.
First working prototype happened in 6460596.
- Add an entry to the settings menu in Gitg. This entry pops up a dialog to take input for number of commits to rebase from HEAD.
- Spawn git interactive rebase.
- Code clean-up: Discard handwritten UI code and add UI files (generated via Glade).
- Code clean-up: Convert UI files to template, the way it is preferred in gitg master.
- Connect the dialog that takes value for number of commits to rebase against HEAD with the start_rebase() function that spawns git.
Issues & TODO
Throughout the iteration, I faced no major issues, a few routine syntax/logical errors, nothing I couldn't fix with a glance at code. The one that stopped in my tracks was a spawn related bug that was somewhat a Heisenbug. Am working on fixing this by making my --rebase and --rebase-commit-editor as independent helper binaries.
I have sporadically blogged and posted reports on mailing lists. I am not good with writing regular reports but my mentors were OK with keeping in touch about work happening via IRC.
Mailing list weekly reports
- Week #3: N/A due to time taken off for final exams.
- Week #4: N/A due to time taken off for final exams.
What's next? Plans!
There has not been a release since gitg was re-written in Vala. The developers planned that we'd make a release including the features resulted from the Summer of Code work. The interactive rebase feature needs some polishing to make the UI look like it's a part of Gitg, some code fix up, error catching and style clean up. I plan to work on it in small tangible results bearing sessions over the next month and hopefully working with my mentors be able to help them make a release sometime before November 2013.
This has been the very first time I ever did any serious coding. Both in my online and offline life I have received plenty of help from 3 key people:
Paolo Borelli (mentor) : Thank you for patiently guiding me and trying to understand me even when I was a nervous wreck!
Ignacio Casal Quintero (mentor) : Thank you for helping me with code reviews and especially making me a convert with jhbuild! You made me see the light about good practises in development.
Anirudh Sanjeev (friend) : Thank you for helping me see the *why* in the code!
Other bug fixes (pre/during SoC)
In order to explore different parts of gitg I worked on these other features/bugs and took on random tasks from mentors as well both pre and during SoC. Here's a git shortlog of the code that I've contributed to master:
Sindhu S (29): Show commit author's time and zone in commit log and diff view Change camel case var timeZone to gitg style Fix relative time in history view; bug 698830 Fix dialog titles to comply with GNOME HIG guidelines. Fix Gitg dialog titles and menu items in UI to be consistent Fix gitg window title to match other GNOME applications Fix date format in commit history view Add User information config menu in dash and views. Fix for singular and plural relative time format. Better fix for relative time in Gitg Add refresh feature to gitg Add Reload menu entry to history view Add U as keyboard navigator letter for "User Information" Add keyboard shortcuts Add F5 as keyboard shortcut to reload repository Fix User Information dialog title Make the user information dialog a modal dialog Allow long names in User Information Dialog (Bug 701093) Change label for User Information dialog Fix diff view formatting of EOFNL (Add and Delete) Show different icon if repository has remote Add expander to diff view Fix diff view for first commit, bug 702259 Add infobar Add an AppData description for gitg User Information dialog is now Author Details Add "Get Patch" button to Diff View Fix 'Clone Repository' dialog errors Port jquery to v2.0.3 Sindhu Sundar (2): User information dialog now saves with Enter/Return key Renamed references_foreach to references_foreach_name
WIP: Dash View and others
I also started working on revamping the Dash View, starting with adding useful subtext to each item on the recent list of the Dash View. The work-in-progress branch is available at: https://github.com/sindhus/gitg/commits/wip/sindhus/branch-info. This branch will see code for enhancements requests such as:
In addition, I have also taken on: Bug 647085 - documentation.