Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(23)

Side by Side Diff: infra/libs/git2/repo.py

Issue 413983003: Refactor infra git libs and testing. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Address comments Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4
4 import collections 5 import collections
5 import fnmatch 6 import fnmatch
6 import logging 7 import logging
7 import os 8 import os
8 import subprocess 9 import subprocess
9 import sys 10 import sys
10 import tempfile 11 import tempfile
11 import urlparse 12 import urlparse
12 13
13 from infra.services.gnumbd.support.util import ( 14 from infra.libs.git2 import CalledProcessError
14 cached_property, CalledProcessError) 15 from infra.libs.git2 import Commit
15 16 from infra.libs.git2 import Ref
16 from infra.services.gnumbd.support.data import CommitData
17 17
18 LOGGER = logging.getLogger(__name__) 18 LOGGER = logging.getLogger(__name__)
19 19
20 20
21 class _Invalid(object):
22 def __call__(self, *_args, **_kwargs):
23 return self
24
25 def __getattr__(self, _key):
26 return self
27
28 def __eq__(self, _other):
29 return False
30
31 def __ne__(self, _other): # pylint: disable=R0201
32 return True
33
34 INVALID = _Invalid()
35
36
37 class Repo(object): 21 class Repo(object):
38 """Represents a remote git repo. 22 """Represents a remote git repo.
39 23
40 Manages the (bare) on-disk mirror of the remote repo. 24 Manages the (bare) on-disk mirror of the remote repo.
41 """ 25 """
42 MAX_CACHE_SIZE = 1024 26 MAX_CACHE_SIZE = 1024
43 27
44 def __init__(self, url): 28 def __init__(self, url):
45 self.dry_run = False 29 self.dry_run = False
46 self.repos_dir = None 30 self.repos_dir = None
47 31
48 self._url = url 32 self._url = url
49 self._repo_path = None 33 self._repo_path = None
50 self._commit_cache = collections.OrderedDict() 34 self._commit_cache = collections.OrderedDict()
51 self._log = LOGGER.getChild('Repo') 35 self._log = LOGGER.getChild('Repo')
52 36
37 def __getitem__(self, ref):
38 """Get a Ref attached to this Repo."""
39 return Ref(self, ref)
40
53 def reify(self): 41 def reify(self):
54 """Ensures the local mirror of this Repo exists.""" 42 """Ensures the local mirror of this Repo exists."""
55 assert self.repos_dir is not None 43 assert self.repos_dir is not None
56 parsed = urlparse.urlparse(self._url) 44 parsed = urlparse.urlparse(self._url)
57 norm_url = parsed.netloc + parsed.path 45 norm_url = parsed.netloc + parsed.path
58 if norm_url.endswith('.git'): 46 if norm_url.endswith('.git'):
59 norm_url = norm_url[:-len('.git')] 47 norm_url = norm_url[:-len('.git')]
60 folder = norm_url.replace('-', '--').replace('/', '-').lower() 48 folder = norm_url.replace('-', '--').replace('/', '-').lower()
61 49
62 rpath = os.path.abspath(os.path.join(self.repos_dir, folder)) 50 rpath = os.path.abspath(os.path.join(self.repos_dir, folder))
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
139 if retcode not in ok_ret: 127 if retcode not in ok_ret:
140 raise CalledProcessError(retcode, cmd, output, errout) 128 raise CalledProcessError(retcode, cmd, output, errout)
141 129
142 if errout: 130 if errout:
143 sys.stderr.write(errout) 131 sys.stderr.write(errout)
144 return output 132 return output
145 133
146 def intern(self, data, typ='blob'): 134 def intern(self, data, typ='blob'):
147 return self.run( 135 return self.run(
148 'hash-object', '-w', '-t', typ, '--stdin', indata=str(data)).strip() 136 'hash-object', '-w', '-t', typ, '--stdin', indata=str(data)).strip()
149
150
151 class Commit(object):
152 """Represents the identity of a commit in a git repo."""
153
154 def __init__(self, repo, hsh):
155 """
156 @type repo: Repo
157 """
158 assert CommitData.HASH_RE.match(hsh)
159 self._repo = repo
160 self._hsh = hsh
161
162 # Comparison & Representation
163 def __eq__(self, other):
164 return (self is other) or (
165 isinstance(other, Commit) and (
166 self.hsh == other.hsh
167 )
168 )
169
170 def __ne__(self, other):
171 return not (self == other)
172
173 def __repr__(self):
174 return 'Commit({_repo!r}, {_hsh!r})'.format(**self.__dict__)
175
176 # Accessors
177 # pylint: disable=W0212
178 repo = property(lambda self: self._repo)
179 hsh = property(lambda self: self._hsh)
180
181 # Properties
182 @cached_property
183 def data(self):
184 """Get a structured data representation of this commit."""
185 try:
186 raw_data = self.repo.run('cat-file', 'commit', self.hsh)
187 except CalledProcessError:
188 return INVALID
189 return CommitData.from_raw(raw_data)
190
191 @cached_property
192 def parent(self):
193 """Get the corresponding parent Commit() for this Commit(), or None.
194
195 If self has more than one parent, this raises an Exception.
196 """
197 parents = self.data.parents
198 if len(parents) > 1:
199 LOGGER.error('Commit %r has more than one parent!', self.hsh)
200 return INVALID
201 return self.repo.get_commit(parents[0]) if parents else None
202
203 # Methods
204 def alter(self, **kwargs):
205 """Get a new Commit which is the same as this one, except for alterations
206 specified by kwargs.
207
208 This will intern the new Commit object into the Repo.
209 """
210 return self.repo.get_commit(
211 self.repo.intern(self.data.alter(**kwargs), 'commit'))
212
213
214 class Ref(object):
215 """Represents a single simple ref in a git Repo."""
216 def __init__(self, repo, ref_str):
217 """
218 @type repo: Repo
219 @type ref_str: str
220 """
221 self._repo = repo
222 self._ref = ref_str
223
224 # Comparison & Representation
225 def __eq__(self, other):
226 return (self is other) or (
227 isinstance(other, Ref) and (
228 self.ref == other.ref and
229 self.repo is other.repo
230 )
231 )
232
233 def __ne__(self, other):
234 return not (self == other)
235
236 def __repr__(self):
237 return 'Ref({_repo!r}, {_ref!r})'.format(**self.__dict__)
238
239 # Accessors
240 # pylint: disable=W0212
241 repo = property(lambda self: self._repo)
242 ref = property(lambda self: self._ref)
243
244 # Properties
245 @property
246 def commit(self):
247 """Get the Commit at the tip of this Ref."""
248 try:
249 val = self._repo.run('show-ref', '--verify', self._ref)
250 except CalledProcessError:
251 return INVALID
252 return self._repo.get_commit(val.split()[0])
253
254 # Methods
255 def to(self, other):
256 """Generate Commit()'s which occur from `self..other`."""
257 assert self.commit is not INVALID
258 arg = '%s..%s' % (self.ref, other.ref)
259 for hsh in self.repo.run('rev-list', '--reverse', arg).splitlines():
260 yield self.repo.get_commit(hsh)
261
262 def fast_forward_push(self, commit):
263 """Push |commit| to this ref on the remote, and update the local copy of the
264 ref to |commit|."""
265 self.repo.run('push', 'origin', '%s:%s' % (commit.hsh, self.ref))
266 self.update_to(commit)
267
268 def update_to(self, commit):
269 """Update the local copy of the ref to |commit|."""
270 self.repo.run('update-ref', self.ref, commit.hsh)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698