{"thread":{"id":"7741","subject":"[PATCH] gitview: annotation support","startedAt":"2007-04-19T16:56:03Z","lastAt":"2007-04-19T16:56:03Z","messageCount":1,"participants":["Aneesh Kumar K.V"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"39907","messageId":"11770017633282-git-send-email-aneesh.kumar@gmail.com","threadId":"7741","inReplyTo":null,"subject":"[PATCH] gitview: annotation support","fromName":"Aneesh Kumar K.V","fromEmail":"aneesh.kumar@gmail.com","sentAt":"2007-04-19T16:56:03Z","receivedAt":"2007-04-19T16:56:03Z","isPatch":true,"sender":{"key":"aneesh.kumar@gmail.com","avatar":"https://gravatar.com/avatar/0621fc0b2f14ead1e9024382f16053a808c148596da30c1b92572fa075621f68?d=mp&s=160"},"body":"List files modifed as a part of the commit in the diff window\nSupport annotation of the file listed in the diff window\nSupport history browsing in the annotation window.\n\nSigned-off-by: Aneesh Kumar K.V <aneesh.kumar@gmail.com>\n---\n contrib/gitview/gitview |  260 ++++++++++++++++++++++++++++++++++++++++++++++-\n 1 files changed, 256 insertions(+), 4 deletions(-)\n\ndiff --git a/contrib/gitview/gitview b/contrib/gitview/gitview\nindex 521b2fc..4b8cde1 100755\n--- a/contrib/gitview/gitview\n+++ b/contrib/gitview/gitview\n@@ -10,7 +10,8 @@ GUI browser for git repository\n This program is based on bzrk by Scott James Remnant <scott@ubuntu.com>\n \"\"\"\n __copyright__ = \"Copyright (C) 2006 Hewlett-Packard Development Company, L.P.\"\n-__author__    = \"Aneesh Kumar K.V <aneesh.kumar@hp.com>\"\n+__copyright__ = \"Copyright (C) 2007 Aneesh Kumar K.V <aneesh.kumar@gmail.com\"\n+__author__    = \"Aneesh Kumar K.V <aneesh.kumar@gmail.com>\"\n \n \n import sys\n@@ -24,6 +25,7 @@ import gobject\n import cairo\n import math\n import string\n+import fcntl\n \n try:\n     import gtksourceview\n@@ -337,6 +339,186 @@ class Commit:\n \t\tfp.close()\n \t\treturn diff\n \n+class AnnotateWindow:\n+\t\"\"\"Annotate window.\n+\tThis object represents and manages a single window containing the\n+\tannotate information of the file\n+\t\"\"\"\n+\n+\tdef __init__(self):\n+\t\tself.window = gtk.Window(gtk.WINDOW_TOPLEVEL)\n+\t\tself.window.set_border_width(0)\n+\t\tself.window.set_title(\"Git repository browser annotation window\")\n+\n+\t\t# Use two thirds of the screen by default\n+\t\tscreen = self.window.get_screen()\n+\t\tmonitor = screen.get_monitor_geometry(0)\n+\t\twidth = int(monitor.width * 0.66)\n+\t\theight = int(monitor.height * 0.66)\n+\t\tself.window.set_default_size(width, height)\n+\n+\tdef add_file_data(self, filename, commit_sha1, line_num):\n+\t\tfp = os.popen(\"git cat-file blob \" + commit_sha1 +\":\"+filename)\n+\t\ti = 1;\n+\t\tfor line in fp.readlines():\n+\t\t\tline = string.strip(line)\n+\t\t\tself.model.append(None, [\"HEAD\", filename, line, i])\n+\t\t\ti = i+1\n+\t\tfp.close()\n+\n+\t\t# now set the cursor position\n+\t\tself.treeview.set_cursor(line_num-1)\n+\t\tself.treeview.grab_focus()\n+\n+\tdef _treeview_cursor_cb(self, *args):\n+\t\t\"\"\"Callback for when the treeview cursor changes.\"\"\"\n+\t\t(path, col) = self.treeview.get_cursor()\n+\t\tcommit_sha1 = self.model[path][0]\n+\t\tcommit_msg = \"\"\n+\t\tfp = os.popen(\"git cat-file commit \" + commit_sha1)\n+\t\tfor line in fp.readlines():\n+\t\t\tcommit_msg =  commit_msg + line\n+\t\tfp.close()\n+\n+\t\tself.commit_buffer.set_text(commit_msg)\n+\n+\tdef _treeview_row_activated(self, *args):\n+\t\t\"\"\"Callback for when the treeview row gets selected.\"\"\"\n+\t\t(path, col) = self.treeview.get_cursor()\n+\t\tcommit_sha1 = self.model[path][0]\n+\t\tfilename    = self.model[path][1]\n+\t\tline_num    = self.model[path][3]\n+\n+\t\twindow = AnnotateWindow();\n+\t\tfp = os.popen(\"git rev-parse \"+ commit_sha1 + \"~1\")\n+\t\tcommit_sha1 = string.strip(fp.readline())\n+\t\tfp.close()\n+\t\twindow.annotate(filename, commit_sha1, line_num)\n+\n+\tdef data_ready(self, source, condition):\n+\t\twhile (1):\n+\t\t\ttry :\n+\t\t\t\tbuffer = source.read(8192)\n+\t\t\texcept:\n+\t\t\t\t# resource temporary not available\n+\t\t\t\treturn True\n+\n+\t\t\tif (len(buffer) == 0):\n+\t\t\t\tgobject.source_remove(self.io_watch_tag)\n+\t\t\t\tsource.close()\n+\t\t\t\treturn False\n+\n+\t\t\tfor buff in buffer.split(\"\\n\"):\n+\t\t\t\tannotate_line = re.compile('^([0-9a-f]{40}) (.+) (.+) (.+)$')\n+\t\t\t\tm = annotate_line.match(buff)\n+\t\t\t\tif not m:\n+\t\t\t\t\tannotate_line = re.compile('^(filename) (.+)$')\n+\t\t\t\t\tm = annotate_line.match(buff)\n+\t\t\t\t\tif not m:\n+\t\t\t\t\t\tcontinue\n+\t\t\t\t\tfilename = m.group(2)\n+\t\t\t\telse:\n+\t\t\t\t\tself.commit_sha1 = m.group(1)\n+\t\t\t\t\tself.source_line = int(m.group(2))\n+\t\t\t\t\tself.result_line = int(m.group(3))\n+\t\t\t\t\tself.count\t    = int(m.group(4))\n+\t\t\t\t\t#set the details only when we have the file name\n+\t\t\t\t\tcontinue\n+\n+\t\t\t\twhile (self.count > 0):\n+\t\t\t\t\t# set at result_line + count-1 the sha1 as commit_sha1\n+\t\t\t\t\tself.count = self.count - 1\n+\t\t\t\t\titer = self.model.iter_nth_child(None, self.result_line + self.count-1)\n+\t\t\t\t\tself.model.set(iter, 0, self.commit_sha1, 1, filename, 3, self.source_line)\n+\n+\n+\tdef annotate(self, filename, commit_sha1, line_num):\n+\t\t# verify the commit_sha1 specified has this filename\n+\n+\t\tfp = os.popen(\"git ls-tree \"+ commit_sha1 + \" -- \" + filename)\n+\t\tline = string.strip(fp.readline())\n+\t\tif line == '':\n+\t\t\t# pop up the message the file is not there as a part of the commit\n+\t\t\tfp.close()\n+\t\t\tdialog = gtk.MessageDialog(parent=None, flags=0,\n+\t\t\t\t\ttype=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE,\n+\t\t\t\t\tmessage_format=None)\n+\t\t\tdialog.set_markup(\"The file %s is not present in the parent commit %s\" % (filename, commit_sha1))\n+\t\t\tdialog.run()\n+\t\t\tdialog.destroy()\n+\t\t\treturn\n+\n+\t\tfp.close()\n+\n+\t\tvpan = gtk.VPaned();\n+\t\tself.window.add(vpan);\n+\t\tvpan.show()\n+\n+\t\tscrollwin = gtk.ScrolledWindow()\n+\t\tscrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)\n+\t\tscrollwin.set_shadow_type(gtk.SHADOW_IN)\n+\t\tvpan.pack1(scrollwin, True, True);\n+\t\tscrollwin.show()\n+\n+\t\tself.model = gtk.TreeStore(str, str, str, int)\n+\t\tself.treeview = gtk.TreeView(self.model)\n+\t\tself.treeview.set_rules_hint(True)\n+\t\tself.treeview.set_search_column(0)\n+\t\tself.treeview.connect(\"cursor-changed\", self._treeview_cursor_cb)\n+\t\tself.treeview.connect(\"row-activated\", self._treeview_row_activated)\n+\t\tscrollwin.add(self.treeview)\n+\t\tself.treeview.show()\n+\n+\t\tcell = gtk.CellRendererText()\n+\t\tcell.set_property(\"width-chars\", 10)\n+\t\tcell.set_property(\"ellipsize\", pango.ELLIPSIZE_END)\n+\t\tcolumn = gtk.TreeViewColumn(\"Commit\")\n+\t\tcolumn.set_resizable(True)\n+\t\tcolumn.pack_start(cell, expand=True)\n+\t\tcolumn.add_attribute(cell, \"text\", 0)\n+\t\tself.treeview.append_column(column)\n+\n+\t\tcell = gtk.CellRendererText()\n+\t\tcell.set_property(\"width-chars\", 20)\n+\t\tcell.set_property(\"ellipsize\", pango.ELLIPSIZE_END)\n+\t\tcolumn = gtk.TreeViewColumn(\"File Name\")\n+\t\tcolumn.set_resizable(True)\n+\t\tcolumn.pack_start(cell, expand=True)\n+\t\tcolumn.add_attribute(cell, \"text\", 1)\n+\t\tself.treeview.append_column(column)\n+\n+\t\tcell = gtk.CellRendererText()\n+\t\tcell.set_property(\"width-chars\", 20)\n+\t\tcell.set_property(\"ellipsize\", pango.ELLIPSIZE_END)\n+\t\tcolumn = gtk.TreeViewColumn(\"Data\")\n+\t\tcolumn.set_resizable(True)\n+\t\tcolumn.pack_start(cell, expand=True)\n+\t\tcolumn.add_attribute(cell, \"text\", 2)\n+\t\tself.treeview.append_column(column)\n+\n+\t\t# The commit message window\n+\t\tscrollwin = gtk.ScrolledWindow()\n+\t\tscrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)\n+\t\tscrollwin.set_shadow_type(gtk.SHADOW_IN)\n+\t\tvpan.pack2(scrollwin, True, True);\n+\t\tscrollwin.show()\n+\n+\t\tcommit_text = gtk.TextView()\n+\t\tself.commit_buffer = gtk.TextBuffer()\n+\t\tcommit_text.set_buffer(self.commit_buffer)\n+\t\tscrollwin.add(commit_text)\n+\t\tcommit_text.show()\n+\n+\t\tself.window.show()\n+\n+\t\tself.add_file_data(filename, commit_sha1, line_num)\n+\n+\t\tfp = os.popen(\"git blame --incremental -- \" + filename + \" \" + commit_sha1)\n+\t\tflags = fcntl.fcntl(fp.fileno(), fcntl.F_GETFL)\n+\t\tfcntl.fcntl(fp.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK)\n+\t\tself.io_watch_tag = gobject.io_add_watch(fp, gobject.IO_IN, self.data_ready)\n+\n+\n class DiffWindow:\n \t\"\"\"Diff window.\n \tThis object represents and manages a single window containing the\n@@ -355,6 +537,7 @@ class DiffWindow:\n \t\theight = int(monitor.height * 0.66)\n \t\tself.window.set_default_size(width, height)\n \n+\n \t\tself.construct()\n \n \tdef construct(self):\n@@ -371,10 +554,12 @@ class DiffWindow:\n \t\tvbox.pack_start(menu_bar, expand=False, fill=True)\n \t\tmenu_bar.show()\n \n+\t\thpan = gtk.HPaned()\n+\n \t\tscrollwin = gtk.ScrolledWindow()\n \t\tscrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)\n \t\tscrollwin.set_shadow_type(gtk.SHADOW_IN)\n-\t\tvbox.pack_start(scrollwin, expand=True, fill=True)\n+\t\thpan.pack1(scrollwin, True, True)\n \t\tscrollwin.show()\n \n \t\tif have_gtksourceview:\n@@ -388,11 +573,77 @@ class DiffWindow:\n \t\t\tself.buffer = gtk.TextBuffer()\n \t\t\tsourceview = gtk.TextView(self.buffer)\n \n+\n \t\tsourceview.set_editable(False)\n \t\tsourceview.modify_font(pango.FontDescription(\"Monospace\"))\n \t\tscrollwin.add(sourceview)\n \t\tsourceview.show()\n \n+\t\t# The file hierarchy: a scrollable treeview\n+\t\tscrollwin = gtk.ScrolledWindow()\n+\t\tscrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)\n+\t\tscrollwin.set_shadow_type(gtk.SHADOW_IN)\n+\t\tscrollwin.set_size_request(20, -1)\n+\t\thpan.pack2(scrollwin, True, True)\n+\t\tscrollwin.show()\n+\n+\t\tself.model = gtk.TreeStore(str, str, str)\n+\t\tself.treeview = gtk.TreeView(self.model)\n+\t\tself.treeview.set_search_column(1)\n+\t\tself.treeview.connect(\"cursor-changed\", self._treeview_clicked)\n+\t\tscrollwin.add(self.treeview)\n+\t\tself.treeview.show()\n+\n+\t\tcell = gtk.CellRendererText()\n+\t\tcell.set_property(\"width-chars\", 20)\n+\t\tcolumn = gtk.TreeViewColumn(\"Select to annotate\")\n+\t\tcolumn.pack_start(cell, expand=True)\n+\t\tcolumn.add_attribute(cell, \"text\", 0)\n+\t\tself.treeview.append_column(column)\n+\n+\t\tvbox.pack_start(hpan, expand=True, fill=True)\n+\t\thpan.show()\n+\n+\tdef _treeview_clicked(self, *args):\n+\t\t\"\"\"Callback for when the treeview cursor changes.\"\"\"\n+\t\t(path, col) = self.treeview.get_cursor()\n+\t\tspecific_file = self.model[path][1]\n+\t\tcommit_sha1 =  self.model[path][2]\n+\t\tif specific_file ==  None :\n+\t\t\treturn\n+\t\telif specific_file ==  \"\" :\n+\t\t\tspecific_file =  None\n+\n+\t\twindow = AnnotateWindow();\n+\t\twindow.annotate(specific_file, commit_sha1, 1)\n+\n+\n+\tdef commit_files(self, commit_sha1, parent_sha1):\n+\t\tself.model.clear()\n+\t\tadd  = self.model.append(None, [ \"Added\", None, None])\n+\t\tdele = self.model.append(None, [ \"Deleted\", None, None])\n+\t\tmod  = self.model.append(None, [ \"Modified\", None, None])\n+\t\tdiff_tree = re.compile('^(:.{6}) (.{6}) (.{40}) (.{40}) (A|D|M)\\s(.+)$')\n+\t\tfp = os.popen(\"git diff-tree -r --no-commit-id \" + parent_sha1 + \" \" + commit_sha1)\n+\t\twhile 1:\n+\t\t\tline = string.strip(fp.readline())\n+\t\t\tif line == '':\n+\t\t\t\tbreak\n+\t\t\tm = diff_tree.match(line)\n+\t\t\tif not m:\n+\t\t\t\tcontinue\n+\n+\t\t\tattr = m.group(5)\n+\t\t\tfilename = m.group(6)\n+\t\t\tif attr == \"A\":\n+\t\t\t\tself.model.append(add,  [filename, filename, commit_sha1])\n+\t\t\telif attr == \"D\":\n+\t\t\t\tself.model.append(dele, [filename, filename, commit_sha1])\n+\t\t\telif attr == \"M\":\n+\t\t\t\tself.model.append(mod,  [filename, filename, commit_sha1])\n+\t\tfp.close()\n+\n+\t\tself.treeview.expand_all()\n \n \tdef set_diff(self, commit_sha1, parent_sha1, encoding):\n \t\t\"\"\"Set the differences showed by this window.\n@@ -406,6 +657,7 @@ class DiffWindow:\n \t\tfp = os.popen(\"git diff-tree -p \" + parent_sha1 + \" \" + commit_sha1)\n \t\tself.buffer.set_text(unicode(fp.read(), encoding).encode('utf-8'))\n \t\tfp.close()\n+\t\tself.commit_files(commit_sha1, parent_sha1)\n \t\tself.window.show()\n \n \tdef save_menu_response(self, widget, string):\n@@ -425,7 +677,7 @@ class DiffWindow:\n class GitView:\n \t\"\"\" This is the main class\n \t\"\"\"\n-\tversion = \"0.8\"\n+\tversion = \"0.9\"\n \n \tdef __init__(self, with_diff=0):\n \t\tself.with_diff = with_diff\n@@ -590,7 +842,7 @@ class GitView:\n \t\tdialog = gtk.AboutDialog()\n \t\tdialog.set_name(\"Gitview\")\n \t\tdialog.set_version(GitView.version)\n-\t\tdialog.set_authors([\"Aneesh Kumar K.V <aneesh.kumar@hp.com>\"])\n+\t\tdialog.set_authors([\"Aneesh Kumar K.V <aneesh.kumar@gmail.com>\"])\n \t\tdialog.set_website(\"http://www.kernel.org/pub/software/scm/git/\")\n \t\tdialog.set_copyright(\"Use and distribute under the terms of the GNU General Public License\")\n \t\tdialog.set_wrap_license(True)\n-- \n1.5.1.1.135.gf948-dirty\n"}]}