summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorD. Dotsenko <dotsa@hotmail.com>2010-10-20 22:41:52 -0700
committerDaniel Dotsenko <dotsa@hotmail.com>2010-10-20 22:41:52 -0700
commitcfd2121eda5fadd18fba6819f90efb8868fad14a (patch)
treeda2d2b16a0a68145a5ada2d514540b20e6f79665
parent7bfca5efce8988e6070e7284bd409d1a731a3fbc (diff)
downloadgitpython-cfd2121eda5fadd18fba6819f90efb8868fad14a.tar.gz
Added submodule type and handling through Tree listing.
-rw-r--r--lib/git/commit.py2
-rwxr-xr-xlib/git/submodule.py103
-rw-r--r--lib/git/tree.py47
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)