| OLD | NEW |
| 1 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """SCM-specific utility classes.""" | 5 """SCM-specific utility classes.""" |
| 6 | 6 |
| 7 import cStringIO | 7 import cStringIO |
| 8 import glob | 8 import glob |
| 9 import os | 9 import os |
| 10 import re | 10 import re |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 59 for line in file_content: | 59 for line in file_content: |
| 60 data.write('+') | 60 data.write('+') |
| 61 data.write(line) | 61 data.write(line) |
| 62 result = data.getvalue() | 62 result = data.getvalue() |
| 63 data.close() | 63 data.close() |
| 64 return result | 64 return result |
| 65 | 65 |
| 66 | 66 |
| 67 class GIT(object): | 67 class GIT(object): |
| 68 @staticmethod | 68 @staticmethod |
| 69 def Capture(args, in_directory=None, print_error=True, error_ok=False): | 69 def Capture(args, **kwargs): |
| 70 """Runs git, capturing output sent to stdout as a string. | 70 return gclient_utils.CheckCall(['git'] + args, print_error=False, |
| 71 | 71 **kwargs)[0] |
| 72 Args: | |
| 73 args: A sequence of command line parameters to be passed to git. | |
| 74 in_directory: The directory where git is to be run. | |
| 75 | |
| 76 Returns: | |
| 77 The output sent to stdout as a string. | |
| 78 """ | |
| 79 try: | |
| 80 return gclient_utils.CheckCall(['git'] + args, in_directory, print_error) | |
| 81 except gclient_utils.CheckCallError: | |
| 82 if error_ok: | |
| 83 return ('', '') | |
| 84 raise | |
| 85 | 72 |
| 86 @staticmethod | 73 @staticmethod |
| 87 def CaptureStatus(files, upstream_branch=None): | 74 def CaptureStatus(files, upstream_branch=None): |
| 88 """Returns git status. | 75 """Returns git status. |
| 89 | 76 |
| 90 @files can be a string (one file) or a list of files. | 77 @files can be a string (one file) or a list of files. |
| 91 | 78 |
| 92 Returns an array of (status, file) tuples.""" | 79 Returns an array of (status, file) tuples.""" |
| 93 if upstream_branch is None: | 80 if upstream_branch is None: |
| 94 upstream_branch = GIT.GetUpstreamBranch(os.getcwd()) | 81 upstream_branch = GIT.GetUpstreamBranch(os.getcwd()) |
| 95 if upstream_branch is None: | 82 if upstream_branch is None: |
| 96 raise Exception("Cannot determine upstream branch") | 83 raise gclient_utils.Error('Cannot determine upstream branch') |
| 97 command = ["diff", "--name-status", "-r", "%s..." % upstream_branch] | 84 command = ['diff', '--name-status', '-r', '%s...' % upstream_branch] |
| 98 if not files: | 85 if not files: |
| 99 pass | 86 pass |
| 100 elif isinstance(files, basestring): | 87 elif isinstance(files, basestring): |
| 101 command.append(files) | 88 command.append(files) |
| 102 else: | 89 else: |
| 103 command.extend(files) | 90 command.extend(files) |
| 104 | 91 status = GIT.Capture(command).rstrip() |
| 105 status = GIT.Capture(command)[0].rstrip() | |
| 106 results = [] | 92 results = [] |
| 107 if status: | 93 if status: |
| 108 for statusline in status.split('\n'): | 94 for statusline in status.splitlines(): |
| 109 m = re.match('^(\w)\t(.+)$', statusline) | 95 m = re.match('^(\w)\t(.+)$', statusline) |
| 110 if not m: | 96 if not m: |
| 111 raise Exception("status currently unsupported: %s" % statusline) | 97 raise gclient_utils.Error( |
| 98 'status currently unsupported: %s' % statusline) |
| 112 results.append(('%s ' % m.group(1), m.group(2))) | 99 results.append(('%s ' % m.group(1), m.group(2))) |
| 113 return results | 100 return results |
| 114 | 101 |
| 115 @staticmethod | 102 @staticmethod |
| 116 def GetEmail(repo_root): | 103 def GetEmail(cwd): |
| 117 """Retrieves the user email address if known.""" | 104 """Retrieves the user email address if known.""" |
| 118 # We could want to look at the svn cred when it has a svn remote but it | 105 # We could want to look at the svn cred when it has a svn remote but it |
| 119 # should be fine for now, users should simply configure their git settings. | 106 # should be fine for now, users should simply configure their git settings. |
| 120 return GIT.Capture(['config', 'user.email'], | 107 try: |
| 121 repo_root, error_ok=True)[0].strip() | 108 return GIT.Capture(['config', 'user.email'], cwd=cwd).strip() |
| 109 except gclient_utils.CheckCallError: |
| 110 return '' |
| 122 | 111 |
| 123 @staticmethod | 112 @staticmethod |
| 124 def ShortBranchName(branch): | 113 def ShortBranchName(branch): |
| 125 """Converts a name like 'refs/heads/foo' to just 'foo'.""" | 114 """Converts a name like 'refs/heads/foo' to just 'foo'.""" |
| 126 return branch.replace('refs/heads/', '') | 115 return branch.replace('refs/heads/', '') |
| 127 | 116 |
| 128 @staticmethod | 117 @staticmethod |
| 129 def GetBranchRef(cwd): | 118 def GetBranchRef(cwd): |
| 130 """Returns the full branch reference, e.g. 'refs/heads/master'.""" | 119 """Returns the full branch reference, e.g. 'refs/heads/master'.""" |
| 131 return GIT.Capture(['symbolic-ref', 'HEAD'], cwd)[0].strip() | 120 return GIT.Capture(['symbolic-ref', 'HEAD'], cwd=cwd).strip() |
| 132 | 121 |
| 133 @staticmethod | 122 @staticmethod |
| 134 def GetBranch(cwd): | 123 def GetBranch(cwd): |
| 135 """Returns the short branch name, e.g. 'master'.""" | 124 """Returns the short branch name, e.g. 'master'.""" |
| 136 return GIT.ShortBranchName(GIT.GetBranchRef(cwd)) | 125 return GIT.ShortBranchName(GIT.GetBranchRef(cwd)) |
| 137 | 126 |
| 138 @staticmethod | 127 @staticmethod |
| 139 def IsGitSvn(cwd): | 128 def IsGitSvn(cwd): |
| 140 """Returns true if this repo looks like it's using git-svn.""" | 129 """Returns true if this repo looks like it's using git-svn.""" |
| 141 # If you have any "svn-remote.*" config keys, we think you're using svn. | 130 # If you have any "svn-remote.*" config keys, we think you're using svn. |
| 142 try: | 131 try: |
| 143 GIT.Capture(['config', '--get-regexp', r'^svn-remote\.'], cwd) | 132 GIT.Capture(['config', '--get-regexp', r'^svn-remote\.'], cwd=cwd) |
| 144 return True | 133 return True |
| 145 except gclient_utils.CheckCallError: | 134 except gclient_utils.CheckCallError: |
| 146 return False | 135 return False |
| 147 | 136 |
| 148 @staticmethod | 137 @staticmethod |
| 149 def GetSVNBranch(cwd): | 138 def GetSVNBranch(cwd): |
| 150 """Returns the svn branch name if found.""" | 139 """Returns the svn branch name if found.""" |
| 151 # Try to figure out which remote branch we're based on. | 140 # Try to figure out which remote branch we're based on. |
| 152 # Strategy: | 141 # Strategy: |
| 153 # 1) find all git-svn branches and note their svn URLs. | 142 # 1) find all git-svn branches and note their svn URLs. |
| 154 # 2) iterate through our branch history and match up the URLs. | 143 # 2) iterate through our branch history and match up the URLs. |
| 155 | 144 |
| 156 # regexp matching the git-svn line that contains the URL. | 145 # regexp matching the git-svn line that contains the URL. |
| 157 git_svn_re = re.compile(r'^\s*git-svn-id: (\S+)@', re.MULTILINE) | 146 git_svn_re = re.compile(r'^\s*git-svn-id: (\S+)@', re.MULTILINE) |
| 158 | 147 |
| 159 # Get the refname and svn url for all refs/remotes/*. | 148 # Get the refname and svn url for all refs/remotes/*. |
| 160 remotes = GIT.Capture( | 149 remotes = GIT.Capture( |
| 161 ['for-each-ref', '--format=%(refname)', 'refs/remotes'], | 150 ['for-each-ref', '--format=%(refname)', 'refs/remotes'], |
| 162 cwd)[0].splitlines() | 151 cwd=cwd).splitlines() |
| 163 svn_refs = {} | 152 svn_refs = {} |
| 164 for ref in remotes: | 153 for ref in remotes: |
| 165 match = git_svn_re.search( | 154 match = git_svn_re.search( |
| 166 GIT.Capture(['cat-file', '-p', ref], cwd)[0]) | 155 GIT.Capture(['cat-file', '-p', ref], cwd=cwd)) |
| 167 # Prefer origin/HEAD over all others. | 156 # Prefer origin/HEAD over all others. |
| 168 if match and (match.group(1) not in svn_refs or | 157 if match and (match.group(1) not in svn_refs or |
| 169 ref == "refs/remotes/origin/HEAD"): | 158 ref == "refs/remotes/origin/HEAD"): |
| 170 svn_refs[match.group(1)] = ref | 159 svn_refs[match.group(1)] = ref |
| 171 | 160 |
| 172 svn_branch = '' | 161 svn_branch = '' |
| 173 if len(svn_refs) == 1: | 162 if len(svn_refs) == 1: |
| 174 # Only one svn branch exists -- seems like a good candidate. | 163 # Only one svn branch exists -- seems like a good candidate. |
| 175 svn_branch = svn_refs.values()[0] | 164 svn_branch = svn_refs.values()[0] |
| 176 elif len(svn_refs) > 1: | 165 elif len(svn_refs) > 1: |
| (...skipping 14 matching lines...) Expand all Loading... |
| 191 return svn_branch | 180 return svn_branch |
| 192 | 181 |
| 193 @staticmethod | 182 @staticmethod |
| 194 def FetchUpstreamTuple(cwd): | 183 def FetchUpstreamTuple(cwd): |
| 195 """Returns a tuple containg remote and remote ref, | 184 """Returns a tuple containg remote and remote ref, |
| 196 e.g. 'origin', 'refs/heads/master' | 185 e.g. 'origin', 'refs/heads/master' |
| 197 Tries to be intelligent and understand git-svn. | 186 Tries to be intelligent and understand git-svn. |
| 198 """ | 187 """ |
| 199 remote = '.' | 188 remote = '.' |
| 200 branch = GIT.GetBranch(cwd) | 189 branch = GIT.GetBranch(cwd) |
| 201 upstream_branch = None | 190 try: |
| 202 upstream_branch = GIT.Capture( | 191 upstream_branch = GIT.Capture( |
| 203 ['config', 'branch.%s.merge' % branch], in_directory=cwd, | 192 ['config', 'branch.%s.merge' % branch], cwd=cwd).strip() |
| 204 error_ok=True)[0].strip() | 193 except gclient_utils.Error: |
| 194 upstream_branch = None |
| 205 if upstream_branch: | 195 if upstream_branch: |
| 206 remote = GIT.Capture( | 196 try: |
| 207 ['config', 'branch.%s.remote' % branch], | 197 remote = GIT.Capture( |
| 208 in_directory=cwd, error_ok=True)[0].strip() | 198 ['config', 'branch.%s.remote' % branch], cwd=cwd).strip() |
| 199 except gclient_utils.Error: |
| 200 pass |
| 209 else: | 201 else: |
| 210 # Fall back on trying a git-svn upstream branch. | 202 # Fall back on trying a git-svn upstream branch. |
| 211 if GIT.IsGitSvn(cwd): | 203 if GIT.IsGitSvn(cwd): |
| 212 upstream_branch = GIT.GetSVNBranch(cwd) | 204 upstream_branch = GIT.GetSVNBranch(cwd) |
| 213 else: | 205 else: |
| 214 # Else, try to guess the origin remote. | 206 # Else, try to guess the origin remote. |
| 215 remote_branches = GIT.Capture( | 207 remote_branches = GIT.Capture(['branch', '-r'], cwd=cwd).split() |
| 216 ['branch', '-r'], in_directory=cwd)[0].split() | |
| 217 if 'origin/master' in remote_branches: | 208 if 'origin/master' in remote_branches: |
| 218 # Fall back on origin/master if it exits. | 209 # Fall back on origin/master if it exits. |
| 219 remote = 'origin' | 210 remote = 'origin' |
| 220 upstream_branch = 'refs/heads/master' | 211 upstream_branch = 'refs/heads/master' |
| 221 elif 'origin/trunk' in remote_branches: | 212 elif 'origin/trunk' in remote_branches: |
| 222 # Fall back on origin/trunk if it exists. Generally a shared | 213 # Fall back on origin/trunk if it exists. Generally a shared |
| 223 # git-svn clone | 214 # git-svn clone |
| 224 remote = 'origin' | 215 remote = 'origin' |
| 225 upstream_branch = 'refs/heads/trunk' | 216 upstream_branch = 'refs/heads/trunk' |
| 226 else: | 217 else: |
| (...skipping 20 matching lines...) Expand all Loading... |
| 247 if not branch: | 238 if not branch: |
| 248 branch = GIT.GetUpstreamBranch(cwd) | 239 branch = GIT.GetUpstreamBranch(cwd) |
| 249 command = ['diff', '-p', '--no-prefix', '--no-ext-diff', | 240 command = ['diff', '-p', '--no-prefix', '--no-ext-diff', |
| 250 branch + "..." + branch_head] | 241 branch + "..." + branch_head] |
| 251 if not full_move: | 242 if not full_move: |
| 252 command.append('-C') | 243 command.append('-C') |
| 253 # TODO(maruel): --binary support. | 244 # TODO(maruel): --binary support. |
| 254 if files: | 245 if files: |
| 255 command.append('--') | 246 command.append('--') |
| 256 command.extend(files) | 247 command.extend(files) |
| 257 diff = GIT.Capture(command, cwd)[0].splitlines(True) | 248 diff = GIT.Capture(command, cwd=cwd).splitlines(True) |
| 258 for i in range(len(diff)): | 249 for i in range(len(diff)): |
| 259 # In the case of added files, replace /dev/null with the path to the | 250 # In the case of added files, replace /dev/null with the path to the |
| 260 # file being added. | 251 # file being added. |
| 261 if diff[i].startswith('--- /dev/null'): | 252 if diff[i].startswith('--- /dev/null'): |
| 262 diff[i] = '--- %s' % diff[i+1][4:] | 253 diff[i] = '--- %s' % diff[i+1][4:] |
| 263 return ''.join(diff) | 254 return ''.join(diff) |
| 264 | 255 |
| 265 @staticmethod | 256 @staticmethod |
| 266 def GetDifferentFiles(cwd, branch=None, branch_head='HEAD'): | 257 def GetDifferentFiles(cwd, branch=None, branch_head='HEAD'): |
| 267 """Returns the list of modified files between two branches.""" | 258 """Returns the list of modified files between two branches.""" |
| 268 if not branch: | 259 if not branch: |
| 269 branch = GIT.GetUpstreamBranch(cwd) | 260 branch = GIT.GetUpstreamBranch(cwd) |
| 270 command = ['diff', '--name-only', branch + "..." + branch_head] | 261 command = ['diff', '--name-only', branch + "..." + branch_head] |
| 271 return GIT.Capture(command, cwd)[0].splitlines(False) | 262 return GIT.Capture(command, cwd=cwd).splitlines(False) |
| 272 | 263 |
| 273 @staticmethod | 264 @staticmethod |
| 274 def GetPatchName(cwd): | 265 def GetPatchName(cwd): |
| 275 """Constructs a name for this patch.""" | 266 """Constructs a name for this patch.""" |
| 276 short_sha = GIT.Capture(['rev-parse', '--short=4', 'HEAD'], cwd)[0].strip() | 267 short_sha = GIT.Capture(['rev-parse', '--short=4', 'HEAD'], cwd=cwd).strip() |
| 277 return "%s#%s" % (GIT.GetBranch(cwd), short_sha) | 268 return "%s#%s" % (GIT.GetBranch(cwd), short_sha) |
| 278 | 269 |
| 279 @staticmethod | 270 @staticmethod |
| 280 def GetCheckoutRoot(path): | 271 def GetCheckoutRoot(cwd): |
| 281 """Returns the top level directory of a git checkout as an absolute path. | 272 """Returns the top level directory of a git checkout as an absolute path. |
| 282 """ | 273 """ |
| 283 root = GIT.Capture(['rev-parse', '--show-cdup'], path)[0].strip() | 274 root = GIT.Capture(['rev-parse', '--show-cdup'], cwd=cwd).strip() |
| 284 return os.path.abspath(os.path.join(path, root)) | 275 return os.path.abspath(os.path.join(cwd, root)) |
| 285 | 276 |
| 286 @staticmethod | 277 @staticmethod |
| 287 def AssertVersion(min_version): | 278 def AssertVersion(min_version): |
| 288 """Asserts git's version is at least min_version.""" | 279 """Asserts git's version is at least min_version.""" |
| 289 def only_int(val): | 280 def only_int(val): |
| 290 if val.isdigit(): | 281 if val.isdigit(): |
| 291 return int(val) | 282 return int(val) |
| 292 else: | 283 else: |
| 293 return 0 | 284 return 0 |
| 294 current_version = GIT.Capture(['--version'])[0].split()[-1] | 285 current_version = GIT.Capture(['--version']).split()[-1] |
| 295 current_version_list = map(only_int, current_version.split('.')) | 286 current_version_list = map(only_int, current_version.split('.')) |
| 296 for min_ver in map(int, min_version.split('.')): | 287 for min_ver in map(int, min_version.split('.')): |
| 297 ver = current_version_list.pop(0) | 288 ver = current_version_list.pop(0) |
| 298 if ver < min_ver: | 289 if ver < min_ver: |
| 299 return (False, current_version) | 290 return (False, current_version) |
| 300 elif ver > min_ver: | 291 elif ver > min_ver: |
| 301 return (True, current_version) | 292 return (True, current_version) |
| 302 return (True, current_version) | 293 return (True, current_version) |
| 303 | 294 |
| 304 | 295 |
| (...skipping 517 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 822 if not SVN.current_version: | 813 if not SVN.current_version: |
| 823 SVN.current_version = SVN.Capture(['--version']).split()[2] | 814 SVN.current_version = SVN.Capture(['--version']).split()[2] |
| 824 current_version_list = map(only_int, SVN.current_version.split('.')) | 815 current_version_list = map(only_int, SVN.current_version.split('.')) |
| 825 for min_ver in map(int, min_version.split('.')): | 816 for min_ver in map(int, min_version.split('.')): |
| 826 ver = current_version_list.pop(0) | 817 ver = current_version_list.pop(0) |
| 827 if ver < min_ver: | 818 if ver < min_ver: |
| 828 return (False, SVN.current_version) | 819 return (False, SVN.current_version) |
| 829 elif ver > min_ver: | 820 elif ver > min_ver: |
| 830 return (True, SVN.current_version) | 821 return (True, SVN.current_version) |
| 831 return (True, SVN.current_version) | 822 return (True, SVN.current_version) |
| OLD | NEW |