remove .fns file for removed host burns
[build-farm.git] / buildfarm / history.py
index 6ca1af86fa764ba92731bee24f9d240a32275047..1d00b758df107654034b7f3247f0e233d60bcf03 100644 (file)
 #   along with this program; if not, write to the Free Software
 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
+from cStringIO import StringIO
 
-from buildfarm import util
+from dulwich.objects import Tree
+from dulwich.patch import write_tree_diff
+from dulwich.repo import Repo
 
-import commands
-import os
 
-BASEDIR = "/home/build/master"
-HISTORYDIR = "/home/build/master/cache"
-TIMEZONE = "PST"
-TIMEOFFSET = 0
-UNPACKED_DIR = "/home/ftp/pub/unpacked"
+class Branch(object):
+    """A version control branch."""
 
-class History(object):
+    def log(self, limit=None):
+        raise NotImplementedError(self.log)
 
-    def __init__(self, db):
-        self.db = db
+    def diff(self, revision):
+        raise NotImplementedError(self.diff)
 
-    def _log(self, tree):
-        return util.LoadStructure(os.path.join(HISTORYDIR, "history.%s" % tree))
+    def changes_summary(self, revision):
+        raise NotImplementedError(self.changes_summary)
 
-    def diff(self, author, date, tree, revision):
-        """get recent git entries"""
-        # validate the tree
-        t = self.db.trees[tree]
 
-        if t.scm == "git":
-            self._git_diff(t, revision, tree)
-        else:
-            raise Exception("Unknown VCS %s" % t.scm)
+class Revision(object):
+
+    def __init__(self, revision, date, committer, author, message):
+        self.revision = revision
+        self.date = date
+        self.author = author
+        self.committer = committer
+        self.message = message
 
-    def _git_diff(self, t, revision, tree):
-        """show recent git entries"""
 
-        log = self._log(tree)
+class GitBranch(Branch):
 
-        # backwards? why? well, usually our users are looking for the newest
-        # stuff, so it's most likely to be found sooner
-        for i in range(len(log), 0, -1):
-            if log[i]["REVISION"] == revision:
-                entry = log[i]
-                break
+    def __init__(self, path, branch="master"):
+        self.repo = Repo(path)
+        self.store = self.repo.object_store
+        self.branch = branch
+
+    def _changes_for(self, commit):
+        if len(commit.parents) == 0:
+            parent_tree = Tree().id
+        else:
+            parent_tree = self.store[commit.parents[0]].tree
+        return self.store.tree_changes(parent_tree, commit.tree)
+
+    def _revision_from_commit(self, commit):
+        return Revision(commit.id, commit.commit_time,
+            committer=commit.committer, author=commit.author,
+            message=commit.message)
+
+    def log(self, from_rev=None, exclude_revs=None, limit=None):
+        if exclude_revs is None:
+            exclude_revs = set()
+        if from_rev is None:
+            try:
+                commit = self.repo["refs/heads/%s" % self.branch]
+            except KeyError:
+                return
+            from_rev = commit.id
+        done = set()
+        pending_commits = [from_rev]
+        while pending_commits != []:
+             commit_id = pending_commits.pop(0)
+             commit = self.repo[commit_id]
+             yield self._revision_from_commit(commit)
+             done.add(commit.id)
+             if len(done) >= limit:
+                 return
+             exclude_revs.add(commit.id)
+             # FIXME: Add sorted by commit_time
+             for p in commit.parents:
+                 if p in exclude_revs:
+                     continue
+                 pending_commits.append(p)
+                 exclude_revs.add(p)
+
+    def changes_summary(self, revision):
+        commit = self.repo[revision]
+        added = set()
+        modified = set()
+        removed = set()
+        for ((oldpath, newpath), (oldmode, newmode), (oldsha, newsha)) in self._changes_for(commit):
+            if oldpath is None:
+                added.add(newpath)
+            elif newpath is None:
+                removed.add(oldpath)
+            else:
+                modified.add(newpath)
+        return (added, modified, removed)
+
+    def diff(self, revision):
+        commit = self.repo[revision]
+        f = StringIO()
+        if len(commit.parents) == 0:
+            parent_tree = Tree().id
         else:
-            raise Exception("Unable to locate commit information revision[%s]." % revision)
-
-        # get information about the current diff
-        title = "GIT Diff in %s:%s for revision %s" % (
-            tree, t.branch, revision)
-
-        pwd = os.environ["PWD"]
-        ret = None
-        try:
-            os.chdir(os.path.join(UNPACKED_DIR, tree))
-            cmd = "git diff %s^ %s ./" % (revision, revision)
-            ret = (title, entry, tree, [(cmd, commands.getoutput("%s 2> /dev/null" % cmd))])
-
-        finally:
-            os.chdir(pwd)
-            return ret
-
-    def authors(self, tree):
-        log = self._log(tree)
-        authors = set()
-        for entry in log:
-            authors.add(entry["AUTHOR"])
-        return authors
-
-    def history(self, tree, author=None):
-        """get commit history for the given tree"""
-        log = self._log(tree)
-
-        # what? backwards? why is that? oh... I know... we want the newest first
-        for i in range(len(log), 0, -1):
-            entry = log[i]
-            if (author is None or
-                (author == "") or
-                (author == "ALL") or
-                (author == entry["AUTHOR"])):
-                yield entry, tree
+            parent_tree = self.store[commit.parents[0]].tree
+        write_tree_diff(f, self.store, parent_tree, commit.tree)
+        return (self._revision_from_commit(commit), f.getvalue())