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

Side by Side Diff: dashboard/dashboard/pinpoint/models/change/commit.py

Issue 3013013002: [pinpoint] Change refactor. (Closed)
Patch Set: UI Created 3 years, 3 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
(Empty)
1 # Copyright 2016 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 import collections
6
7 from dashboard.common import namespaced_stored_object
8 from dashboard.services import gitiles_service
9
10
11 _REPOSITORIES_KEY = 'repositories'
12
13
14 class NonLinearError(Exception):
15 """Raised when trying to find the midpoint of Changes that are not linear."""
16
17
18 class Commit(collections.namedtuple('Commit', ('repository', 'git_hash'))):
19 """A git repository pinned to a particular commit."""
20
21 def __str__(self):
22 """Returns an informal short string representation of this Commit."""
23 return self.repository + '@' + self.git_hash[:7]
24
25 @property
26 def id_string(self):
27 """Returns a string that is unique to this repository and git hash."""
28 return self.repository + '@' + self.git_hash
29
30 @property
31 def repository_url(self):
32 """The HTTPS URL of the repository as passed to `git clone`."""
33 repositories = namespaced_stored_object.Get(_REPOSITORIES_KEY)
34 return repositories[self.repository]['repository_url']
35
36 def Deps(self):
37 """Return the DEPS of this Commit as a frozenset of Commits."""
38 # Download and execute DEPS file.
39 deps_file_contents = gitiles_service.FileContents(
40 self.repository_url, self.git_hash, 'DEPS')
41 deps_data = {'Var': lambda variable: deps_data['vars'][variable]}
42 exec deps_file_contents in deps_data # pylint: disable=exec-used
43
44 # Pull out deps dict, including OS-specific deps.
45 deps_dict = deps_data['deps']
46 for deps_os in deps_data.get('deps_os', {}).itervalues():
47 deps_dict.update(deps_os)
48
49 # Convert deps strings to Commit objects.
50 commits = []
51 for dep_string in deps_dict.itervalues():
52 dep_string_parts = dep_string.split('@')
53 if len(dep_string_parts) < 2:
54 continue # Dep is not pinned to any particular revision.
55 if len(dep_string_parts) > 2:
56 raise NotImplementedError('Unknown DEP format: ' + dep_string)
57
58 repository_url, git_hash = dep_string_parts
59 try:
60 repository = _Repository(repository_url)
61 except KeyError:
62 repository = _AddRepository(repository_url)
63 commits.append(Commit(repository, git_hash))
64
65 return frozenset(commits)
66
67 def AsDict(self):
68 return {
69 'repository': self.repository,
70 'git_hash': self.git_hash,
71 'url': self.repository_url + '/+/' + self.git_hash,
72 }
73
74 @classmethod
75 def FromDict(cls, data):
76 """Create a Commit from a dict.
77
78 If the repository is a repository URL, it will be translated to its short
79 form name.
80
81 Raises:
82 KeyError: The repository name is not in the local datastore,
83 or the git hash is not valid.
84 """
85 repository = data['repository']
86
87 # Translate repository if it's a URL.
88 if repository.startswith('https://'):
89 repository = _Repository(repository)
90
91 commit = cls(repository, data['git_hash'])
92
93 try:
94 gitiles_service.CommitInfo(commit.repository_url, commit.git_hash)
95 except gitiles_service.NotFoundError as e:
96 raise KeyError(str(e))
97
98 return commit
99
100 @classmethod
101 def Midpoint(cls, commit_a, commit_b):
102 """Return a Commit halfway between the two given Commits.
103
104 Uses Gitiles to look up the commit range.
105
106 Args:
107 commit_a: The first Commit in the range.
108 commit_b: The last Commit in the range.
109
110 Returns:
111 A new Commit representing the midpoint.
112 The commit before the midpoint if the range has an even number of commits.
113 commit_a if the Commits are the same or adjacent.
114
115 Raises:
116 NonLinearError: The Commits are in different repositories or commit_a does
117 not come before commit_b.
118 """
119 if commit_a == commit_b:
120 return commit_a
121
122 if commit_a.repository != commit_b.repository:
123 raise NonLinearError('Repositories differ between Commits: %s vs %s' %
124 (commit_a.repository, commit_b.repository))
125
126 commits = gitiles_service.CommitRange(commit_a.repository_url,
127 commit_a.git_hash, commit_b.git_hash)
128 # We don't handle NotFoundErrors because we assume that all Commits either
129 # came from this method or were already validated elsewhere.
130 if len(commits) == 0:
131 raise NonLinearError('Commit "%s" does not come before commit "%s".' %
132 commit_a, commit_b)
133 if len(commits) == 1:
134 return commit_a
135 commits.pop(0) # Remove commit_b from the range.
136
137 return cls(commit_a.repository, commits[len(commits) / 2]['commit'])
138
139
140 def _Repository(repository_url):
141 if repository_url.endswith('.git'):
142 repository_url = repository_url[:-4]
143
144 repositories = namespaced_stored_object.Get(_REPOSITORIES_KEY)
145 for repo_label, repo_info in repositories.iteritems():
146 if repository_url == repo_info['repository_url']:
147 return repo_label
148
149 raise KeyError('Unknown repository URL: ' + repository_url)
150
151
152 def _AddRepository(repository_url):
153 if repository_url.endswith('.git'):
154 repository_url = repository_url[:-4]
155
156 repositories = namespaced_stored_object.Get(_REPOSITORIES_KEY)
157 repository = repository_url.split('/')[-1]
158
159 if repository in repositories:
160 raise AssertionError("Attempted to add a repository that's already in the "
161 'Datastore: %s: %s' % (repository, repository_url))
162
163 repositories[repository] = {'repository_url': repository_url}
164 namespaced_stored_object.Set(_REPOSITORIES_KEY, repositories)
165
166 return repository
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698