1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
# tree.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 os
from lazy import LazyMixin
import blob
import submodule
class Tree(LazyMixin):
def __init__(self, repo, id, mode=None, name=None, commit_context = '', 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):
# Ensure the treeish references directly a tree
treeish = self.id
if not treeish.endswith(':'):
treeish = treeish + ':'
# 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, 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, commit_context = None, path=''):
"""
Parse a content item and create the appropriate object
``repo``
is the Repo
``text``
is the single line containing the items data in `git ls-tree` format
Returns
``git.Blob`` or ``git.Tree``
"""
try:
mode, typ, id, name = text.expandtabs(1).split(" ", 3)
except:
return None
if typ == "tree":
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" 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)
def __div__(self, file):
"""
Find the named object in this tree's contents
Examples::
>>> Repo('/path/to/python-git').tree()/'lib'
<git.Tree "6cc23ee138be09ff8c28b07162720018b244e95e">
>>> Repo('/path/to/python-git').tree()/'README'
<git.Blob "8b1e02c0fb554eed2ce2ef737a68bb369d7527df">
Returns
``git.Blob`` or ``git.Tree`` or ``None`` if not found
"""
return self.get(file)
@property
def basename(self):
os.path.basename(self.name)
def __repr__(self):
return '<git.Tree "%s">' % self.id
# Implement the basics of the dict protocol:
# directories/trees can be seen as object dicts.
def __getitem__(self, key):
return self._contents[key]
def __iter__(self):
return iter(self._contents)
def __len__(self):
return len(self._contents)
def __contains__(self, key):
return key in self._contents
def get(self, key):
return self._contents.get(key)
def items(self):
return self._contents.items()
def keys(self):
return self._contents.keys()
def values(self):
return self._contents.values()
|