diff options
author | D. Dotsenko <dotsa@hotmail.com> | 2010-10-20 22:41:52 -0700 |
---|---|---|
committer | Daniel Dotsenko <dotsa@hotmail.com> | 2010-10-20 22:41:52 -0700 |
commit | cfd2121eda5fadd18fba6819f90efb8868fad14a (patch) | |
tree | da2d2b16a0a68145a5ada2d514540b20e6f79665 | |
parent | 7bfca5efce8988e6070e7284bd409d1a731a3fbc (diff) | |
download | gitpython-cfd2121eda5fadd18fba6819f90efb8868fad14a.tar.gz |
Added submodule type and handling through Tree listing.
-rw-r--r-- | lib/git/commit.py | 2 | ||||
-rwxr-xr-x | lib/git/submodule.py | 103 | ||||
-rw-r--r-- | lib/git/tree.py | 47 |
3 files changed, 145 insertions, 7 deletions
diff --git a/lib/git/commit.py b/lib/git/commit.py index b074d3a8..9a9a77ad 100644 --- a/lib/git/commit.py +++ b/lib/git/commit.py @@ -71,7 +71,7 @@ class Commit(LazyMixin): if parents is not None: self.parents = [Commit(repo, p) for p in parents] if tree is not None: - self.tree = Tree(repo, id=tree) + self.tree = Tree(repo, id=tree, commit_context = self.id) def __bake__(self): """ diff --git a/lib/git/submodule.py b/lib/git/submodule.py new file mode 100755 index 00000000..c6c506f4 --- /dev/null +++ b/lib/git/submodule.py @@ -0,0 +1,103 @@ +# head.py +# Copyright (C) 2008-2010 Michael Trier (mtrier@gmail.com) and contributors +# +# This module is part of GitPython and is released under +# the BSD License: http://www.opensource.org/licenses/bsd-license.php + +import re + +class Submodule(object): + """ + A Submodule is a named reference to a Commit on another Repo. + Every Submodule instance contains a name and local and remote paths. + + Submodules are very close in behavior to HEAD pointer. It just sits on + top of a structure, in this case, at the end of folders tree, and says + something about that ending. + + Examples:: + >>> repo = Repo("/path/to/repo") + >>> s = repo.commit('master').tree['lib']['mysubmodule_folder'] + >>> s.name + 'mysubmodule_folder' + >>> s.path + '/lib/mysubmodule_folder' + >>> s.url + 'http://example.com/path/to/repo.git' + >>> s.id + "1c09f116cbc2cb4100fb6935bb162daa4723f455" + """ + + def __init__(self, repo=None, id=None, mode=None, name='', + commit_context=None, path=''): + """ + Initialize a newly instanced Submodule + + 'repo' + Pointer to Repo object instance. + 'id' + Is the Sha of the commit on a remote server. This object does NOT + (usually) exist on this, current repo. + 'mode' + A black hole at this time. Trying to keep the input args + similar between Tree, Blob and Subprocess instantiation classes. + 'name' + This is just the last segment in the submodule's full local path. + It's the name of the actual folder to which a submodule is tied. + 'mode' + A black hole at this time. Trying to keep the input args + similar between Tree, Blob and Subprocess instantiation classes. + 'commit_context' + A string with ID of the commit that was the root for the tree + structure that lead us to this folder (Tree object) that contains + this submodule reference. + See comments in Tree object code for background. + 'path' + This is the "longer" version of "name" argument. It includes all + the parent folders we passed on the way from root of the commit to + this point in the folder tree. + Submodules in the .gitmodules are referenced by their full path + and contents of this argument are used to retrieve the URI of the + remote repo tied to this full local path. + Example: "/lib/vendor/vendors_repoA" + """ + self.repo = repo + self.id = id + self.path = path + self.name = name + self._commit_context = commit_context + self._cached_URI = None + + def getURI(self, commit_context = None): + '''Returns the remote repo URI for the submodule. + + This data is NOT stored in the blob or anywhere close. It's in a + .gitmodules file in the root Tree of SOME commit. + + We need to know what commit to look into to look for .gitmodules. + + We try to retain the "origin" commit ID within the object when we + traverse the Tree chain if it started with a particular commit. + + When this does not work, or if you want to override the behavior, + pass the string with commit's ID to the commit_context argument. + ''' + if not self._cached_URI and ( commit_context or self._commit_context ): + _b = self.repo.commit(commit_context or self._commit_context).tree.get('.gitmodules') + if _b: + _m = re.findall( + r'\[submodule "[^\t]+?\s+path\s*=\s*([^\t]+)\s+url\s*=\s*([^\t]+)' + ,'\t'.join(_b.data.splitlines()) + ) + for _e in _m: + if _e[0] == self.path.strip('/'): + self._cached_URI = _e[1].strip().strip('"').strip("'") + break + return self._cached_URI + + @property + def url(self): + return self.getURI() + + def __repr__(self): + return '<git.Submodule "%s">' % self.id diff --git a/lib/git/tree.py b/lib/git/tree.py index 02266dce..45f5fdcb 100644 --- a/lib/git/tree.py +++ b/lib/git/tree.py @@ -7,14 +7,47 @@ import os from lazy import LazyMixin import blob +import submodule class Tree(LazyMixin): - def __init__(self, repo, id, mode=None, name=None): + def __init__(self, repo, id, mode=None, name=None, commit_context = None, path = ''): LazyMixin.__init__(self) self.repo = repo self.id = id self.mode = mode self.name = name + # commit_context (A string with ID of the commit) is a "crutch" that + # allows us to look up details for submodules, should we find any in + # this particular tree. + # Trees don't have a reference to parent (commit, other tree). + # They can have infinite amounts of parents. + # However, we need to know what commit got us to this particular + # tree if we want to know the URI of the submodule. + # The commit ID of the repo pointed out by submodule is here, in the tree. + # However, the only way to know what URI that submodule refers to is + # to read .gitmodules file that's in the top-most tree of SOME commit. + # Each commit can have a different version of .gitmodule, but through + # tree chain lead to the same Tree instance where the submodule is rooted. + # + # There is a short-cut. If submodule is placed in top-most Tree in a + # commit (i.e. submodule's path value is "mysubmodule") the .gitmodules + # file will be in the same exact tree. YEY! we just read that and know + # the submodule's URI. Shortcut is gone when submodule is nested in the + # commit like so: "commonfolder/otherfolder/mysubmodule" In this case, + # commit's root tree will have "Tree 'commonfolder'" which will have + # "Tree "otherfolder", which will have "Submodule 'mysubmodule'" + # By the time we get to "Tree 'otherfolder'" we don't know where to + # look for ".gitmodules". This is what commit_context is for. + # The only way you get a value here if you either set it by hand, or + # traverse the Tree chain that started with CommitInstance.tree, which + # populates the context upon Tree instantiation. + self.commit_context = commit_context + # path is the friend commit_context. since trees don't have links to + # parents, we have no clue what the "full local path" of a child + # submodule would be. Submodules are listed as "name" in trees and + # as "folder/folder/name" in .gitmodules. path helps us keep up with the + # the folder changes. + self.path = path self._contents = None def __bake__(self): @@ -26,12 +59,12 @@ class Tree(LazyMixin): # Read the tree contents. self._contents = {} for line in self.repo.git.ls_tree(self.id).splitlines(): - obj = self.content_from_string(self.repo, line) + obj = self.content_from_string(self.repo, line, commit_context = self.commit_context, path = self.path) if obj is not None: self._contents[obj.name] = obj @staticmethod - def content_from_string(repo, text): + def content_from_string(repo, text, commit_context = None, path=''): """ Parse a content item and create the appropriate object @@ -50,11 +83,13 @@ class Tree(LazyMixin): return None if typ == "tree": - return Tree(repo, id=id, mode=mode, name=name) + return Tree(repo, id=id, mode=mode, name=name, + commit_context = commit_context, path='/'.join([path,name])) elif typ == "blob": return blob.Blob(repo, id=id, mode=mode, name=name) - elif typ == "commit": - return None + elif typ == "commit" and mode == '160000': + return submodule.Submodule(repo, id=id, name=name, + commit_context = commit_context, path='/'.join([path,name])) else: raise(TypeError, "Invalid type: %s" % typ) |