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

Side by Side Diff: tools/git_utils.py

Issue 330423004: Use new common tools in Python scripts (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: append Created 6 years, 5 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
« no previous file with comments | « tools/git-sync-deps ('k') | tools/misc_utils.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright 2014 Google Inc.
2 #
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Module to host the ChangeGitBranch class and test_git_executable function.
7 """
8
9 import os
10 import subprocess
11
12 import misc_utils
13
14
15 class ChangeGitBranch(object):
16 """Class to manage git branches.
17
18 This class allows one to create a new branch in a repository based
19 off of a given commit, and restore the original tree state.
20
21 Assumes current working directory is a git repository.
22
23 Example:
24 with ChangeGitBranch():
25 edit_files(files)
26 git_add(files)
27 git_commit()
28 git_format_patch('HEAD~')
29 # At this point, the repository is returned to its original
30 # state.
31
32 Constructor Args:
33 branch_name: (string) if not None, the name of the branch to
34 use. If None, then use a temporary branch that will be
35 deleted. If the branch already exists, then a different
36 branch name will be created. Use git_branch_name() to
37 find the actual branch name used.
38 upstream_branch: (string) if not None, the name of the branch or
39 commit to branch from. If None, then use origin/master
40 verbose: (boolean) if true, makes debugging easier.
41
42 Raises:
43 OSError: the git executable disappeared.
44 subprocess.CalledProcessError: git returned unexpected status.
45 Exception: if the given branch name exists, or if the repository
46 isn't clean on exit, or git can't be found.
47 """
48 # pylint: disable=I0011,R0903,R0902
49
50 def __init__(self,
51 branch_name=None,
52 upstream_branch=None,
53 verbose=False):
54 # pylint: disable=I0011,R0913
55 if branch_name:
56 self._branch_name = branch_name
57 self._delete_branch = False
58 else:
59 self._branch_name = 'ChangeGitBranchTempBranch'
60 self._delete_branch = True
61
62 if upstream_branch:
63 self._upstream_branch = upstream_branch
64 else:
65 self._upstream_branch = 'origin/master'
66
67 self._git = git_executable()
68 if not self._git:
69 raise Exception('Git can\'t be found.')
70
71 self._stash = None
72 self._original_branch = None
73 self._vsp = misc_utils.VerboseSubprocess(verbose)
74
75 def _has_git_diff(self):
76 """Return true iff repository has uncommited changes."""
77 return bool(self._vsp.call([self._git, 'diff', '--quiet', 'HEAD']))
78
79 def _branch_exists(self, branch):
80 """Return true iff branch exists."""
81 return 0 == self._vsp.call([self._git, 'show-ref', '--quiet', branch])
82
83 def __enter__(self):
84 git, vsp = self._git, self._vsp
85
86 if self._branch_exists(self._branch_name):
87 i, branch_name = 0, self._branch_name
88 while self._branch_exists(branch_name):
89 i += 1
90 branch_name = '%s_%03d' % (self._branch_name, i)
91 self._branch_name = branch_name
92
93 self._stash = self._has_git_diff()
94 if self._stash:
95 vsp.check_call([git, 'stash', 'save'])
96 self._original_branch = git_branch_name(vsp.verbose)
97 vsp.check_call(
98 [git, 'checkout', '-q', '-b',
99 self._branch_name, self._upstream_branch])
100
101 def __exit__(self, etype, value, traceback):
102 git, vsp = self._git, self._vsp
103
104 if self._has_git_diff():
105 status = vsp.check_output([git, 'status', '-s'])
106 raise Exception('git checkout not clean:\n%s' % status)
107 vsp.check_call([git, 'checkout', '-q', self._original_branch])
108 if self._stash:
109 vsp.check_call([git, 'stash', 'pop'])
110 if self._delete_branch:
111 assert self._original_branch != self._branch_name
112 vsp.check_call([git, 'branch', '-D', self._branch_name])
113
114
115 def git_branch_name(verbose=False):
116 """Return a description of the current branch.
117
118 Args:
119 verbose: (boolean) makes debugging easier
120
121 Returns:
122 A string suitable for passing to `git checkout` later.
123 """
124 git = git_executable()
125 vsp = misc_utils.VerboseSubprocess(verbose)
126 try:
127 full_branch = vsp.strip_output([git, 'symbolic-ref', 'HEAD'])
128 return full_branch.split('/')[-1]
129 except (subprocess.CalledProcessError,):
130 # "fatal: ref HEAD is not a symbolic ref"
131 return vsp.strip_output([git, 'rev-parse', 'HEAD'])
132
133
134 def test_git_executable(git):
135 """Test the git executable.
136
137 Args:
138 git: git executable path.
139 Returns:
140 True if test is successful.
141 """
142 with open(os.devnull, 'w') as devnull:
143 try:
144 subprocess.call([git, '--version'], stdout=devnull)
145 except (OSError,):
146 return False
147 return True
148
149
150 def git_executable():
151 """Find the git executable.
152
153 If the GIT_EXECUTABLE environment variable is set, that will
154 override whatever is found in the PATH.
155
156 If no suitable executable is found, return None
157
158 Returns:
159 A string suiable for passing to subprocess functions, or None.
160 """
161 env_git = os.environ.get('GIT_EXECUTABLE')
162 if env_git and test_git_executable(env_git):
163 return env_git
164 for git in ('git', 'git.exe', 'git.bat'):
165 if test_git_executable(git):
166 return git
167 return None
168
OLDNEW
« no previous file with comments | « tools/git-sync-deps ('k') | tools/misc_utils.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698