2 from stgit
import exception
, run
, utils
4 class RepositoryException(exception
.StgException
):
7 class DetachedHeadException(RepositoryException
):
9 RepositoryException
.__init__(self
, 'Not on any branch')
15 class NoValue(object):
18 def make_defaults(defaults
):
22 elif defaults
!= NoValue
:
23 return getattr(defaults
, attr
)
30 def __init__(self
, name
= NoValue
, email
= NoValue
,
31 date
= NoValue
, defaults
= NoValue
):
32 d
= make_defaults(defaults
)
33 self
.__name
= d(name
, 'name')
34 self
.__email
= d(email
, 'email')
35 self
.__date
= d(date
, 'date')
36 name
= property(lambda self
: self
.__name
)
37 email
= property(lambda self
: self
.__email
)
38 date
= property(lambda self
: self
.__date
)
39 def set_name(self
, name
):
40 return type(self
)(name
= name
, defaults
= self
)
41 def set_email(self
, email
):
42 return type(self
)(email
= email
, defaults
= self
)
43 def set_date(self
, date
):
44 return type(self
)(date
= date
, defaults
= self
)
46 return '%s <%s> %s' %
(self
.name
, self
.email
, self
.date
)
49 m
= re
.match(r
'^([^<]*)<([^>]*)>\s+(\d+\s+[+-]\d{4})$', s
)
51 name
= m
.group(1).strip()
54 return cls(name
, email
, date
)
58 def __init__(self
, sha1
):
60 sha1
= property(lambda self
: self
.__sha1
)
62 return 'Tree<%s>' % self
.sha1
64 class Commitdata(Repr
):
66 def __init__(self
, tree
= NoValue
, parents
= NoValue
, author
= NoValue
,
67 committer
= NoValue
, message
= NoValue
, defaults
= NoValue
):
68 d
= make_defaults(defaults
)
69 self
.__tree
= d(tree
, 'tree')
70 self
.__parents
= d(parents
, 'parents')
71 self
.__author
= d(author
, 'author')
72 self
.__committer
= d(committer
, 'committer')
73 self
.__message
= d(message
, 'message')
74 tree
= property(lambda self
: self
.__tree
)
75 parents
= property(lambda self
: self
.__parents
)
78 assert len(self
.__parents
) == 1
79 return self
.__parents
[0]
80 author
= property(lambda self
: self
.__author
)
81 committer
= property(lambda self
: self
.__committer
)
82 message
= property(lambda self
: self
.__message
)
83 def set_tree(self
, tree
):
84 return type(self
)(tree
= tree
, defaults
= self
)
85 def set_parents(self
, parents
):
86 return type(self
)(parents
= parents
, defaults
= self
)
87 def add_parent(self
, parent
):
88 return type(self
)(parents
= list(self
.parents
or []) + [parent
],
90 def set_parent(self
, parent
):
91 return self
.set_parents([parent
])
92 def set_author(self
, author
):
93 return type(self
)(author
= author
, defaults
= self
)
94 def set_committer(self
, committer
):
95 return type(self
)(committer
= committer
, defaults
= self
)
96 def set_message(self
, message
):
97 return type(self
)(message
= message
, defaults
= self
)
102 tree
= self
.tree
.sha1
103 if self
.parents
== None:
106 parents
= [p
.sha1
for p
in self
.parents
]
107 return ('Commitdata<tree: %s, parents: %s, author: %s,'
108 ' committer: %s, message: "%s">'
109 ) %
(tree
, parents
, self
.author
, self
.committer
, self
.message
)
111 def parse(cls
, repository
, s
):
113 lines
= list(s
.splitlines(True))
114 for i
in xrange(len(lines
)):
115 line
= lines
[i
].strip()
117 return cd
.set_message(''.join(lines
[i
+1:]))
118 key
, value
= line
.split(None, 1)
120 cd
= cd
.set_tree(repository
.get_tree(value
))
121 elif key
== 'parent':
122 cd
= cd
.add_parent(repository
.get_commit(value
))
123 elif key
== 'author':
124 cd
= cd
.set_author(Person
.parse(value
))
125 elif key
== 'committer':
126 cd
= cd
.set_committer(Person
.parse(value
))
133 def __init__(self
, repository
, sha1
):
135 self
.__repository
= repository
137 sha1
= property(lambda self
: self
.__sha1
)
140 if self
.__data
== None:
141 self
.__data
= Commitdata
.parse(
143 self
.__repository
.cat_object(self
.sha1
))
146 return 'Commit<sha1: %s, data: %s>' %
(self
.sha1
, self
.__data
)
149 def __init__(self
, repository
):
150 self
.__repository
= repository
152 def __cache_refs(self
):
154 for line
in self
.__repository
.run(['git', 'show-ref']).output_lines():
155 m
= re
.match(r
'^([0-9a-f]{40})\s+(\S+)$', line
)
156 sha1
, ref
= m
.groups()
157 self
.__refs
[ref
] = sha1
159 """Throws KeyError if ref doesn't exist."""
160 if self
.__refs
== None:
162 return self
.__repository
.get_commit(self
.__refs
[ref
])
163 def exists(self
, ref
):
170 def set(self
, ref
, commit
, msg
):
171 if self
.__refs
== None:
173 old_sha1
= self
.__refs
.get(ref
, '0'*40)
174 new_sha1
= commit
.sha1
175 if old_sha1
!= new_sha1
:
176 self
.__repository
.run(['git', 'update-ref', '-m', msg
,
177 ref
, new_sha1
, old_sha1
]).no_output()
178 self
.__refs
[ref
] = new_sha1
179 def delete(self
, ref
):
180 if self
.__refs
== None:
182 self
.__repository
.run(['git', 'update-ref',
183 '-d', ref
, self
.__refs
[ref
]]).no_output()
186 class ObjectCache(object):
187 """Cache for Python objects, for making sure that we create only one
188 Python object per git object."""
189 def __init__(self
, create
):
191 self
.__create
= create
192 def __getitem__(self
, name
):
193 if not name
in self
.__objects
:
194 self
.__objects
[name
] = self
.__create(name
)
195 return self
.__objects
[name
]
196 def __contains__(self
, name
):
197 return name
in self
.__objects
198 def __setitem__(self
, name
, val
):
199 assert not name
in self
.__objects
200 self
.__objects
[name
] = val
202 class RunWithEnv(object):
203 def run(self
, args
, env
= {}):
204 return run
.Run(*args
).env(utils
.add_dict(self
.env
, env
))
206 class Repository(RunWithEnv
):
207 def __init__(self
, directory
):
208 self
.__git_dir
= directory
209 self
.__refs
= Refs(self
)
210 self
.__trees
= ObjectCache(lambda sha1
: Tree(sha1
))
211 self
.__commits
= ObjectCache(lambda sha1
: Commit(self
, sha1
))
212 env
= property(lambda self
: { 'GIT_DIR': self
.__git_dir
})
215 """Return the default repository."""
217 return cls(run
.Run('git', 'rev-parse', '--git-dir'
219 except run
.RunException
:
220 raise RepositoryException('Cannot find git repository')
221 directory
= property(lambda self
: self
.__git_dir
)
222 refs
= property(lambda self
: self
.__refs
)
223 def cat_object(self
, sha1
):
224 return self
.run(['git', 'cat-file', '-p', sha1
]).raw_output()
225 def rev_parse(self
, rev
):
227 return self
.get_commit(self
.run(
228 ['git', 'rev-parse', '%s^{commit}' % rev
]
230 except run
.RunException
:
231 raise RepositoryException('%s: No such revision' % rev
)
232 def get_tree(self
, sha1
):
233 return self
.__trees
[sha1
]
234 def get_commit(self
, sha1
):
235 return self
.__commits
[sha1
]
236 def commit(self
, commitdata
):
237 c
= ['git', 'commit-tree', commitdata
.tree
.sha1
]
238 for p
in commitdata
.parents
:
242 for p
, v1
in ((commitdata
.author
, 'AUTHOR'),
243 (commitdata
.committer
, 'COMMITTER')):
245 for attr
, v2
in (('name', 'NAME'), ('email', 'EMAIL'),
247 if getattr(p
, attr
) != None:
248 env
['GIT_%s_%s' %
(v1
, v2
)] = getattr(p
, attr
)
249 sha1
= self
.run(c
, env
= env
).raw_input(commitdata
.message
251 return self
.get_commit(sha1
)
255 return self
.run(['git', 'symbolic-ref', '-q', 'HEAD']
257 except run
.RunException
:
258 raise DetachedHeadException()
259 def set_head(self
, ref
, msg
):
260 self
.run(['git', 'symbolic-ref', '-m', msg
, 'HEAD', ref
]).no_output()