All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Aneesh Kumar K.V" <aneesh.kumar@gmail.com>
To: git@vger.kernel.org
Cc: junkio@cox.net, aneesh.kumar@gmail.com
Subject: [PATCH] gitview: annotation support
Date: Thu, 19 Apr 2007 22:26:03 +0530	[thread overview]
Message-ID: <11770017633282-git-send-email-aneesh.kumar@gmail.com> (raw)

List files modifed as a part of the commit in the diff window
Support annotation of the file listed in the diff window
Support history browsing in the annotation window.

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@gmail.com>
---
 contrib/gitview/gitview |  260 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 256 insertions(+), 4 deletions(-)

diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview
index 521b2fc..4b8cde1 100755
--- a/contrib/gitview/gitview
+++ b/contrib/gitview/gitview
@@ -10,7 +10,8 @@ GUI browser for git repository
 This program is based on bzrk by Scott James Remnant <scott@ubuntu.com>
 """
 __copyright__ = "Copyright (C) 2006 Hewlett-Packard Development Company, L.P."
-__author__    = "Aneesh Kumar K.V <aneesh.kumar@hp.com>"
+__copyright__ = "Copyright (C) 2007 Aneesh Kumar K.V <aneesh.kumar@gmail.com"
+__author__    = "Aneesh Kumar K.V <aneesh.kumar@gmail.com>"
 
 
 import sys
@@ -24,6 +25,7 @@ import gobject
 import cairo
 import math
 import string
+import fcntl
 
 try:
     import gtksourceview
@@ -337,6 +339,186 @@ class Commit:
 		fp.close()
 		return diff
 
+class AnnotateWindow:
+	"""Annotate window.
+	This object represents and manages a single window containing the
+	annotate information of the file
+	"""
+
+	def __init__(self):
+		self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+		self.window.set_border_width(0)
+		self.window.set_title("Git repository browser annotation window")
+
+		# Use two thirds of the screen by default
+		screen = self.window.get_screen()
+		monitor = screen.get_monitor_geometry(0)
+		width = int(monitor.width * 0.66)
+		height = int(monitor.height * 0.66)
+		self.window.set_default_size(width, height)
+
+	def add_file_data(self, filename, commit_sha1, line_num):
+		fp = os.popen("git cat-file blob " + commit_sha1 +":"+filename)
+		i = 1;
+		for line in fp.readlines():
+			line = string.strip(line)
+			self.model.append(None, ["HEAD", filename, line, i])
+			i = i+1
+		fp.close()
+
+		# now set the cursor position
+		self.treeview.set_cursor(line_num-1)
+		self.treeview.grab_focus()
+
+	def _treeview_cursor_cb(self, *args):
+		"""Callback for when the treeview cursor changes."""
+		(path, col) = self.treeview.get_cursor()
+		commit_sha1 = self.model[path][0]
+		commit_msg = ""
+		fp = os.popen("git cat-file commit " + commit_sha1)
+		for line in fp.readlines():
+			commit_msg =  commit_msg + line
+		fp.close()
+
+		self.commit_buffer.set_text(commit_msg)
+
+	def _treeview_row_activated(self, *args):
+		"""Callback for when the treeview row gets selected."""
+		(path, col) = self.treeview.get_cursor()
+		commit_sha1 = self.model[path][0]
+		filename    = self.model[path][1]
+		line_num    = self.model[path][3]
+
+		window = AnnotateWindow();
+		fp = os.popen("git rev-parse "+ commit_sha1 + "~1")
+		commit_sha1 = string.strip(fp.readline())
+		fp.close()
+		window.annotate(filename, commit_sha1, line_num)
+
+	def data_ready(self, source, condition):
+		while (1):
+			try :
+				buffer = source.read(8192)
+			except:
+				# resource temporary not available
+				return True
+
+			if (len(buffer) == 0):
+				gobject.source_remove(self.io_watch_tag)
+				source.close()
+				return False
+
+			for buff in buffer.split("\n"):
+				annotate_line = re.compile('^([0-9a-f]{40}) (.+) (.+) (.+)$')
+				m = annotate_line.match(buff)
+				if not m:
+					annotate_line = re.compile('^(filename) (.+)$')
+					m = annotate_line.match(buff)
+					if not m:
+						continue
+					filename = m.group(2)
+				else:
+					self.commit_sha1 = m.group(1)
+					self.source_line = int(m.group(2))
+					self.result_line = int(m.group(3))
+					self.count	    = int(m.group(4))
+					#set the details only when we have the file name
+					continue
+
+				while (self.count > 0):
+					# set at result_line + count-1 the sha1 as commit_sha1
+					self.count = self.count - 1
+					iter = self.model.iter_nth_child(None, self.result_line + self.count-1)
+					self.model.set(iter, 0, self.commit_sha1, 1, filename, 3, self.source_line)
+
+
+	def annotate(self, filename, commit_sha1, line_num):
+		# verify the commit_sha1 specified has this filename
+
+		fp = os.popen("git ls-tree "+ commit_sha1 + " -- " + filename)
+		line = string.strip(fp.readline())
+		if line == '':
+			# pop up the message the file is not there as a part of the commit
+			fp.close()
+			dialog = gtk.MessageDialog(parent=None, flags=0,
+					type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE,
+					message_format=None)
+			dialog.set_markup("The file %s is not present in the parent commit %s" % (filename, commit_sha1))
+			dialog.run()
+			dialog.destroy()
+			return
+
+		fp.close()
+
+		vpan = gtk.VPaned();
+		self.window.add(vpan);
+		vpan.show()
+
+		scrollwin = gtk.ScrolledWindow()
+		scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+		scrollwin.set_shadow_type(gtk.SHADOW_IN)
+		vpan.pack1(scrollwin, True, True);
+		scrollwin.show()
+
+		self.model = gtk.TreeStore(str, str, str, int)
+		self.treeview = gtk.TreeView(self.model)
+		self.treeview.set_rules_hint(True)
+		self.treeview.set_search_column(0)
+		self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
+		self.treeview.connect("row-activated", self._treeview_row_activated)
+		scrollwin.add(self.treeview)
+		self.treeview.show()
+
+		cell = gtk.CellRendererText()
+		cell.set_property("width-chars", 10)
+		cell.set_property("ellipsize", pango.ELLIPSIZE_END)
+		column = gtk.TreeViewColumn("Commit")
+		column.set_resizable(True)
+		column.pack_start(cell, expand=True)
+		column.add_attribute(cell, "text", 0)
+		self.treeview.append_column(column)
+
+		cell = gtk.CellRendererText()
+		cell.set_property("width-chars", 20)
+		cell.set_property("ellipsize", pango.ELLIPSIZE_END)
+		column = gtk.TreeViewColumn("File Name")
+		column.set_resizable(True)
+		column.pack_start(cell, expand=True)
+		column.add_attribute(cell, "text", 1)
+		self.treeview.append_column(column)
+
+		cell = gtk.CellRendererText()
+		cell.set_property("width-chars", 20)
+		cell.set_property("ellipsize", pango.ELLIPSIZE_END)
+		column = gtk.TreeViewColumn("Data")
+		column.set_resizable(True)
+		column.pack_start(cell, expand=True)
+		column.add_attribute(cell, "text", 2)
+		self.treeview.append_column(column)
+
+		# The commit message window
+		scrollwin = gtk.ScrolledWindow()
+		scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+		scrollwin.set_shadow_type(gtk.SHADOW_IN)
+		vpan.pack2(scrollwin, True, True);
+		scrollwin.show()
+
+		commit_text = gtk.TextView()
+		self.commit_buffer = gtk.TextBuffer()
+		commit_text.set_buffer(self.commit_buffer)
+		scrollwin.add(commit_text)
+		commit_text.show()
+
+		self.window.show()
+
+		self.add_file_data(filename, commit_sha1, line_num)
+
+		fp = os.popen("git blame --incremental -- " + filename + " " + commit_sha1)
+		flags = fcntl.fcntl(fp.fileno(), fcntl.F_GETFL)
+		fcntl.fcntl(fp.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK)
+		self.io_watch_tag = gobject.io_add_watch(fp, gobject.IO_IN, self.data_ready)
+
+
 class DiffWindow:
 	"""Diff window.
 	This object represents and manages a single window containing the
@@ -355,6 +537,7 @@ class DiffWindow:
 		height = int(monitor.height * 0.66)
 		self.window.set_default_size(width, height)
 
+
 		self.construct()
 
 	def construct(self):
@@ -371,10 +554,12 @@ class DiffWindow:
 		vbox.pack_start(menu_bar, expand=False, fill=True)
 		menu_bar.show()
 
+		hpan = gtk.HPaned()
+
 		scrollwin = gtk.ScrolledWindow()
 		scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 		scrollwin.set_shadow_type(gtk.SHADOW_IN)
-		vbox.pack_start(scrollwin, expand=True, fill=True)
+		hpan.pack1(scrollwin, True, True)
 		scrollwin.show()
 
 		if have_gtksourceview:
@@ -388,11 +573,77 @@ class DiffWindow:
 			self.buffer = gtk.TextBuffer()
 			sourceview = gtk.TextView(self.buffer)
 
+
 		sourceview.set_editable(False)
 		sourceview.modify_font(pango.FontDescription("Monospace"))
 		scrollwin.add(sourceview)
 		sourceview.show()
 
+		# The file hierarchy: a scrollable treeview
+		scrollwin = gtk.ScrolledWindow()
+		scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+		scrollwin.set_shadow_type(gtk.SHADOW_IN)
+		scrollwin.set_size_request(20, -1)
+		hpan.pack2(scrollwin, True, True)
+		scrollwin.show()
+
+		self.model = gtk.TreeStore(str, str, str)
+		self.treeview = gtk.TreeView(self.model)
+		self.treeview.set_search_column(1)
+		self.treeview.connect("cursor-changed", self._treeview_clicked)
+		scrollwin.add(self.treeview)
+		self.treeview.show()
+
+		cell = gtk.CellRendererText()
+		cell.set_property("width-chars", 20)
+		column = gtk.TreeViewColumn("Select to annotate")
+		column.pack_start(cell, expand=True)
+		column.add_attribute(cell, "text", 0)
+		self.treeview.append_column(column)
+
+		vbox.pack_start(hpan, expand=True, fill=True)
+		hpan.show()
+
+	def _treeview_clicked(self, *args):
+		"""Callback for when the treeview cursor changes."""
+		(path, col) = self.treeview.get_cursor()
+		specific_file = self.model[path][1]
+		commit_sha1 =  self.model[path][2]
+		if specific_file ==  None :
+			return
+		elif specific_file ==  "" :
+			specific_file =  None
+
+		window = AnnotateWindow();
+		window.annotate(specific_file, commit_sha1, 1)
+
+
+	def commit_files(self, commit_sha1, parent_sha1):
+		self.model.clear()
+		add  = self.model.append(None, [ "Added", None, None])
+		dele = self.model.append(None, [ "Deleted", None, None])
+		mod  = self.model.append(None, [ "Modified", None, None])
+		diff_tree = re.compile('^(:.{6}) (.{6}) (.{40}) (.{40}) (A|D|M)\s(.+)$')
+		fp = os.popen("git diff-tree -r --no-commit-id " + parent_sha1 + " " + commit_sha1)
+		while 1:
+			line = string.strip(fp.readline())
+			if line == '':
+				break
+			m = diff_tree.match(line)
+			if not m:
+				continue
+
+			attr = m.group(5)
+			filename = m.group(6)
+			if attr == "A":
+				self.model.append(add,  [filename, filename, commit_sha1])
+			elif attr == "D":
+				self.model.append(dele, [filename, filename, commit_sha1])
+			elif attr == "M":
+				self.model.append(mod,  [filename, filename, commit_sha1])
+		fp.close()
+
+		self.treeview.expand_all()
 
 	def set_diff(self, commit_sha1, parent_sha1, encoding):
 		"""Set the differences showed by this window.
@@ -406,6 +657,7 @@ class DiffWindow:
 		fp = os.popen("git diff-tree -p " + parent_sha1 + " " + commit_sha1)
 		self.buffer.set_text(unicode(fp.read(), encoding).encode('utf-8'))
 		fp.close()
+		self.commit_files(commit_sha1, parent_sha1)
 		self.window.show()
 
 	def save_menu_response(self, widget, string):
@@ -425,7 +677,7 @@ class DiffWindow:
 class GitView:
 	""" This is the main class
 	"""
-	version = "0.8"
+	version = "0.9"
 
 	def __init__(self, with_diff=0):
 		self.with_diff = with_diff
@@ -590,7 +842,7 @@ class GitView:
 		dialog = gtk.AboutDialog()
 		dialog.set_name("Gitview")
 		dialog.set_version(GitView.version)
-		dialog.set_authors(["Aneesh Kumar K.V <aneesh.kumar@hp.com>"])
+		dialog.set_authors(["Aneesh Kumar K.V <aneesh.kumar@gmail.com>"])
 		dialog.set_website("http://www.kernel.org/pub/software/scm/git/")
 		dialog.set_copyright("Use and distribute under the terms of the GNU General Public License")
 		dialog.set_wrap_license(True)
-- 
1.5.1.1.135.gf948-dirty

                 reply	other threads:[~2007-04-19 16:56 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=11770017633282-git-send-email-aneesh.kumar@gmail.com \
    --to=aneesh.kumar@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=junkio@cox.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.