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

Side by Side Diff: third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/git.py

Issue 2815093005: Clean up webkitpy.common.checkout.git module. (Closed)
Patch Set: git co wpt-import Created 3 years, 8 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 | « no previous file | third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/git_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2009, 2010, 2011 Google Inc. All rights reserved. 1 # Copyright (c) 2009, 2010, 2011 Google Inc. All rights reserved.
2 # Copyright (c) 2009 Apple Inc. All rights reserved. 2 # Copyright (c) 2009 Apple Inc. All rights reserved.
3 # 3 #
4 # Redistribution and use in source and binary forms, with or without 4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are 5 # modification, are permitted provided that the following conditions are
6 # met: 6 # met:
7 # 7 #
8 # * Redistributions of source code must retain the above copyright 8 # * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer. 9 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above 10 # * Redistributions in binary form must reproduce the above
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
59 _log.info('The current directory (%s) is not in a git repo, trying d irectory %s.', 59 _log.info('The current directory (%s) is not in a git repo, trying d irectory %s.',
60 cwd, module_directory) 60 cwd, module_directory)
61 if Git.in_working_directory(module_directory, executive=self._execut ive): 61 if Git.in_working_directory(module_directory, executive=self._execut ive):
62 self.cwd = module_directory 62 self.cwd = module_directory
63 _log.error('Failed to find Git repo for %s or %s', cwd, module_direc tory) 63 _log.error('Failed to find Git repo for %s or %s', cwd, module_direc tory)
64 64
65 self._init_executable_name() 65 self._init_executable_name()
66 self.checkout_root = self.find_checkout_root(self.cwd) 66 self.checkout_root = self.find_checkout_root(self.cwd)
67 67
68 def _init_executable_name(self): 68 def _init_executable_name(self):
69 # FIXME: This is a hack and should be removed. 69 """Sets the executable name on Windows.
70
71 The Win port uses the depot_tools package, which contains a number
72 of development tools, including Python and git. Instead of using a
73 real git executable, depot_tools indirects via a batch file, called
74 "git.bat". This batch file is used because it allows depot_tools to
75 auto-update the real git executable, which is contained in a
76 subdirectory.
77
78 FIXME: This is a hack and should be resolved in a different way if
79 possible.
80 """
70 try: 81 try:
71 self._executive.run_command(['git', 'help']) 82 self._executive.run_command(['git', 'help'])
72 except OSError: 83 except OSError:
73 try: 84 try:
74 self._executive.run_command(['git.bat', 'help']) 85 self._executive.run_command(['git.bat', 'help'])
75 # The Win port uses the depot_tools package, which contains a nu mber
76 # of development tools, including Python and git. Instead of usi ng a
77 # real git executable, depot_tools indirects via a batch file, c alled
78 # "git.bat". This batch file allows depot_tools to auto-update t he real
79 # git executable, which is contained in a subdirectory.
80 _log.debug('Engaging git.bat Windows hack.') 86 _log.debug('Engaging git.bat Windows hack.')
81 self.executable_name = 'git.bat' 87 self.executable_name = 'git.bat'
82 except OSError: 88 except OSError:
83 _log.debug('Failed to engage git.bat Windows hack.') 89 _log.debug('Failed to engage git.bat Windows hack.')
84 90
85 def _run_git(self, 91 def run(self, command_args, cwd=None, stdin=None, decode_output=True, return _exit_code=False):
86 command_args, 92 """Invokes git with the given args."""
87 cwd=None,
88 input=None, # pylint: disable=redefined-builtin
89 timeout_seconds=None,
90 decode_output=True,
91 return_exit_code=False):
92 full_command_args = [self.executable_name] + command_args 93 full_command_args = [self.executable_name] + command_args
93 cwd = cwd or self.checkout_root 94 cwd = cwd or self.checkout_root
94 return self._executive.run_command( 95 return self._executive.run_command(
95 full_command_args, 96 full_command_args,
96 cwd=cwd, 97 cwd=cwd,
97 input=input, 98 input=stdin,
98 timeout_seconds=timeout_seconds,
99 return_exit_code=return_exit_code, 99 return_exit_code=return_exit_code,
100 decode_output=decode_output) 100 decode_output=decode_output)
101 101
102 # SCM always returns repository relative path, but sometimes we need
103 # absolute paths to pass to rm, etc.
104 def absolute_path(self, repository_relative_path): 102 def absolute_path(self, repository_relative_path):
103 """Converts repository-relative paths to absolute paths."""
105 return self._filesystem.join(self.checkout_root, repository_relative_pat h) 104 return self._filesystem.join(self.checkout_root, repository_relative_pat h)
106 105
107 @classmethod 106 @classmethod
108 def in_working_directory(cls, path, executive=None): 107 def in_working_directory(cls, path, executive=None):
109 try: 108 try:
110 executive = executive or Executive() 109 executive = executive or Executive()
111 return executive.run_command([cls.executable_name, 'rev-parse', '--i s-inside-work-tree'], 110 return executive.run_command(
112 cwd=path, error_handler=Executive.ignor e_error).rstrip() == 'true' 111 [cls.executable_name, 'rev-parse', '--is-inside-work-tree'],
112 cwd=path, error_handler=Executive.ignore_error).rstrip() == 'tru e'
113 except OSError: 113 except OSError:
114 # The Windows bots seem to through a WindowsError when git isn't ins talled. 114 # The Windows bots seem to throw a WindowsError when git isn't insta lled.
115 # TODO(qyearsley): This might be because the git executable name
116 # isn't initialized yet; maybe this would be fixed by using the
117 # _init_executable_name hack above.
118 _log.warn('Got OSError when running Git.in_working_directory.')
115 return False 119 return False
116 120
117 def find_checkout_root(self, path): 121 def find_checkout_root(self, path):
118 # "git rev-parse --show-cdup" would be another way to get to the root 122 # "git rev-parse --show-cdup" would be another way to get to the root
119 checkout_root = self._run_git(['rev-parse', '--show-toplevel'], cwd=(pat h or './')).strip() 123 checkout_root = self.run(['rev-parse', '--show-toplevel'], cwd=(path or './')).strip()
120 if not self._filesystem.isabs(checkout_root): # Sometimes git returns r elative paths 124 if not self._filesystem.isabs(checkout_root): # Sometimes git returns r elative paths
121 checkout_root = self._filesystem.join(path, checkout_root) 125 checkout_root = self._filesystem.join(path, checkout_root)
122 return checkout_root 126 return checkout_root
123 127
124 @classmethod 128 @classmethod
125 def read_git_config(cls, key, cwd=None, executive=None): 129 def read_git_config(cls, key, cwd=None, executive=None):
126 # FIXME: This should probably use cwd=self.checkout_root. 130 # FIXME: This should probably use cwd=self.checkout_root.
127 # Pass --get-all for cases where the config has multiple values 131 # Pass --get-all for cases where the config has multiple values
128 # Pass the cwd if provided so that we can handle the case of running web kit-patch outside of the working directory. 132 # Pass the cwd if provided so that we can handle the case of running web kit-patch outside of the working directory.
129 # FIXME: This should use an Executive. 133 # FIXME: This should use an Executive.
130 executive = executive or Executive() 134 executive = executive or Executive()
131 return executive.run_command( 135 return executive.run_command(
132 [cls.executable_name, 'config', '--get-all', key], error_handler=Exe cutive.ignore_error, cwd=cwd).rstrip('\n') 136 [cls.executable_name, 'config', '--get-all', key], error_handler=Exe cutive.ignore_error, cwd=cwd).rstrip('\n')
133 137
134 def _discard_local_commits(self): 138 def _discard_local_commits(self):
135 self._run_git(['reset', '--hard', self._remote_branch_ref()]) 139 self.run(['reset', '--hard', self._remote_branch_ref()])
136
137 def _local_commits(self, ref='HEAD'):
138 return self._run_git(['log', '--pretty=oneline', ref + '...' + self._rem ote_branch_ref()]).splitlines()
139 140
140 def _rebase_in_progress(self): 141 def _rebase_in_progress(self):
141 return self._filesystem.exists(self.absolute_path(self._filesystem.join( '.git', 'rebase-apply'))) 142 return self._filesystem.exists(self.absolute_path(self._filesystem.join( '.git', 'rebase-apply')))
142 143
143 def has_working_directory_changes(self, pathspec=None): 144 def has_working_directory_changes(self, pathspec=None):
144 """Checks whether there are uncommitted changes.""" 145 """Checks whether there are uncommitted changes."""
145 command = ['diff', 'HEAD', '--no-renames', '--name-only'] 146 command = ['diff', 'HEAD', '--no-renames', '--name-only']
146 if pathspec: 147 if pathspec:
147 command.extend(['--', pathspec]) 148 command.extend(['--', pathspec])
148 return self._run_git(command) != '' 149 return self.run(command) != ''
149 150
150 def _discard_working_directory_changes(self): 151 def _discard_working_directory_changes(self):
151 # Could run git clean here too, but that wouldn't match subversion 152 # TODO(qyearsley): Could run git clean here too; this wasn't done
152 self._run_git(['reset', 'HEAD', '--hard']) 153 # before in order to match svn, but this is no longer a concern.
153 # Aborting rebase even though this does not match subversion 154 self.run(['reset', 'HEAD', '--hard'])
154 if self._rebase_in_progress(): 155 if self._rebase_in_progress():
155 self._run_git(['rebase', '--abort']) 156 self.run(['rebase', '--abort'])
156 157
157 def unstaged_changes(self): 158 def unstaged_changes(self):
158 """Lists files with unstaged changes, including untracked files. 159 """Lists files with unstaged changes, including untracked files.
159 160
160 Returns a dict mapping modified file paths (relative to checkout root) 161 Returns a dict mapping modified file paths (relative to checkout root)
161 to one-character codes identifying the change, e.g. 'M' for modified, 162 to one-character codes identifying the change, e.g. 'M' for modified,
162 'D' for deleted, '?' for untracked. 163 'D' for deleted, '?' for untracked.
163 """ 164 """
164 # `git status -z` is a version of `git status -s`, that's recommended 165 # `git status -z` is a version of `git status -s`, that's recommended
165 # for machine parsing. Lines are terminated with NUL rather than LF. 166 # for machine parsing. Lines are terminated with NUL rather than LF.
166 change_lines = self._run_git(['status', '-z', '--untracked-files=all']). rstrip('\x00') 167 change_lines = self.run(['status', '-z', '--untracked-files=all']).rstri p('\x00')
167 if not change_lines: 168 if not change_lines:
168 return {} # No changes. 169 return {} # No changes.
169 unstaged_changes = {} 170 unstaged_changes = {}
170 for line in change_lines.split('\x00'): 171 for line in change_lines.split('\x00'):
171 assert len(line) >= 4, 'Unexpected change line format %s' % line 172 assert len(line) >= 4, 'Unexpected change line format %s' % line
172 if line[1] == ' ': 173 if line[1] == ' ':
173 continue # Already staged for commit. 174 continue # Already staged for commit.
174 path = line[3:] 175 path = line[3:]
175 unstaged_changes[path] = line[1] 176 unstaged_changes[path] = line[1]
176 return unstaged_changes 177 return unstaged_changes
177 178
178 def add_list(self, paths, return_exit_code=False): 179 def add_list(self, paths, return_exit_code=False):
179 return self._run_git(['add'] + paths, return_exit_code=return_exit_code) 180 return self.run(['add'] + paths, return_exit_code=return_exit_code)
180 181
181 def delete_list(self, paths): 182 def delete_list(self, paths):
182 return self._run_git(['rm', '-f'] + paths) 183 return self.run(['rm', '-f'] + paths)
183 184
184 def move(self, origin, destination): 185 def move(self, origin, destination):
185 return self._run_git(['mv', '-f', origin, destination]) 186 return self.run(['mv', '-f', origin, destination])
186 187
187 def exists(self, path): 188 def exists(self, path):
188 return_code = self._run_git(['show', 'HEAD:%s' % path], return_exit_code =True, decode_output=False) 189 return_code = self.run(['show', 'HEAD:%s' % path], return_exit_code=True , decode_output=False)
189 return return_code != self.ERROR_FILE_IS_MISSING 190 return return_code != self.ERROR_FILE_IS_MISSING
190 191
191 def _branch_from_ref(self, ref): 192 def _branch_from_ref(self, ref):
192 return ref.replace('refs/heads/', '') 193 return ref.replace('refs/heads/', '')
193 194
194 def current_branch(self): 195 def current_branch(self):
195 """Returns the name of the current branch, or empty string if HEAD is de tached.""" 196 """Returns the name of the current branch, or empty string if HEAD is de tached."""
196 ref = self._run_git(['rev-parse', '--symbolic-full-name', 'HEAD']).strip () 197 ref = self.run(['rev-parse', '--symbolic-full-name', 'HEAD']).strip()
197 if ref == 'HEAD': 198 if ref == 'HEAD':
198 # HEAD is detached; return an empty string. 199 # HEAD is detached; return an empty string.
199 return '' 200 return ''
200 return self._branch_from_ref(ref) 201 return self._branch_from_ref(ref)
201 202
202 def current_branch_or_ref(self): 203 def current_branch_or_ref(self):
203 """Returns the name of the current branch, or the commit hash if HEAD is detached.""" 204 """Returns the name of the current branch, or the commit hash if HEAD is detached."""
204 branch_name = self.current_branch() 205 branch_name = self.current_branch()
205 if not branch_name: 206 if not branch_name:
206 # HEAD is detached; use commit SHA instead. 207 # HEAD is detached; use commit SHA instead.
207 return self._run_git(['rev-parse', 'HEAD']).strip() 208 return self.run(['rev-parse', 'HEAD']).strip()
208 return branch_name 209 return branch_name
209 210
210 def _upstream_branch(self): 211 def _upstream_branch(self):
211 current_branch = self.current_branch() 212 current_branch = self.current_branch()
212 return self._branch_from_ref(self.read_git_config( 213 return self._branch_from_ref(self.read_git_config(
213 'branch.%s.merge' % current_branch, cwd=self.checkout_root, executiv e=self._executive).strip()) 214 'branch.%s.merge' % current_branch, cwd=self.checkout_root, executiv e=self._executive).strip())
214 215
215 def _merge_base(self, git_commit=None): 216 def _merge_base(self, git_commit=None):
216 if git_commit: 217 if git_commit:
217 # Rewrite UPSTREAM to the upstream branch 218 # Rewrite UPSTREAM to the upstream branch
(...skipping 19 matching lines...) Expand all
237 '--no-renames', '--no-ext-diff', '--full-index', self. _merge_base(git_commit)] 238 '--no-renames', '--no-ext-diff', '--full-index', self. _merge_base(git_commit)]
238 # Added (A), Copied (C), Deleted (D), Modified (M), Renamed (R) 239 # Added (A), Copied (C), Deleted (D), Modified (M), Renamed (R)
239 return self._run_status_and_extract_filenames(status_command, self._stat us_regexp(diff_filter)) 240 return self._run_status_and_extract_filenames(status_command, self._stat us_regexp(diff_filter))
240 241
241 def added_files(self): 242 def added_files(self):
242 return self._run_status_and_extract_filenames(self.status_command(), sel f._status_regexp('A')) 243 return self._run_status_and_extract_filenames(self.status_command(), sel f._status_regexp('A'))
243 244
244 def _run_status_and_extract_filenames(self, status_command, status_regexp): 245 def _run_status_and_extract_filenames(self, status_command, status_regexp):
245 filenames = [] 246 filenames = []
246 # We run with cwd=self.checkout_root so that returned-paths are root-rel ative. 247 # We run with cwd=self.checkout_root so that returned-paths are root-rel ative.
247 for line in self._run_git(status_command, cwd=self.checkout_root).splitl ines(): 248 for line in self.run(status_command, cwd=self.checkout_root).splitlines( ):
248 match = re.search(status_regexp, line) 249 match = re.search(status_regexp, line)
249 if not match: 250 if not match:
250 continue 251 continue
251 # status = match.group('status') 252 # status = match.group('status')
252 filename = match.group('filename') 253 filename = match.group('filename')
253 filenames.append(filename) 254 filenames.append(filename)
254 return filenames 255 return filenames
255 256
256 def status_command(self): 257 def status_command(self):
257 # git status returns non-zero when there are changes, so we use git diff name --name-status HEAD instead. 258 # git status returns non-zero when there are changes, so we use git diff name --name-status HEAD instead.
258 # No file contents printed, thus utf-8 autodecoding in self.run is fine. 259 # No file contents printed, thus utf-8 autodecoding in self.run is fine.
259 return ['diff', '--name-status', '--no-renames', 'HEAD'] 260 return ['diff', '--name-status', '--no-renames', 'HEAD']
260 261
261 def _status_regexp(self, expected_types): 262 def _status_regexp(self, expected_types):
262 return '^(?P<status>[%s])\t(?P<filename>.+)$' % expected_types 263 return '^(?P<status>[%s])\t(?P<filename>.+)$' % expected_types
263 264
264 @staticmethod 265 @staticmethod
265 def supports_local_commits(): 266 def supports_local_commits():
267 # TODO(qyearsley): Remove this.
266 return True 268 return True
267 269
268 def display_name(self): 270 def display_name(self):
269 return 'git' 271 return 'git'
270 272
271 def most_recent_log_matching(self, grep_str, path): 273 def most_recent_log_matching(self, grep_str, path):
272 # We use '--grep=' + foo rather than '--grep', foo because 274 # We use '--grep=' + foo rather than '--grep', foo because
273 # git 1.7.0.4 (and earlier) didn't support the separate arg. 275 # git 1.7.0.4 (and earlier) didn't support the separate arg.
274 return self._run_git(['log', '-1', '--grep=' + grep_str, '--date=iso', s elf.find_checkout_root(path)]) 276 return self.run(['log', '-1', '--grep=' + grep_str, '--date=iso', self.f ind_checkout_root(path)])
275 277
276 def _commit_position_from_git_log(self, git_log): 278 def _commit_position_from_git_log(self, git_log):
277 match = re.search(r"^\s*Cr-Commit-Position:.*@\{#(?P<commit_position>\d+ )\}", git_log, re.MULTILINE) 279 match = re.search(r"^\s*Cr-Commit-Position:.*@\{#(?P<commit_position>\d+ )\}", git_log, re.MULTILINE)
278 if not match: 280 if not match:
279 return '' 281 return ''
280 return int(match.group('commit_position')) 282 return int(match.group('commit_position'))
281 283
282 def commit_position(self, path): 284 def commit_position(self, path):
283 """Returns the latest chromium commit position found in the checkout.""" 285 """Returns the latest chromium commit position found in the checkout."""
284 git_log = self.most_recent_log_matching('Cr-Commit-Position:', path) 286 git_log = self.most_recent_log_matching('Cr-Commit-Position:', path)
(...skipping 13 matching lines...) Expand all
298 time_with_timezone = datetime.datetime(int(match.group(1)), int(match.gr oup(2)), int(match.group(3)), 300 time_with_timezone = datetime.datetime(int(match.group(1)), int(match.gr oup(2)), int(match.group(3)),
299 int(match.group(4)), int(match.gr oup(5)), int(match.group(6)), 0) 301 int(match.group(4)), int(match.gr oup(5)), int(match.group(6)), 0)
300 302
301 sign = 1 if match.group(7) == '+' else -1 303 sign = 1 if match.group(7) == '+' else -1
302 time_without_timezone = time_with_timezone - \ 304 time_without_timezone = time_with_timezone - \
303 datetime.timedelta(hours=sign * int(match.group(8)), minutes=int(mat ch.group(9))) 305 datetime.timedelta(hours=sign * int(match.group(8)), minutes=int(mat ch.group(9)))
304 return time_without_timezone.strftime('%Y-%m-%dT%H:%M:%SZ') 306 return time_without_timezone.strftime('%Y-%m-%dT%H:%M:%SZ')
305 307
306 def create_patch(self, git_commit=None, changed_files=None): 308 def create_patch(self, git_commit=None, changed_files=None):
307 """Returns a byte array (str()) representing the patch file. 309 """Returns a byte array (str()) representing the patch file.
310
308 Patch files are effectively binary since they may contain 311 Patch files are effectively binary since they may contain
309 files of multiple different encodings. 312 files of multiple different encodings.
310 """ 313 """
311 order = self._patch_order() 314 order = self._patch_order()
312 command = [ 315 command = [
313 'diff', 316 'diff',
314 '--binary', 317 '--binary',
315 '--no-color', 318 '--no-color',
316 '--no-ext-diff', 319 '--no-ext-diff',
317 '--full-index', 320 '--full-index',
318 '--no-renames', 321 '--no-renames',
319 '--src-prefix=a/', 322 '--src-prefix=a/',
320 '--dst-prefix=b/', 323 '--dst-prefix=b/',
321 324
322 ] 325 ]
323 if order: 326 if order:
324 command.append(order) 327 command.append(order)
325 command += [self._merge_base(git_commit), '--'] 328 command += [self._merge_base(git_commit), '--']
326 if changed_files: 329 if changed_files:
327 command += changed_files 330 command += changed_files
328 return self._run_git(command, decode_output=False, cwd=self.checkout_roo t) 331 return self.run(command, decode_output=False, cwd=self.checkout_root)
329 332
330 def _patch_order(self): 333 def _patch_order(self):
331 # Put code changes at the top of the patch and layout tests 334 # Put code changes at the top of the patch and layout tests
332 # at the bottom, this makes for easier reviewing. 335 # at the bottom, this makes for easier reviewing.
333 config_path = self._filesystem.dirname(self._filesystem.path_to_module(' webkitpy.common.config')) 336 config_path = self._filesystem.dirname(self._filesystem.path_to_module(' webkitpy.common.config'))
334 order_file = self._filesystem.join(config_path, 'orderfile') 337 order_file = self._filesystem.join(config_path, 'orderfile')
335 if self._filesystem.exists(order_file): 338 if self._filesystem.exists(order_file):
336 return '-O%s' % order_file 339 return '-O%s' % order_file
337 return '' 340 return ''
338 341
339 @memoized 342 @memoized
340 def commit_position_from_git_commit(self, git_commit): 343 def commit_position_from_git_commit(self, git_commit):
341 git_log = self.git_commit_detail(git_commit) 344 git_log = self.git_commit_detail(git_commit)
342 return self._commit_position_from_git_log(git_log) 345 return self._commit_position_from_git_log(git_log)
343 346
344 def checkout_branch(self, name): 347 def checkout_branch(self, name):
345 self._run_git(['checkout', '-q', name]) 348 self.run(['checkout', '-q', name])
346 349
347 def create_clean_branch(self, name): 350 def create_clean_branch(self, name):
348 self._run_git(['checkout', '-q', '-b', name, self._remote_branch_ref()]) 351 self.run(['checkout', '-q', '-b', name, self._remote_branch_ref()])
349 352
350 def blame(self, path): 353 def blame(self, path):
351 return self._run_git(['blame', '--show-email', path]) 354 return self.run(['blame', '--show-email', path])
352 355
353 # Git-specific methods: 356 # Git-specific methods:
354 def _branch_ref_exists(self, branch_ref): 357 def _branch_ref_exists(self, branch_ref):
355 return self._run_git(['show-ref', '--quiet', '--verify', branch_ref], re turn_exit_code=True) == 0 358 return self.run(['show-ref', '--quiet', '--verify', branch_ref], return_ exit_code=True) == 0
356 359
357 def delete_branch(self, branch_name): 360 def delete_branch(self, branch_name):
358 if self._branch_ref_exists('refs/heads/' + branch_name): 361 if self._branch_ref_exists('refs/heads/' + branch_name):
359 self._run_git(['branch', '-D', branch_name]) 362 self.run(['branch', '-D', branch_name])
360 363
361 def _remote_merge_base(self): 364 def _remote_merge_base(self):
362 return self._run_git(['merge-base', self._remote_branch_ref(), 'HEAD']). strip() 365 return self.run(['merge-base', self._remote_branch_ref(), 'HEAD']).strip ()
363 366
364 def _remote_branch_ref(self): 367 def _remote_branch_ref(self):
365 # Use references so that we can avoid collisions, e.g. we don't want to operate on refs/heads/trunk if it exists. 368 # Use references so that we can avoid collisions, e.g. we don't want to operate on refs/heads/trunk if it exists.
366 remote_master_ref = 'refs/remotes/origin/master' 369 remote_master_ref = 'refs/remotes/origin/master'
367 if not self._branch_ref_exists(remote_master_ref): 370 if not self._branch_ref_exists(remote_master_ref):
368 raise ScriptError(message="Can't find a branch to diff against. %s d oes not exist" % remote_master_ref) 371 raise ScriptError(message="Can't find a branch to diff against. %s d oes not exist" % remote_master_ref)
369 return remote_master_ref 372 return remote_master_ref
370 373
371 def commit_locally_with_message(self, message): 374 def commit_locally_with_message(self, message):
372 command = ['commit', '--all', '-F', '-'] 375 command = ['commit', '--all', '-F', '-']
373 self._run_git(command, input=message) 376 self.run(command, stdin=message)
374
375 def pull(self, timeout_seconds=None):
376 self._run_git(['pull'], timeout_seconds=timeout_seconds)
377 377
378 def latest_git_commit(self): 378 def latest_git_commit(self):
379 return self._run_git(['log', '-1', '--format=%H']).strip() 379 return self.run(['log', '-1', '--format=%H']).strip()
380 380
381 def git_commits_since(self, commit): 381 def git_commits_since(self, commit):
382 return self._run_git(['log', commit + '..master', '--format=%H', '--reve rse']).split() 382 return self.run(['log', commit + '..master', '--format=%H', '--reverse'] ).split()
383 383
384 def git_commit_detail(self, commit, format=None): # pylint: disable=redefin ed-builtin 384 def git_commit_detail(self, commit, format=None): # pylint: disable=redefin ed-builtin
385 args = ['log', '-1', commit] 385 args = ['log', '-1', commit]
386 if format: 386 if format:
387 args.append('--format=' + format) 387 args.append('--format=' + format)
388 return self._run_git(args) 388 return self.run(args)
389 389
390 def affected_files(self, commit): 390 def affected_files(self, commit):
391 output = self._run_git(['log', '-1', '--format=', '--name-only', commit] ) 391 output = self.run(['log', '-1', '--format=', '--name-only', commit])
392 return output.strip().split('\n') 392 return output.strip().split('\n')
393 393
394 def _branch_tracking_remote_master(self): 394 def _branch_tracking_remote_master(self):
395 origin_info = self._run_git(['remote', 'show', 'origin', '-n']) 395 origin_info = self.run(['remote', 'show', 'origin', '-n'])
396 match = re.search(r"^\s*(?P<branch_name>\S+)\s+merges with remote master $", origin_info, re.MULTILINE) 396 match = re.search(r"^\s*(?P<branch_name>\S+)\s+merges with remote master $", origin_info, re.MULTILINE)
397 if not match: 397 if not match:
398 raise ScriptError(message='Unable to find local branch tracking orig in/master.') 398 raise ScriptError(message='Unable to find local branch tracking orig in/master.')
399 branch = str(match.group('branch_name')) 399 branch = str(match.group('branch_name'))
400 return self._branch_from_ref(self._run_git(['rev-parse', '--symbolic-ful l-name', branch]).strip()) 400 return self._branch_from_ref(self.run(['rev-parse', '--symbolic-full-nam e', branch]).strip())
401
402 def is_cleanly_tracking_remote_master(self):
403 if self.has_working_directory_changes():
404 return False
405 if self.current_branch() != self._branch_tracking_remote_master():
406 return False
407 if len(self._local_commits(self._branch_tracking_remote_master())) > 0:
408 return False
409 return True
410 401
411 def ensure_cleanly_tracking_remote_master(self): 402 def ensure_cleanly_tracking_remote_master(self):
412 self._discard_working_directory_changes() 403 self._discard_working_directory_changes()
413 self._run_git(['checkout', '-q', self._branch_tracking_remote_master()]) 404 self.run(['checkout', '-q', self._branch_tracking_remote_master()])
414 self._discard_local_commits() 405 self._discard_local_commits()
OLDNEW
« no previous file with comments | « no previous file | third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/git_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698