OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2014 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2014 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """This module contains functions for using git.""" | 6 """This module contains functions for using git.""" |
7 | 7 |
8 import os | 8 import os |
9 import re | 9 import re |
10 import shell_utils | 10 import shell_utils |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
145 self.commit_and_upload(use_commit_queue=self._commit_queue) | 145 self.commit_and_upload(use_commit_queue=self._commit_queue) |
146 finally: | 146 finally: |
147 shell_utils.run([GIT, 'checkout', 'master']) | 147 shell_utils.run([GIT, 'checkout', 'master']) |
148 if self._delete_when_finished: | 148 if self._delete_when_finished: |
149 shell_utils.run([GIT, 'branch', '-D', self._branch_name]) | 149 shell_utils.run([GIT, 'branch', '-D', self._branch_name]) |
150 | 150 |
151 | 151 |
152 class NewGitCheckout(object): | 152 class NewGitCheckout(object): |
153 """Creates a new local checkout of a Git repository.""" | 153 """Creates a new local checkout of a Git repository.""" |
154 | 154 |
155 def __init__(self, repository, refspec=None, subdir=None, | 155 def __init__(self, repository, refspec=None, commit='HEAD', |
156 containing_dir=None): | 156 subdir=None, containing_dir=None): |
157 """Check out a new local copy of the repository. | 157 """Set parameters for this local copy of a Git repository. |
158 | 158 |
159 Because this is a new checkout, rather than a reference to an existing | 159 Because this is a new checkout, rather than a reference to an existing |
160 checkout on disk, it is safe to assume that the calling thread is the | 160 checkout on disk, it is safe to assume that the calling thread is the |
161 only thread manipulating the checkout. | 161 only thread manipulating the checkout. |
162 | 162 |
163 You can use the 'with' statement to create this object in such a way that | 163 You must use the 'with' statement to create this object: |
164 it cleans up after itself: | |
165 | 164 |
166 with NewGitCheckout(*args) as checkout: | 165 with NewGitCheckout(*args) as checkout: |
167 # use checkout instance | 166 # use checkout instance |
168 # the checkout is automatically cleaned up here | 167 # the checkout is automatically cleaned up here |
169 | 168 |
170 Args: | 169 Args: |
171 repository: name of the remote repository | 170 repository: URL of the remote repository (e.g., |
172 refspec: an arbitrary remote ref (e.g., the name of a branch); | 171 'https://skia.googlesource.com/common') or path to a local repository |
173 if None, allow the git command to pick a default | 172 (e.g., '/path/to/repo/.git') to check out a copy of |
| 173 refspec: which refs (e.g., a branch name) to fetch from the repository; |
| 174 if None, git-fetch will choose the default refs to fetch |
| 175 commit: commit hash, branch, or tag within refspec, indicating what point |
| 176 to update the local checkout to |
174 subdir: if specified, the caller only wants access to files within this | 177 subdir: if specified, the caller only wants access to files within this |
175 subdir in the repository. | 178 subdir in the repository. |
176 For now, we check out the entire repository regardless of this param, | 179 For now, we check out the entire repository regardless of this param, |
177 and just hide the rest of the repository; but later on we may | 180 and just hide the rest of the repository; but later on we may |
178 optimize performance by only checking out this part of the repo. | 181 optimize performance by only checking out this part of the repo. |
179 containing_dir: if specified, the new checkout will be created somewhere | 182 containing_dir: if specified, the new checkout will be created somewhere |
180 within this directory; otherwise, a system-dependent default location | 183 within this directory; otherwise, a system-dependent default location |
181 will be used, as determined by tempfile.mkdtemp() | 184 will be used, as determined by tempfile.mkdtemp() |
182 """ | 185 """ |
183 # _git_root points to the tree holding the git checkout in its entirety; | 186 self._repository = repository |
184 # _file_root points to the files the caller wants to look at | 187 self._refspec = refspec |
185 self._git_root = tempfile.mkdtemp(dir=containing_dir) | 188 self._commit = commit |
186 if subdir: | 189 self._subdir = subdir |
187 self._file_root = os.path.join(self._git_root, subdir) | 190 self._containing_dir = containing_dir |
188 else: | 191 self._git_root = None |
189 self._file_root = self._git_root | 192 self._file_root = None |
190 | 193 |
191 pull_cmd = [GIT, 'pull', repository] | |
192 if refspec: | |
193 pull_cmd.append(refspec) | |
194 self._run_in_git_root(args=[GIT, 'init']) | |
195 self._run_in_git_root(args=pull_cmd) | |
196 | 194 |
197 @property | 195 @property |
198 def root(self): | 196 def root(self): |
199 """Returns the root directory containing the checked-out files. | 197 """Returns the root directory containing the checked-out files. |
200 | 198 |
201 If you specified the subdir parameter in the constructor, this directory | 199 If you specified the subdir parameter in the constructor, this directory |
202 will point at just the subdir you requested. | 200 will point at just the subdir you requested. |
203 """ | 201 """ |
204 return self._file_root | 202 return self._file_root |
205 | 203 |
206 def commithash(self): | 204 def commithash(self): |
207 """Returns the commithash of the local checkout.""" | 205 """Returns the commithash of the local checkout.""" |
208 return self._run_in_git_root( | 206 return self._run_in_git_root( |
209 args=[GIT, 'rev-parse', 'HEAD']).strip() | 207 args=[GIT, 'rev-parse', 'HEAD']).strip() |
210 | 208 |
211 def __enter__(self): | 209 def __enter__(self): |
| 210 """Check out a new local copy of the repository. |
| 211 |
| 212 Uses the parameters that were passed into the constructor. |
| 213 """ |
| 214 # _git_root points to the tree holding the git checkout in its entirety; |
| 215 # _file_root points to the files the caller wants to look at |
| 216 self._git_root = tempfile.mkdtemp(dir=self._containing_dir) |
| 217 if self._subdir: |
| 218 self._file_root = os.path.join(self._git_root, self._subdir) |
| 219 else: |
| 220 self._file_root = self._git_root |
| 221 |
| 222 local_branch_name = 'local' |
| 223 self._run_in_git_root(args=[GIT, 'init']) |
| 224 fetch_cmd = [GIT, 'fetch', self._repository] |
| 225 if self._refspec: |
| 226 fetch_cmd.append(self._refspec) |
| 227 self._run_in_git_root(args=fetch_cmd) |
| 228 self._run_in_git_root(args=[GIT, 'merge', 'FETCH_HEAD']) |
| 229 self._run_in_git_root(args=[GIT, 'branch', local_branch_name, self._commit]) |
| 230 self._run_in_git_root(args=[GIT, 'checkout', local_branch_name]) |
| 231 |
212 return self | 232 return self |
213 | 233 |
214 # pylint: disable=W0622 | 234 # pylint: disable=W0622 |
215 def __exit__(self, type, value, traceback): | 235 def __exit__(self, type, value, traceback): |
216 shutil.rmtree(self._git_root) | 236 shutil.rmtree(self._git_root) |
217 | 237 |
218 def _run_in_git_root(self, args): | 238 def _run_in_git_root(self, args): |
219 """Run an external command with cwd set to self._git_root. | 239 """Run an external command with cwd set to self._git_root. |
220 | 240 |
221 Returns the command's output as a byte string. | 241 Returns the command's output as a byte string. |
222 | 242 |
223 Raises an Exception if the command fails. | 243 Raises an Exception if the command fails. |
224 """ | 244 """ |
225 return subprocess.check_output(args=args, cwd=self._git_root) | 245 return subprocess.check_output(args=args, cwd=self._git_root) |
OLD | NEW |