Wrap trees and blobs in Python objects (just like commits were already wrapped), so that StGit code can read and write them. Signed-off-by: Karl Hasselström <kha@xxxxxxxxxxx> --- I'm quite pleased with the Foo/Foodata distinction (the former being a blob, tree, or commit in the git repository, the latter an immutable specification for such an object). It's convenient to work with, without loss of efficiency. stgit/lib/git.py | 139 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 files changed, 118 insertions(+), 21 deletions(-) diff --git a/stgit/lib/git.py b/stgit/lib/git.py index 6ee8a71..8184fec 100644 --- a/stgit/lib/git.py +++ b/stgit/lib/git.py @@ -146,13 +146,100 @@ class Person(Repr): defaults = cls.user()) return cls.__committer -class Tree(Repr): +class GitObject(Repr): + """Base class for all git objects.""" + +class Blobdata(Repr): + def __init__(self, string): + self.__string = str(string) + str = property(lambda self: self.__string) + def commit(self, repository): + sha1 = repository.run(['git', 'hash-object', '-w', '--stdin'] + ).raw_input(self.str).output_one_line() + return repository.get_blob(sha1) + +class Blob(GitObject): """Immutable.""" - def __init__(self, sha1): + typename = 'blob' + default_perm = '100644' + def __init__(self, repository, sha1): + self.__repository = repository self.__sha1 = sha1 sha1 = property(lambda self: self.__sha1) def __str__(self): - return 'Tree<%s>' % self.sha1 + return 'Blob<%s>' % self.sha1 + @property + def data(self): + return Blobdata(self.__repository.cat_object(self.sha1)) + +class ImmutableDict(dict): + def error(*args, **kwargs): + raise TypeError('Cannot modify immutable dict') + __delitem__ = error + __setitem__ = error + clear = error + pop = error + popitem = error + setdefault = error + update = error + +class Treedata(Repr): + """Immutable.""" + @staticmethod + def __x(po): + if isinstance(po, GitObject): + perm, object = po.default_perm, po + else: + perm, object = po + return perm, object + def __init__(self, entries): + self.__entries = ImmutableDict((name, self.__x(po)) + for (name, po) in entries.iteritems()) + entries = property(lambda self: self.__entries) + def set_entry(self, name, po): + e = dict(self.entries) + e[name] = self.__x(po) + return type(self)(e) + def del_entry(self, name): + e = dict(self.entries) + del e[name] + return type(self)(e) + def commit(self, repository): + listing = ''.join( + '%s %s %s\t%s\0' % (mode, obj.typename, obj.sha1, name) + for (name, (mode, obj)) in self.entries.iteritems()) + sha1 = repository.run(['git', 'mktree', '-z'] + ).raw_input(listing).output_one_line() + return repository.get_tree(sha1) + @classmethod + def parse(cls, repository, s): + entries = {} + for line in s.split('\0')[:-1]: + m = re.match(r'^([0-7]{6}) ([a-z]) ([0-9a-f]{40})\t(.*)$', line) + assert m + perm, type, sha1, name = m.groups() + entries[name] = (perm, repository.get_object(type, sha1)) + return cls(entries) + +class Tree(GitObject): + """Immutable.""" + typename = 'tree' + default_perm = '040000' + def __init__(self, repository, sha1): + self.__sha1 = sha1 + self.__repository = repository + self.__data = None + sha1 = property(lambda self: self.__sha1) + @property + def data(self): + if self.__data == None: + self.__data = Treedata.parse( + self.__repository, + self.__repository.run(['git', 'ls-tree', '-z', self.sha1] + ).raw_output()) + return self.__data + def __str__(self): + return 'Tree<sha1: %s>' % self.sha1 class Commitdata(Repr): """Immutable.""" @@ -202,6 +289,22 @@ class Commitdata(Repr): return ('Commitdata<tree: %s, parents: %s, author: %s,' ' committer: %s, message: "%s">' ) % (tree, parents, self.author, self.committer, self.message) + def commit(self, repository): + c = ['git', 'commit-tree', self.tree.sha1] + for p in self.parents: + c.append('-p') + c.append(p.sha1) + env = {} + for p, v1 in ((self.author, 'AUTHOR'), + (self.committer, 'COMMITTER')): + if p != None: + for attr, v2 in (('name', 'NAME'), ('email', 'EMAIL'), + ('date', 'DATE')): + if getattr(p, attr) != None: + env['GIT_%s_%s' % (v1, v2)] = str(getattr(p, attr)) + sha1 = repository.run(c, env = env).raw_input(self.message + ).output_one_line() + return repository.get_commit(sha1) @classmethod def parse(cls, repository, s): cd = cls() @@ -223,8 +326,9 @@ class Commitdata(Repr): assert False assert False -class Commit(Repr): +class Commit(GitObject): """Immutable.""" + typename = 'commit' def __init__(self, repository, sha1): self.__sha1 = sha1 self.__repository = repository @@ -302,7 +406,8 @@ class Repository(RunWithEnv): def __init__(self, directory): self.__git_dir = directory self.__refs = Refs(self) - self.__trees = ObjectCache(lambda sha1: Tree(sha1)) + self.__blobs = ObjectCache(lambda sha1: Blob(self, sha1)) + self.__trees = ObjectCache(lambda sha1: Tree(self, sha1)) self.__commits = ObjectCache(lambda sha1: Commit(self, sha1)) self.__default_index = None self.__default_worktree = None @@ -353,26 +458,18 @@ class Repository(RunWithEnv): ).output_one_line()) except run.RunException: raise RepositoryException('%s: No such revision' % rev) + def get_blob(self, sha1): + return self.__blobs[sha1] def get_tree(self, sha1): return self.__trees[sha1] def get_commit(self, sha1): return self.__commits[sha1] - def commit(self, commitdata): - c = ['git', 'commit-tree', commitdata.tree.sha1] - for p in commitdata.parents: - c.append('-p') - c.append(p.sha1) - env = {} - for p, v1 in ((commitdata.author, 'AUTHOR'), - (commitdata.committer, 'COMMITTER')): - if p != None: - for attr, v2 in (('name', 'NAME'), ('email', 'EMAIL'), - ('date', 'DATE')): - if getattr(p, attr) != None: - env['GIT_%s_%s' % (v1, v2)] = str(getattr(p, attr)) - sha1 = self.run(c, env = env).raw_input(commitdata.message - ).output_one_line() - return self.get_commit(sha1) + def get_object(self, type, sha1): + return { Blob.typename: self.get_blob, + Tree.typename: self.get_tree, + Commit.typename: self.get_commit }[type](sha1) + def commit(self, objectdata): + return objectdata.commit(self) @property def head(self): try: - To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html