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 |