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.

Tasks

From the Summer of Code 2013 Ideas page, I proposed to implement the following advanced operations of git as features for gitg:

Development Environment

I set-up Jhbuild on Ubuntu GNOME 13.04.

gitg jargon for UI

These screenshots indicate the different parts of Gitg as referred to in this document.

Gitg with History View

Gitg with History and Diff View

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:

  1. Idenitfying where existing requests for generating the diff was called for.
  2. Adding a new request type called "patch" and then correspondingly creating gitg-diff-view-request-patch.vala
  3. Creating new class and filling it in with File utility functions that will handle creation of patch, saving to disk etc.
  4. 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.
  5. Overriding the run_after_async().
  6. 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.

Gitg with 'Get Patch' button

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:

Command

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...

  1. ../.git/rebase-merge directory is created.
  2. ../.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
  1. Git looks for values set for the environment variable $GIT_SEQUENCE_EDITOR and $GIT_EDITOR. If set, it executes the command for these variables.
  2. 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.
  3. 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.

  1. 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.
  2. Once all commits which have been queued for an action (other than pick) have been iterated through, git replays the re-written history.

Failed approaches

  1. Unfortunately, libgit2 has no functions that allow me to implement interactive rebase.
  2. 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:

  1. Initate a rebase against HEAD of the current branch till N, value for 'N' will an input from the user via the UI.
  2. 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

  1. https://git.gnome.org/browse/gitg/log/?h=wip/sindhus/interactive-rebase

  2. https://github.com/sindhus/gitg/commits/wip/sindhus/interactive-rebase

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

  1. 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.

  2. 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.
  3. 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.

  4. First working prototype happened in 6460596.

  5. Add a GTK+ window that allows the user to edit the commit message and save this modified text. This made me learn how to use SourceView and TextView widget.

  6. 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.
  7. Spawn git interactive rebase.
  8. Code clean-up: Discard handwritten UI code and add UI files (generated via Glade).
  9. Code clean-up: Convert UI files to template, the way it is preferred in gitg master.
  10. Connect the dialog that takes value for number of commits to rebase against HEAD with the start_rebase() function that spawns git.

Screenshots

Gitg with Interactive Rebase dialog

Gitg with Interactive Rebase Action Window

Gitg with Interactive Rebase Commit Editor Window

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.

Progress Reports

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.

Blog posts

  1. Hello from a GSoC intern

  2. Converting to template in Gitg

  3. Life with Jhbuild

  4. TIL Jhbuild commands

  5. Gitg format-patch feature

  6. Routing errors to GTK.Infobar

Mailing list weekly reports

  1. Week #0

  2. Week #1

  3. Week #3: N/A due to time taken off for final exams.
  4. Week #4: N/A due to time taken off for final exams.
  5. Week #5

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.

Thank you!

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:

L-R: Paolo, me and Nacho at GUADEC 2013 in Brno, Czech Republic

  • 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:

  1. Bug 702643 - Provide a more useful overview

  2. Bug 702524 - Add a welcome screen to the dash

In addition, I have also taken on: Bug 647085 - documentation.

Contact

https://wiki.gnome.org/sindhus

Outreach/SummerOfCode/2013/Projects/SindhuS_ImplementingAdvancedGitOperationsInGitg (last edited 2013-12-03 18:34:15 by WilliamJonMcCann)