Chromium Code Reviews| Index: py/utils/git_utils.py |
| diff --git a/py/utils/git_utils.py b/py/utils/git_utils.py |
| index 5ac63ec26a8f1ddfd821c876fffd258dd49b3b24..52ad83a6409fd4ff17853e966ba69b68775cdc28 100644 |
| --- a/py/utils/git_utils.py |
| +++ b/py/utils/git_utils.py |
| @@ -5,9 +5,12 @@ |
| """This module contains functions for using git.""" |
| - |
| +import os |
| import re |
| import shell_utils |
| +import shutil |
| +import subprocess |
| +import tempfile |
| def _FindGit(): |
| @@ -144,3 +147,79 @@ class GitBranch(object): |
| shell_utils.run([GIT, 'checkout', 'master']) |
| if self._delete_when_finished: |
| shell_utils.run([GIT, 'branch', '-D', self._branch_name]) |
| + |
| + |
| +class NewGitCheckout(object): |
| + """Creates a new local checkout of a Git repository.""" |
| + |
| + def __init__(self, repository, refspec=None, subdir=None, |
| + containing_dir=None): |
|
rmistry
2014/08/13 20:13:20
I think it makes sense to have this default to tem
rmistry
2014/08/13 20:14:20
Ah I just saw your documentation update, so if dir
epoger
2014/08/13 21:03:45
Yup, you got it.
|
| + """Check out a new local copy of the repository. |
| + |
| + Because this is a new checkout, rather than a reference to an existing |
| + checkout on disk, it is safe to assume that the calling thread is the |
| + only thread manipulating the checkout. |
| + |
| + You can use the 'with' statement to create this object in such a way that |
| + it cleans up after itself: |
| + |
| + with NewGitCheckout(*args) as checkout: |
| + # use checkout instance |
| + # the checkout is automatically cleaned up here |
| + |
| + Args: |
| + repository: name of the remote repository |
| + refspec: an arbitrary remote ref (e.g., the name of a branch); |
| + if None, allow the git command to pick a default |
| + subdir: if specified, the caller only wants access to files within this |
| + subdir in the repository. |
| + For now, we check out the entire repository regardless of this param, |
| + and just hide the rest of the repository; but later on we may |
| + optimize performance by only checking out this part of the repo. |
| + containing_dir: if specified, the new checkout will be created somewhere |
| + within this directory; otherwise, a system-dependent default location |
| + will be used, as determined by tempfile.mkdtemp() |
| + """ |
| + # _git_root points to the tree holding the git checkout in its entirety; |
| + # _file_root points to the files the caller wants to look at |
| + self._git_root = tempfile.mkdtemp(dir=containing_dir) |
| + if subdir: |
| + self._file_root = os.path.join(self._git_root, subdir) |
| + else: |
| + self._file_root = self._git_root |
| + |
| + pull_cmd = [GIT, 'pull', repository] |
| + if refspec: |
| + pull_cmd.append(refspec) |
| + self._run_in_git_root(args=[GIT, 'init']) |
| + self._run_in_git_root(args=pull_cmd) |
| + |
| + @property |
| + def root(self): |
| + """Returns the root directory containing the checked-out files. |
| + |
| + If you specified the subdir parameter in the constructor, this directory |
| + will point at just the subdir you requested. |
| + """ |
| + return self._file_root |
| + |
| + def commithash(self): |
| + """Returns the commithash of the local checkout.""" |
| + return self._run_in_git_root( |
| + args=[GIT, 'rev-parse', 'HEAD']).strip() |
| + |
| + def __enter__(self): |
| + return self |
| + |
| + # pylint: disable=W0622 |
| + def __exit__(self, type, value, traceback): |
| + shutil.rmtree(self._git_root) |
| + |
| + def _run_in_git_root(self, args): |
| + """Run an external command with cwd set to self._git_root. |
| + |
| + Returns the command's output as a byte string. |
| + |
| + Raises an Exception if the command fails. |
| + """ |
| + return subprocess.check_output(args=args, cwd=self._git_root) |