James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 1 | # Copyright 2012 Hewlett-Packard Development Company, L.P. |
James E. Blair | 4076e2b | 2014-01-28 12:42:20 -0800 | [diff] [blame] | 2 | # Copyright 2013-2014 OpenStack Foundation |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 3 | # |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may |
| 5 | # not use this file except in compliance with the License. You may obtain |
| 6 | # a copy of the License at |
| 7 | # |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | # |
| 10 | # Unless required by applicable law or agreed to in writing, software |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 13 | # License for the specific language governing permissions and limitations |
| 14 | # under the License. |
| 15 | |
James E. Blair | ba1c8c0 | 2017-10-04 08:47:48 -0700 | [diff] [blame] | 16 | from contextlib import contextmanager |
| 17 | import logging |
| 18 | import os |
| 19 | import shutil |
| 20 | |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 21 | import git |
James E. Blair | 1960d68 | 2017-04-28 15:44:14 -0700 | [diff] [blame] | 22 | import gitdb |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 23 | |
| 24 | import zuul.model |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 25 | |
James E. Blair | 289f593 | 2017-07-27 15:02:29 -0700 | [diff] [blame] | 26 | NULL_REF = '0000000000000000000000000000000000000000' |
| 27 | |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 28 | |
James E. Blair | 879dafb | 2015-07-17 14:04:49 -0700 | [diff] [blame] | 29 | def reset_repo_to_head(repo): |
| 30 | # This lets us reset the repo even if there is a file in the root |
| 31 | # directory named 'HEAD'. Currently, GitPython does not allow us |
| 32 | # to instruct it to always include the '--' to disambiguate. This |
| 33 | # should no longer be necessary if this PR merges: |
| 34 | # https://github.com/gitpython-developers/GitPython/pull/319 |
| 35 | try: |
| 36 | repo.git.reset('--hard', 'HEAD', '--') |
| 37 | except git.GitCommandError as e: |
| 38 | # git nowadays may use 1 as status to indicate there are still unstaged |
| 39 | # modifications after the reset |
| 40 | if e.status != 1: |
| 41 | raise |
| 42 | |
| 43 | |
James E. Blair | ba1c8c0 | 2017-10-04 08:47:48 -0700 | [diff] [blame] | 44 | @contextmanager |
| 45 | def timeout_handler(path): |
| 46 | try: |
| 47 | yield |
| 48 | except git.exc.GitCommandError as e: |
| 49 | if e.status == -9: |
| 50 | # Timeout. The repo could be in a bad state, so delete it. |
| 51 | shutil.rmtree(path) |
| 52 | raise |
| 53 | |
| 54 | |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 55 | class ZuulReference(git.Reference): |
| 56 | _common_path_default = "refs/zuul" |
| 57 | _points_to_commits_only = True |
| 58 | |
| 59 | |
| 60 | class Repo(object): |
Paul Belanger | edadfed | 2017-10-05 16:04:27 -0400 | [diff] [blame] | 61 | def __init__(self, remote, local, email, username, speed_limit, speed_time, |
James E. Blair | ba1c8c0 | 2017-10-04 08:47:48 -0700 | [diff] [blame] | 62 | sshkey=None, cache_path=None, logger=None, git_timeout=300): |
James E. Blair | da182de | 2017-05-26 14:24:56 -0700 | [diff] [blame] | 63 | if logger is None: |
| 64 | self.log = logging.getLogger("zuul.Repo") |
| 65 | else: |
| 66 | self.log = logger |
Paul Belanger | 06ab26d | 2017-10-05 14:50:31 -0400 | [diff] [blame] | 67 | self.env = { |
Paul Belanger | edadfed | 2017-10-05 16:04:27 -0400 | [diff] [blame] | 68 | 'GIT_HTTP_LOW_SPEED_LIMIT': speed_limit, |
| 69 | 'GIT_HTTP_LOW_SPEED_TIME': speed_time, |
Paul Belanger | 06ab26d | 2017-10-05 14:50:31 -0400 | [diff] [blame] | 70 | } |
James E. Blair | ba1c8c0 | 2017-10-04 08:47:48 -0700 | [diff] [blame] | 71 | self.git_timeout = git_timeout |
James E. Blair | 197e820 | 2017-06-09 12:54:28 -0700 | [diff] [blame] | 72 | if sshkey: |
Paul Belanger | 06ab26d | 2017-10-05 14:50:31 -0400 | [diff] [blame] | 73 | self.env['GIT_SSH_COMMAND'] = 'ssh -i %s' % (sshkey,) |
| 74 | |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 75 | self.remote_url = remote |
| 76 | self.local_path = local |
James E. Blair | 287c06d | 2013-07-24 10:39:30 -0700 | [diff] [blame] | 77 | self.email = email |
| 78 | self.username = username |
James E. Blair | f327c57 | 2017-05-24 13:58:42 -0700 | [diff] [blame] | 79 | self.cache_path = cache_path |
James E. Blair | 287c06d | 2013-07-24 10:39:30 -0700 | [diff] [blame] | 80 | self._initialized = False |
| 81 | try: |
| 82 | self._ensure_cloned() |
James E. Blair | ba1c8c0 | 2017-10-04 08:47:48 -0700 | [diff] [blame] | 83 | except Exception: |
James E. Blair | 287c06d | 2013-07-24 10:39:30 -0700 | [diff] [blame] | 84 | self.log.exception("Unable to initialize repo for %s" % remote) |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 85 | |
| 86 | def _ensure_cloned(self): |
Antoine Musso | bfd8f2a | 2014-09-23 15:31:40 +0200 | [diff] [blame] | 87 | repo_is_cloned = os.path.exists(os.path.join(self.local_path, '.git')) |
Clark Boylan | 6dbbc48 | 2013-10-18 10:57:31 -0700 | [diff] [blame] | 88 | if self._initialized and repo_is_cloned: |
James E. Blair | 287c06d | 2013-07-24 10:39:30 -0700 | [diff] [blame] | 89 | return |
Clark Boylan | 6dbbc48 | 2013-10-18 10:57:31 -0700 | [diff] [blame] | 90 | # If the repo does not exist, clone the repo. |
James E. Blair | f327c57 | 2017-05-24 13:58:42 -0700 | [diff] [blame] | 91 | rewrite_url = False |
Clark Boylan | 6dbbc48 | 2013-10-18 10:57:31 -0700 | [diff] [blame] | 92 | if not repo_is_cloned: |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 93 | self.log.debug("Cloning from %s to %s" % (self.remote_url, |
| 94 | self.local_path)) |
James E. Blair | f327c57 | 2017-05-24 13:58:42 -0700 | [diff] [blame] | 95 | if self.cache_path: |
James E. Blair | ba1c8c0 | 2017-10-04 08:47:48 -0700 | [diff] [blame] | 96 | self._git_clone(self.cache_path) |
James E. Blair | f327c57 | 2017-05-24 13:58:42 -0700 | [diff] [blame] | 97 | rewrite_url = True |
| 98 | else: |
James E. Blair | ba1c8c0 | 2017-10-04 08:47:48 -0700 | [diff] [blame] | 99 | self._git_clone(self.remote_url) |
Clark Boylan | 4ba48d9 | 2013-11-11 18:03:53 -0800 | [diff] [blame] | 100 | repo = git.Repo(self.local_path) |
James E. Blair | 197e820 | 2017-06-09 12:54:28 -0700 | [diff] [blame] | 101 | repo.git.update_environment(**self.env) |
James E. Blair | f327c57 | 2017-05-24 13:58:42 -0700 | [diff] [blame] | 102 | # Create local branches corresponding to all the remote branches |
| 103 | if not repo_is_cloned: |
| 104 | origin = repo.remotes.origin |
| 105 | for ref in origin.refs: |
| 106 | if ref.remote_head == 'HEAD': |
| 107 | continue |
| 108 | repo.create_head(ref.remote_head, ref, force=True) |
Clint Byrum | deaf1fc | 2017-05-10 21:28:31 -0700 | [diff] [blame] | 109 | with repo.config_writer() as config_writer: |
| 110 | if self.email: |
| 111 | config_writer.set_value('user', 'email', self.email) |
| 112 | if self.username: |
| 113 | config_writer.set_value('user', 'name', self.username) |
| 114 | config_writer.write() |
James E. Blair | f327c57 | 2017-05-24 13:58:42 -0700 | [diff] [blame] | 115 | if rewrite_url: |
| 116 | with repo.remotes.origin.config_writer as config_writer: |
| 117 | config_writer.set('url', self.remote_url) |
James E. Blair | 287c06d | 2013-07-24 10:39:30 -0700 | [diff] [blame] | 118 | self._initialized = True |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 119 | |
Antoine Musso | a6529c6 | 2014-06-03 14:55:27 +0200 | [diff] [blame] | 120 | def isInitialized(self): |
| 121 | return self._initialized |
| 122 | |
James E. Blair | ba1c8c0 | 2017-10-04 08:47:48 -0700 | [diff] [blame] | 123 | def _git_clone(self, url): |
| 124 | mygit = git.cmd.Git(os.getcwd()) |
| 125 | mygit.update_environment(**self.env) |
| 126 | with timeout_handler(self.local_path): |
| 127 | mygit.clone(git.cmd.Git.polish_url(url), self.local_path, |
| 128 | kill_after_timeout=self.git_timeout) |
| 129 | |
| 130 | def _git_fetch(self, repo, remote, ref=None, **kwargs): |
| 131 | with timeout_handler(self.local_path): |
| 132 | repo.git.fetch(remote, ref, kill_after_timeout=self.git_timeout, |
| 133 | **kwargs) |
| 134 | |
Clark Boylan | 4ba48d9 | 2013-11-11 18:03:53 -0800 | [diff] [blame] | 135 | def createRepoObject(self): |
James E. Blair | 96c6bf8 | 2016-01-15 16:20:40 -0800 | [diff] [blame] | 136 | self._ensure_cloned() |
| 137 | repo = git.Repo(self.local_path) |
James E. Blair | 197e820 | 2017-06-09 12:54:28 -0700 | [diff] [blame] | 138 | repo.git.update_environment(**self.env) |
Clark Boylan | 4ba48d9 | 2013-11-11 18:03:53 -0800 | [diff] [blame] | 139 | return repo |
Clark Boylan | c259232 | 2013-02-20 17:12:28 -0800 | [diff] [blame] | 140 | |
James E. Blair | b34e926 | 2013-08-27 17:12:31 -0700 | [diff] [blame] | 141 | def reset(self): |
James E. Blair | b34e926 | 2013-08-27 17:12:31 -0700 | [diff] [blame] | 142 | self.log.debug("Resetting repository %s" % self.local_path) |
| 143 | self.update() |
James E. Blair | fbdcdaa | 2015-07-20 15:56:37 -0700 | [diff] [blame] | 144 | repo = self.createRepoObject() |
Clark Boylan | 4ba48d9 | 2013-11-11 18:03:53 -0800 | [diff] [blame] | 145 | origin = repo.remotes.origin |
James E. Blair | b34e926 | 2013-08-27 17:12:31 -0700 | [diff] [blame] | 146 | for ref in origin.refs: |
| 147 | if ref.remote_head == 'HEAD': |
| 148 | continue |
Clark Boylan | 4ba48d9 | 2013-11-11 18:03:53 -0800 | [diff] [blame] | 149 | repo.create_head(ref.remote_head, ref, force=True) |
James E. Blair | b34e926 | 2013-08-27 17:12:31 -0700 | [diff] [blame] | 150 | |
Arie | b8a7792 | 2016-08-29 14:53:25 +0300 | [diff] [blame] | 151 | # try reset to remote HEAD (usually origin/master) |
| 152 | # If it fails, pick the first reference |
| 153 | try: |
| 154 | repo.head.reference = origin.refs['HEAD'] |
| 155 | except IndexError: |
| 156 | repo.head.reference = origin.refs[0] |
James E. Blair | 879dafb | 2015-07-17 14:04:49 -0700 | [diff] [blame] | 157 | reset_repo_to_head(repo) |
Clark Boylan | 4ba48d9 | 2013-11-11 18:03:53 -0800 | [diff] [blame] | 158 | repo.git.clean('-x', '-f', '-d') |
James E. Blair | b34e926 | 2013-08-27 17:12:31 -0700 | [diff] [blame] | 159 | |
Antoine Musso | 965233a | 2014-05-05 17:28:27 +0200 | [diff] [blame] | 160 | def prune(self): |
| 161 | repo = self.createRepoObject() |
| 162 | origin = repo.remotes.origin |
| 163 | stale_refs = origin.stale_refs |
| 164 | if stale_refs: |
| 165 | self.log.debug("Pruning stale refs: %s", stale_refs) |
| 166 | git.refs.RemoteReference.delete(repo, *stale_refs) |
| 167 | |
James E. Blair | b34e926 | 2013-08-27 17:12:31 -0700 | [diff] [blame] | 168 | def getBranchHead(self, branch): |
Clark Boylan | 4ba48d9 | 2013-11-11 18:03:53 -0800 | [diff] [blame] | 169 | repo = self.createRepoObject() |
| 170 | branch_head = repo.heads[branch] |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 171 | return branch_head.commit |
| 172 | |
Antoine Musso | d3fe17f | 2014-05-05 18:19:36 +0200 | [diff] [blame] | 173 | def hasBranch(self, branch): |
| 174 | repo = self.createRepoObject() |
| 175 | origin = repo.remotes.origin |
| 176 | return branch in origin.refs |
| 177 | |
James E. Blair | 2ec590b | 2017-05-24 13:59:17 -0700 | [diff] [blame] | 178 | def getBranches(self): |
James E. Blair | edff2c2 | 2017-10-30 14:04:48 -0700 | [diff] [blame] | 179 | # TODO(jeblair): deprecate with override-branch; replaced by |
| 180 | # getRefs(). |
James E. Blair | 2ec590b | 2017-05-24 13:59:17 -0700 | [diff] [blame] | 181 | repo = self.createRepoObject() |
| 182 | return [x.name for x in repo.heads] |
| 183 | |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 184 | def getCommitFromRef(self, refname): |
| 185 | repo = self.createRepoObject() |
Joshua Hesketh | 29d99b7 | 2014-08-19 16:27:42 +1000 | [diff] [blame] | 186 | if refname not in repo.refs: |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 187 | return None |
| 188 | ref = repo.refs[refname] |
| 189 | return ref.commit |
James E. Blair | b34e926 | 2013-08-27 17:12:31 -0700 | [diff] [blame] | 190 | |
James E. Blair | 34c7daa | 2017-04-28 13:31:27 -0700 | [diff] [blame] | 191 | def getRefs(self): |
| 192 | repo = self.createRepoObject() |
| 193 | return repo.refs |
| 194 | |
James E. Blair | 57c51a1 | 2017-05-24 13:53:35 -0700 | [diff] [blame] | 195 | def setRef(self, path, hexsha, repo=None): |
| 196 | if repo is None: |
| 197 | repo = self.createRepoObject() |
| 198 | binsha = gitdb.util.to_bin_sha(hexsha) |
| 199 | obj = git.objects.Object.new_from_sha(repo, binsha) |
| 200 | self.log.debug("Create reference %s", path) |
| 201 | git.refs.Reference.create(repo, path, obj, force=True) |
| 202 | |
James E. Blair | 1960d68 | 2017-04-28 15:44:14 -0700 | [diff] [blame] | 203 | def setRefs(self, refs): |
| 204 | repo = self.createRepoObject() |
| 205 | current_refs = {} |
| 206 | for ref in repo.refs: |
| 207 | current_refs[ref.path] = ref |
| 208 | unseen = set(current_refs.keys()) |
| 209 | for path, hexsha in refs.items(): |
James E. Blair | 57c51a1 | 2017-05-24 13:53:35 -0700 | [diff] [blame] | 210 | self.setRef(path, hexsha, repo) |
James E. Blair | 1960d68 | 2017-04-28 15:44:14 -0700 | [diff] [blame] | 211 | unseen.discard(path) |
| 212 | for path in unseen: |
James E. Blair | 289f593 | 2017-07-27 15:02:29 -0700 | [diff] [blame] | 213 | self.deleteRef(path, repo) |
| 214 | |
| 215 | def deleteRef(self, path, repo=None): |
| 216 | if repo is None: |
| 217 | repo = self.createRepoObject() |
| 218 | self.log.debug("Delete reference %s", path) |
| 219 | git.refs.SymbolicReference.delete(repo, path) |
James E. Blair | 1960d68 | 2017-04-28 15:44:14 -0700 | [diff] [blame] | 220 | |
Clark Boylan | c259232 | 2013-02-20 17:12:28 -0800 | [diff] [blame] | 221 | def checkout(self, ref): |
Clark Boylan | 4ba48d9 | 2013-11-11 18:03:53 -0800 | [diff] [blame] | 222 | repo = self.createRepoObject() |
Clark Boylan | c259232 | 2013-02-20 17:12:28 -0800 | [diff] [blame] | 223 | self.log.debug("Checking out %s" % ref) |
James E. Blair | f49b525 | 2017-09-08 16:04:52 -0700 | [diff] [blame] | 224 | # Perform a hard reset before checking out so that we clean up |
| 225 | # anything that might be left over from a merge. |
James E. Blair | 879dafb | 2015-07-17 14:04:49 -0700 | [diff] [blame] | 226 | reset_repo_to_head(repo) |
James E. Blair | f49b525 | 2017-09-08 16:04:52 -0700 | [diff] [blame] | 227 | repo.git.checkout(ref) |
James E. Blair | f5f1a61 | 2015-06-24 09:34:03 -0700 | [diff] [blame] | 228 | return repo.head.commit |
James E. Blair | c6294a5 | 2012-08-17 10:19:48 -0700 | [diff] [blame] | 229 | |
James E. Blair | f327c57 | 2017-05-24 13:58:42 -0700 | [diff] [blame] | 230 | def checkoutLocalBranch(self, branch): |
James E. Blair | f49b525 | 2017-09-08 16:04:52 -0700 | [diff] [blame] | 231 | # TODO(jeblair): retire in favor of checkout |
James E. Blair | f327c57 | 2017-05-24 13:58:42 -0700 | [diff] [blame] | 232 | repo = self.createRepoObject() |
James E. Blair | e5921fd | 2017-05-26 14:29:13 -0700 | [diff] [blame] | 233 | # Perform a hard reset before checking out so that we clean up |
| 234 | # anything that might be left over from a merge. |
| 235 | reset_repo_to_head(repo) |
| 236 | repo.heads[branch].checkout() |
James E. Blair | f327c57 | 2017-05-24 13:58:42 -0700 | [diff] [blame] | 237 | |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 238 | def cherryPick(self, ref): |
Clark Boylan | 4ba48d9 | 2013-11-11 18:03:53 -0800 | [diff] [blame] | 239 | repo = self.createRepoObject() |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 240 | self.log.debug("Cherry-picking %s" % ref) |
Clark Boylan | 14b5537 | 2012-11-01 11:57:50 -0700 | [diff] [blame] | 241 | self.fetch(ref) |
Clark Boylan | 4ba48d9 | 2013-11-11 18:03:53 -0800 | [diff] [blame] | 242 | repo.git.cherry_pick("FETCH_HEAD") |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 243 | return repo.head.commit |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 244 | |
James E. Blair | 19deff2 | 2013-08-25 13:17:35 -0700 | [diff] [blame] | 245 | def merge(self, ref, strategy=None): |
Clark Boylan | 4ba48d9 | 2013-11-11 18:03:53 -0800 | [diff] [blame] | 246 | repo = self.createRepoObject() |
James E. Blair | 19deff2 | 2013-08-25 13:17:35 -0700 | [diff] [blame] | 247 | args = [] |
| 248 | if strategy: |
| 249 | args += ['-s', strategy] |
| 250 | args.append('FETCH_HEAD') |
Clark Boylan | 14b5537 | 2012-11-01 11:57:50 -0700 | [diff] [blame] | 251 | self.fetch(ref) |
James E. Blair | 19deff2 | 2013-08-25 13:17:35 -0700 | [diff] [blame] | 252 | self.log.debug("Merging %s with args %s" % (ref, args)) |
Clark Boylan | 4ba48d9 | 2013-11-11 18:03:53 -0800 | [diff] [blame] | 253 | repo.git.merge(*args) |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 254 | return repo.head.commit |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 255 | |
Clark Boylan | 14b5537 | 2012-11-01 11:57:50 -0700 | [diff] [blame] | 256 | def fetch(self, ref): |
Clark Boylan | 4ba48d9 | 2013-11-11 18:03:53 -0800 | [diff] [blame] | 257 | repo = self.createRepoObject() |
James E. Blair | ba1c8c0 | 2017-10-04 08:47:48 -0700 | [diff] [blame] | 258 | # NOTE: The following is currently not applicable, but if we |
| 259 | # switch back to fetch methods from GitPython, we need to |
| 260 | # consider it: |
| 261 | # The git.remote.fetch method may read in git progress info and |
| 262 | # interpret it improperly causing an AssertionError. Because the |
| 263 | # data was fetched properly subsequent fetches don't seem to fail. |
| 264 | # So try again if an AssertionError is caught. |
| 265 | self._git_fetch(repo, 'origin', ref) |
Clark Boylan | 14b5537 | 2012-11-01 11:57:50 -0700 | [diff] [blame] | 266 | |
James E. Blair | 247cab7 | 2017-07-20 16:52:36 -0700 | [diff] [blame] | 267 | def fetchFrom(self, repository, ref): |
Antoine Musso | 45dd2cb | 2014-01-29 17:17:43 +0100 | [diff] [blame] | 268 | repo = self.createRepoObject() |
James E. Blair | ba1c8c0 | 2017-10-04 08:47:48 -0700 | [diff] [blame] | 269 | self._git_fetch(repo, repository, ref) |
Antoine Musso | 45dd2cb | 2014-01-29 17:17:43 +0100 | [diff] [blame] | 270 | |
Clark Boylan | c259232 | 2013-02-20 17:12:28 -0800 | [diff] [blame] | 271 | def createZuulRef(self, ref, commit='HEAD'): |
Clark Boylan | 4ba48d9 | 2013-11-11 18:03:53 -0800 | [diff] [blame] | 272 | repo = self.createRepoObject() |
Spencer Krum | 438ec53 | 2015-08-06 13:56:46 -0700 | [diff] [blame] | 273 | self.log.debug("CreateZuulRef %s at %s on %s" % (ref, commit, repo)) |
Clark Boylan | 4ba48d9 | 2013-11-11 18:03:53 -0800 | [diff] [blame] | 274 | ref = ZuulReference.create(repo, ref, commit) |
Clark Boylan | c259232 | 2013-02-20 17:12:28 -0800 | [diff] [blame] | 275 | return ref.commit |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 276 | |
James E. Blair | daabed2 | 2012-08-15 15:38:57 -0700 | [diff] [blame] | 277 | def push(self, local, remote): |
Clark Boylan | 4ba48d9 | 2013-11-11 18:03:53 -0800 | [diff] [blame] | 278 | repo = self.createRepoObject() |
Antoine Musso | f0506fa | 2014-06-03 15:03:38 +0200 | [diff] [blame] | 279 | self.log.debug("Pushing %s:%s to %s" % (local, remote, |
| 280 | self.remote_url)) |
Clark Boylan | 4ba48d9 | 2013-11-11 18:03:53 -0800 | [diff] [blame] | 281 | repo.remotes.origin.push('%s:%s' % (local, remote)) |
James E. Blair | daabed2 | 2012-08-15 15:38:57 -0700 | [diff] [blame] | 282 | |
Antoine Musso | d71e297 | 2013-01-17 13:40:10 +0100 | [diff] [blame] | 283 | def update(self): |
Clark Boylan | 4ba48d9 | 2013-11-11 18:03:53 -0800 | [diff] [blame] | 284 | repo = self.createRepoObject() |
Antoine Musso | d71e297 | 2013-01-17 13:40:10 +0100 | [diff] [blame] | 285 | self.log.debug("Updating repository %s" % self.local_path) |
James E. Blair | 84c4f94 | 2016-07-29 10:38:29 -0700 | [diff] [blame] | 286 | if repo.git.version_info[:2] < (1, 9): |
| 287 | # Before 1.9, 'git fetch --tags' did not include the |
| 288 | # behavior covered by 'git --fetch', so we run both |
| 289 | # commands in that case. Starting with 1.9, 'git fetch |
| 290 | # --tags' is all that is necessary. See |
| 291 | # https://github.com/git/git/blob/master/Documentation/RelNotes/1.9.0.txt#L18-L20 |
James E. Blair | ba1c8c0 | 2017-10-04 08:47:48 -0700 | [diff] [blame] | 292 | self._git_fetch(repo, 'origin') |
| 293 | self._git_fetch(repo, 'origin', tags=True) |
Antoine Musso | d71e297 | 2013-01-17 13:40:10 +0100 | [diff] [blame] | 294 | |
Tristan Cacqueray | 829e617 | 2017-06-13 06:49:36 +0000 | [diff] [blame] | 295 | def getFiles(self, files, dirs=[], branch=None, commit=None): |
James E. Blair | 14abdf4 | 2015-12-09 16:11:53 -0800 | [diff] [blame] | 296 | ret = {} |
| 297 | repo = self.createRepoObject() |
James E. Blair | 8b1dc3f | 2016-07-05 16:49:00 -0700 | [diff] [blame] | 298 | if branch: |
James E. Blair | 14abdf4 | 2015-12-09 16:11:53 -0800 | [diff] [blame] | 299 | tree = repo.heads[branch].commit.tree |
James E. Blair | 8b1dc3f | 2016-07-05 16:49:00 -0700 | [diff] [blame] | 300 | else: |
| 301 | tree = repo.commit(commit).tree |
| 302 | for fn in files: |
James E. Blair | 14abdf4 | 2015-12-09 16:11:53 -0800 | [diff] [blame] | 303 | if fn in tree: |
Clint Byrum | f322fe2 | 2017-05-10 20:53:12 -0700 | [diff] [blame] | 304 | ret[fn] = tree[fn].data_stream.read().decode('utf8') |
James E. Blair | 14abdf4 | 2015-12-09 16:11:53 -0800 | [diff] [blame] | 305 | else: |
| 306 | ret[fn] = None |
Tristan Cacqueray | 829e617 | 2017-06-13 06:49:36 +0000 | [diff] [blame] | 307 | if dirs: |
| 308 | for dn in dirs: |
| 309 | if dn not in tree: |
| 310 | continue |
| 311 | for blob in tree[dn].traverse(): |
| 312 | if blob.path.endswith(".yaml"): |
| 313 | ret[blob.path] = blob.data_stream.read().decode( |
| 314 | 'utf-8') |
James E. Blair | 14abdf4 | 2015-12-09 16:11:53 -0800 | [diff] [blame] | 315 | return ret |
| 316 | |
Fabien Boucher | 194a2bf | 2017-12-02 18:17:58 +0100 | [diff] [blame] | 317 | def getFilesChanges(self, branch, tosha=None): |
| 318 | repo = self.createRepoObject() |
| 319 | files = set() |
| 320 | head = repo.heads[branch].commit |
| 321 | files.update(set(head.stats.files.keys())) |
| 322 | if tosha: |
| 323 | for cmt in head.iter_parents(): |
| 324 | if cmt.hexsha == tosha: |
| 325 | break |
| 326 | files.update(set(cmt.stats.files.keys())) |
| 327 | return list(files) |
| 328 | |
James E. Blair | f327c57 | 2017-05-24 13:58:42 -0700 | [diff] [blame] | 329 | def deleteRemote(self, remote): |
| 330 | repo = self.createRepoObject() |
| 331 | repo.delete_remote(repo.remotes[remote]) |
| 332 | |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 333 | |
| 334 | class Merger(object): |
James E. Blair | f327c57 | 2017-05-24 13:58:42 -0700 | [diff] [blame] | 335 | def __init__(self, working_root, connections, email, username, |
Paul Belanger | edadfed | 2017-10-05 16:04:27 -0400 | [diff] [blame] | 336 | speed_limit, speed_time, cache_root=None, logger=None): |
James E. Blair | da182de | 2017-05-26 14:24:56 -0700 | [diff] [blame] | 337 | self.logger = logger |
| 338 | if logger is None: |
| 339 | self.log = logging.getLogger("zuul.Merger") |
| 340 | else: |
| 341 | self.log = logger |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 342 | self.repos = {} |
| 343 | self.working_root = working_root |
| 344 | if not os.path.exists(working_root): |
| 345 | os.makedirs(working_root) |
Joshua Hesketh | ca5d0ca | 2017-02-21 13:49:22 -0500 | [diff] [blame] | 346 | self.connections = connections |
Paul Belanger | b67aba1 | 2013-05-13 19:22:14 -0400 | [diff] [blame] | 347 | self.email = email |
| 348 | self.username = username |
Paul Belanger | edadfed | 2017-10-05 16:04:27 -0400 | [diff] [blame] | 349 | self.speed_limit = speed_limit |
| 350 | self.speed_time = speed_time |
James E. Blair | f327c57 | 2017-05-24 13:58:42 -0700 | [diff] [blame] | 351 | self.cache_root = cache_root |
James E. Blair | ad61501 | 2012-11-30 16:14:21 -0800 | [diff] [blame] | 352 | |
James E. Blair | 197e820 | 2017-06-09 12:54:28 -0700 | [diff] [blame] | 353 | def _addProject(self, hostname, project_name, url, sshkey): |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 354 | repo = None |
James E. Blair | 2a53567 | 2017-04-27 12:03:15 -0700 | [diff] [blame] | 355 | key = '/'.join([hostname, project_name]) |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 356 | try: |
James E. Blair | 2a53567 | 2017-04-27 12:03:15 -0700 | [diff] [blame] | 357 | path = os.path.join(self.working_root, hostname, project_name) |
James E. Blair | f327c57 | 2017-05-24 13:58:42 -0700 | [diff] [blame] | 358 | if self.cache_root: |
| 359 | cache_path = os.path.join(self.cache_root, hostname, |
| 360 | project_name) |
| 361 | else: |
| 362 | cache_path = None |
Paul Belanger | edadfed | 2017-10-05 16:04:27 -0400 | [diff] [blame] | 363 | repo = Repo( |
| 364 | url, path, self.email, self.username, self.speed_limit, |
| 365 | self.speed_time, sshkey, cache_path, self.logger) |
Paul Belanger | b67aba1 | 2013-05-13 19:22:14 -0400 | [diff] [blame] | 366 | |
James E. Blair | 2a53567 | 2017-04-27 12:03:15 -0700 | [diff] [blame] | 367 | self.repos[key] = repo |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 368 | except Exception: |
James E. Blair | 2a53567 | 2017-04-27 12:03:15 -0700 | [diff] [blame] | 369 | self.log.exception("Unable to add project %s/%s" % |
| 370 | (hostname, project_name)) |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 371 | return repo |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 372 | |
James E. Blair | 2a53567 | 2017-04-27 12:03:15 -0700 | [diff] [blame] | 373 | def getRepo(self, connection_name, project_name): |
| 374 | source = self.connections.getSource(connection_name) |
| 375 | project = source.getProject(project_name) |
| 376 | hostname = project.canonical_hostname |
| 377 | url = source.getGitUrl(project) |
| 378 | key = '/'.join([hostname, project_name]) |
| 379 | if key in self.repos: |
| 380 | return self.repos[key] |
James E. Blair | 197e820 | 2017-06-09 12:54:28 -0700 | [diff] [blame] | 381 | sshkey = self.connections.connections.get(connection_name).\ |
| 382 | connection_config.get('sshkey') |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 383 | if not url: |
James E. Blair | 2a53567 | 2017-04-27 12:03:15 -0700 | [diff] [blame] | 384 | raise Exception("Unable to set up repo for project %s/%s" |
| 385 | " without a url" % |
| 386 | (connection_name, project_name,)) |
James E. Blair | 197e820 | 2017-06-09 12:54:28 -0700 | [diff] [blame] | 387 | return self._addProject(hostname, project_name, url, sshkey) |
Clark Boylan | c259232 | 2013-02-20 17:12:28 -0800 | [diff] [blame] | 388 | |
James E. Blair | 2a53567 | 2017-04-27 12:03:15 -0700 | [diff] [blame] | 389 | def updateRepo(self, connection_name, project_name): |
James E. Blair | 2a53567 | 2017-04-27 12:03:15 -0700 | [diff] [blame] | 390 | repo = self.getRepo(connection_name, project_name) |
Antoine Musso | feba967 | 2013-01-17 13:44:59 +0100 | [diff] [blame] | 391 | try: |
James E. Blair | 2a53567 | 2017-04-27 12:03:15 -0700 | [diff] [blame] | 392 | self.log.info("Updating local repository %s/%s", |
| 393 | connection_name, project_name) |
James E. Blair | d8d55c7 | 2017-02-14 09:05:49 -0800 | [diff] [blame] | 394 | repo.reset() |
Clark Boylan | 4c6566b | 2014-03-10 11:02:01 -0700 | [diff] [blame] | 395 | except Exception: |
James E. Blair | 2a53567 | 2017-04-27 12:03:15 -0700 | [diff] [blame] | 396 | self.log.exception("Unable to update %s/%s", |
| 397 | connection_name, project_name) |
Antoine Musso | feba967 | 2013-01-17 13:44:59 +0100 | [diff] [blame] | 398 | |
James E. Blair | 2a53567 | 2017-04-27 12:03:15 -0700 | [diff] [blame] | 399 | def checkoutBranch(self, connection_name, project_name, branch): |
James E. Blair | f327c57 | 2017-05-24 13:58:42 -0700 | [diff] [blame] | 400 | self.log.info("Checking out %s/%s branch %s", |
| 401 | connection_name, project_name, branch) |
James E. Blair | 2a53567 | 2017-04-27 12:03:15 -0700 | [diff] [blame] | 402 | repo = self.getRepo(connection_name, project_name) |
James E. Blair | edff2c2 | 2017-10-30 14:04:48 -0700 | [diff] [blame] | 403 | repo.checkout(branch) |
James E. Blair | c73c73a | 2017-01-20 15:15:15 -0800 | [diff] [blame] | 404 | |
James E. Blair | 34c7daa | 2017-04-28 13:31:27 -0700 | [diff] [blame] | 405 | def _saveRepoState(self, connection_name, project_name, repo, |
James E. Blair | 66c6068 | 2017-05-31 12:48:01 -0400 | [diff] [blame] | 406 | repo_state, recent): |
James E. Blair | 34c7daa | 2017-04-28 13:31:27 -0700 | [diff] [blame] | 407 | projects = repo_state.setdefault(connection_name, {}) |
| 408 | project = projects.setdefault(project_name, {}) |
James E. Blair | 34c7daa | 2017-04-28 13:31:27 -0700 | [diff] [blame] | 409 | for ref in repo.getRefs(): |
James E. Blair | 66c6068 | 2017-05-31 12:48:01 -0400 | [diff] [blame] | 410 | if ref.path.startswith('refs/zuul/'): |
James E. Blair | 34c7daa | 2017-04-28 13:31:27 -0700 | [diff] [blame] | 411 | continue |
James E. Blair | 66c6068 | 2017-05-31 12:48:01 -0400 | [diff] [blame] | 412 | if ref.path.startswith('refs/remotes/'): |
James E. Blair | 1960d68 | 2017-04-28 15:44:14 -0700 | [diff] [blame] | 413 | continue |
James E. Blair | 66c6068 | 2017-05-31 12:48:01 -0400 | [diff] [blame] | 414 | if ref.path.startswith('refs/heads/'): |
| 415 | branch = ref.path[len('refs/heads/'):] |
| 416 | key = (connection_name, project_name, branch) |
| 417 | if key not in recent: |
| 418 | recent[key] = ref.object |
James E. Blair | 34c7daa | 2017-04-28 13:31:27 -0700 | [diff] [blame] | 419 | project[ref.path] = ref.object.hexsha |
| 420 | |
James E. Blair | 289f593 | 2017-07-27 15:02:29 -0700 | [diff] [blame] | 421 | def _alterRepoState(self, connection_name, project_name, |
| 422 | repo_state, path, hexsha): |
| 423 | projects = repo_state.setdefault(connection_name, {}) |
| 424 | project = projects.setdefault(project_name, {}) |
| 425 | if hexsha == NULL_REF: |
| 426 | if path in project: |
| 427 | del project[path] |
| 428 | else: |
| 429 | project[path] = hexsha |
| 430 | |
James E. Blair | 1960d68 | 2017-04-28 15:44:14 -0700 | [diff] [blame] | 431 | def _restoreRepoState(self, connection_name, project_name, repo, |
| 432 | repo_state): |
| 433 | projects = repo_state.get(connection_name, {}) |
| 434 | project = projects.get(project_name, {}) |
| 435 | if not project: |
| 436 | # We don't have a state for this project. |
| 437 | return |
| 438 | self.log.debug("Restore repo state for project %s/%s", |
| 439 | connection_name, project_name) |
| 440 | repo.setRefs(project) |
| 441 | |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 442 | def _mergeChange(self, item, ref): |
James E. Blair | 2a53567 | 2017-04-27 12:03:15 -0700 | [diff] [blame] | 443 | repo = self.getRepo(item['connection'], item['project']) |
Clark Boylan | c259232 | 2013-02-20 17:12:28 -0800 | [diff] [blame] | 444 | try: |
| 445 | repo.checkout(ref) |
James E. Blair | 4076e2b | 2014-01-28 12:42:20 -0800 | [diff] [blame] | 446 | except Exception: |
Clark Boylan | c259232 | 2013-02-20 17:12:28 -0800 | [diff] [blame] | 447 | self.log.exception("Unable to checkout %s" % ref) |
James E. Blair | 4076e2b | 2014-01-28 12:42:20 -0800 | [diff] [blame] | 448 | return None |
Clark Boylan | c259232 | 2013-02-20 17:12:28 -0800 | [diff] [blame] | 449 | |
| 450 | try: |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 451 | mode = item['merge_mode'] |
| 452 | if mode == zuul.model.MERGER_MERGE: |
James E. Blair | 247cab7 | 2017-07-20 16:52:36 -0700 | [diff] [blame] | 453 | commit = repo.merge(item['ref']) |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 454 | elif mode == zuul.model.MERGER_MERGE_RESOLVE: |
James E. Blair | 247cab7 | 2017-07-20 16:52:36 -0700 | [diff] [blame] | 455 | commit = repo.merge(item['ref'], 'resolve') |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 456 | elif mode == zuul.model.MERGER_CHERRY_PICK: |
James E. Blair | 247cab7 | 2017-07-20 16:52:36 -0700 | [diff] [blame] | 457 | commit = repo.cherryPick(item['ref']) |
James E. Blair | 19deff2 | 2013-08-25 13:17:35 -0700 | [diff] [blame] | 458 | else: |
| 459 | raise Exception("Unsupported merge mode: %s" % mode) |
Antoine Musso | 5f53e60 | 2014-02-04 14:16:18 +0100 | [diff] [blame] | 460 | except git.GitCommandError: |
| 461 | # Log git exceptions at debug level because they are |
Clark Boylan | c259232 | 2013-02-20 17:12:28 -0800 | [diff] [blame] | 462 | # usually benign merge conflicts |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 463 | self.log.debug("Unable to merge %s" % item, exc_info=True) |
James E. Blair | 4076e2b | 2014-01-28 12:42:20 -0800 | [diff] [blame] | 464 | return None |
Antoine Musso | 5f53e60 | 2014-02-04 14:16:18 +0100 | [diff] [blame] | 465 | except Exception: |
| 466 | self.log.exception("Exception while merging a change:") |
| 467 | return None |
Clark Boylan | c259232 | 2013-02-20 17:12:28 -0800 | [diff] [blame] | 468 | |
Clark Boylan | c259232 | 2013-02-20 17:12:28 -0800 | [diff] [blame] | 469 | return commit |
James E. Blair | 4886cc1 | 2012-07-18 15:39:41 -0700 | [diff] [blame] | 470 | |
James E. Blair | 34c7daa | 2017-04-28 13:31:27 -0700 | [diff] [blame] | 471 | def _mergeItem(self, item, recent, repo_state): |
James E. Blair | 247cab7 | 2017-07-20 16:52:36 -0700 | [diff] [blame] | 472 | self.log.debug("Processing ref %s for project %s/%s / %s uuid %s" % |
| 473 | (item['ref'], item['connection'], |
| 474 | item['project'], item['branch'], |
| 475 | item['buildset_uuid'])) |
James E. Blair | 2a53567 | 2017-04-27 12:03:15 -0700 | [diff] [blame] | 476 | repo = self.getRepo(item['connection'], item['project']) |
| 477 | key = (item['connection'], item['project'], item['branch']) |
Joshua Hesketh | ca5d0ca | 2017-02-21 13:49:22 -0500 | [diff] [blame] | 478 | |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 479 | # We need to merge the change |
| 480 | # Get the most recent commit for this project-branch |
| 481 | base = recent.get(key) |
| 482 | if not base: |
| 483 | # There is none, so use the branch tip |
James E. Blair | b34e926 | 2013-08-27 17:12:31 -0700 | [diff] [blame] | 484 | # we need to reset here in order to call getBranchHead |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 485 | self.log.debug("No base commit found for %s" % (key,)) |
James E. Blair | b34e926 | 2013-08-27 17:12:31 -0700 | [diff] [blame] | 486 | try: |
| 487 | repo.reset() |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 488 | except Exception: |
James E. Blair | b34e926 | 2013-08-27 17:12:31 -0700 | [diff] [blame] | 489 | self.log.exception("Unable to reset repo %s" % repo) |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 490 | return None |
James E. Blair | 1960d68 | 2017-04-28 15:44:14 -0700 | [diff] [blame] | 491 | self._restoreRepoState(item['connection'], item['project'], repo, |
| 492 | repo_state) |
| 493 | |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 494 | base = repo.getBranchHead(item['branch']) |
James E. Blair | 34c7daa | 2017-04-28 13:31:27 -0700 | [diff] [blame] | 495 | # Save the repo state so that later mergers can repeat |
| 496 | # this process. |
| 497 | self._saveRepoState(item['connection'], item['project'], repo, |
James E. Blair | 66c6068 | 2017-05-31 12:48:01 -0400 | [diff] [blame] | 498 | repo_state, recent) |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 499 | else: |
| 500 | self.log.debug("Found base commit %s for %s" % (base, key,)) |
| 501 | # Merge the change |
James E. Blair | 197e820 | 2017-06-09 12:54:28 -0700 | [diff] [blame] | 502 | commit = self._mergeChange(item, base) |
| 503 | if not commit: |
| 504 | return None |
| 505 | # Store this commit as the most recent for this project-branch |
| 506 | recent[key] = commit |
| 507 | # Set the Zuul ref for this item to point to the most recent |
| 508 | # commits of each project-branch |
| 509 | for key, mrc in recent.items(): |
| 510 | connection, project, branch = key |
| 511 | zuul_ref = None |
| 512 | try: |
| 513 | repo = self.getRepo(connection, project) |
James E. Blair | 247cab7 | 2017-07-20 16:52:36 -0700 | [diff] [blame] | 514 | zuul_ref = branch + '/' + item['buildset_uuid'] |
James E. Blair | 197e820 | 2017-06-09 12:54:28 -0700 | [diff] [blame] | 515 | if not repo.getCommitFromRef(zuul_ref): |
| 516 | repo.createZuulRef(zuul_ref, mrc) |
| 517 | except Exception: |
| 518 | self.log.exception("Unable to set zuul ref %s for " |
| 519 | "item %s" % (zuul_ref, item)) |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 520 | return None |
James E. Blair | 197e820 | 2017-06-09 12:54:28 -0700 | [diff] [blame] | 521 | return commit |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 522 | |
Tristan Cacqueray | 829e617 | 2017-06-13 06:49:36 +0000 | [diff] [blame] | 523 | def mergeChanges(self, items, files=None, dirs=None, repo_state=None): |
James E. Blair | 34c7daa | 2017-04-28 13:31:27 -0700 | [diff] [blame] | 524 | # connection+project+branch -> commit |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 525 | recent = {} |
| 526 | commit = None |
James E. Blair | 8b1dc3f | 2016-07-05 16:49:00 -0700 | [diff] [blame] | 527 | read_files = [] |
James E. Blair | 34c7daa | 2017-04-28 13:31:27 -0700 | [diff] [blame] | 528 | # connection -> project -> ref -> commit |
| 529 | if repo_state is None: |
| 530 | repo_state = {} |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 531 | for item in items: |
James E. Blair | 289f593 | 2017-07-27 15:02:29 -0700 | [diff] [blame] | 532 | self.log.debug("Merging for change %s,%s" % |
| 533 | (item["number"], item["patchset"])) |
James E. Blair | 34c7daa | 2017-04-28 13:31:27 -0700 | [diff] [blame] | 534 | commit = self._mergeItem(item, recent, repo_state) |
James E. Blair | ac2c324 | 2014-01-24 13:38:51 -0800 | [diff] [blame] | 535 | if not commit: |
| 536 | return None |
Tristan Cacqueray | 829e617 | 2017-06-13 06:49:36 +0000 | [diff] [blame] | 537 | if files or dirs: |
James E. Blair | 2a53567 | 2017-04-27 12:03:15 -0700 | [diff] [blame] | 538 | repo = self.getRepo(item['connection'], item['project']) |
Tristan Cacqueray | 829e617 | 2017-06-13 06:49:36 +0000 | [diff] [blame] | 539 | repo_files = repo.getFiles(files, dirs, commit=commit) |
James E. Blair | 2a53567 | 2017-04-27 12:03:15 -0700 | [diff] [blame] | 540 | read_files.append(dict( |
| 541 | connection=item['connection'], |
| 542 | project=item['project'], |
| 543 | branch=item['branch'], |
| 544 | files=repo_files)) |
James E. Blair | 57c51a1 | 2017-05-24 13:53:35 -0700 | [diff] [blame] | 545 | ret_recent = {} |
| 546 | for k, v in recent.items(): |
| 547 | ret_recent[k] = v.hexsha |
| 548 | return commit.hexsha, read_files, repo_state, ret_recent |
James E. Blair | 14abdf4 | 2015-12-09 16:11:53 -0800 | [diff] [blame] | 549 | |
James E. Blair | 289f593 | 2017-07-27 15:02:29 -0700 | [diff] [blame] | 550 | def setRepoState(self, items, repo_state): |
| 551 | # Sets the repo state for the items |
| 552 | seen = set() |
| 553 | for item in items: |
| 554 | repo = self.getRepo(item['connection'], item['project']) |
| 555 | key = (item['connection'], item['project'], item['branch']) |
| 556 | |
| 557 | if key in seen: |
| 558 | continue |
| 559 | |
| 560 | repo.reset() |
| 561 | self._restoreRepoState(item['connection'], item['project'], repo, |
| 562 | repo_state) |
| 563 | |
| 564 | def getRepoState(self, items): |
| 565 | # Gets the repo state for items. Generally this will be |
| 566 | # called in any non-change pipeline. We will return the repo |
| 567 | # state for each item, but manipulated with any information in |
| 568 | # the item (eg, if it creates a ref, that will be in the repo |
| 569 | # state regardless of the actual state). |
| 570 | seen = set() |
| 571 | recent = {} |
| 572 | repo_state = {} |
| 573 | for item in items: |
| 574 | repo = self.getRepo(item['connection'], item['project']) |
| 575 | key = (item['connection'], item['project'], item['branch']) |
| 576 | if key not in seen: |
| 577 | try: |
| 578 | repo.reset() |
| 579 | except Exception: |
| 580 | self.log.exception("Unable to reset repo %s" % repo) |
| 581 | return (False, {}) |
| 582 | |
| 583 | self._saveRepoState(item['connection'], item['project'], repo, |
| 584 | repo_state, recent) |
| 585 | |
| 586 | if item.get('newrev'): |
| 587 | # This is a ref update rather than a branch tip, so make sure |
| 588 | # our returned state includes this change. |
| 589 | self._alterRepoState(item['connection'], item['project'], |
| 590 | repo_state, item['ref'], item['newrev']) |
| 591 | return (True, repo_state) |
| 592 | |
Tristan Cacqueray | 829e617 | 2017-06-13 06:49:36 +0000 | [diff] [blame] | 593 | def getFiles(self, connection_name, project_name, branch, files, dirs=[]): |
James E. Blair | 2a53567 | 2017-04-27 12:03:15 -0700 | [diff] [blame] | 594 | repo = self.getRepo(connection_name, project_name) |
Tristan Cacqueray | 829e617 | 2017-06-13 06:49:36 +0000 | [diff] [blame] | 595 | return repo.getFiles(files, dirs, branch=branch) |
Fabien Boucher | 194a2bf | 2017-12-02 18:17:58 +0100 | [diff] [blame] | 596 | |
| 597 | def getFilesChanges(self, connection_name, project_name, branch, |
| 598 | tosha=None): |
| 599 | repo = self.getRepo(connection_name, project_name) |
| 600 | return repo.getFilesChanges(branch, tosha) |