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

Side by Side Diff: scm.py

Issue 391052: Group SCM-specific functions in classes to simplify generalization of the interface. (Closed)
Patch Set: One more fix Created 11 years, 1 month 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
OLDNEW
1 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
Dirk Pranke 2009/11/13 22:42:54 Is there real value in keeping these in one file?
M-A Ruel 2009/11/14 01:15:00 Yes that's probably what I'm going to do but not n
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 functions.""" 5 """SCM-specific utility classes."""
6 6
7 import os 7 import os
8 import re 8 import re
9 import subprocess 9 import subprocess
10 import sys 10 import sys
11 import tempfile
11 import xml.dom.minidom 12 import xml.dom.minidom
12 13
13 import gclient_utils 14 import gclient_utils
14 15
15 16
16 SVN_COMMAND = "svn" 17 class GIT(object):
17 GIT_COMMAND = "git" 18 COMMAND = "git"
18 19
19 # ----------------------------------------------------------------------------- 20 @staticmethod
20 # Git utils: 21 def Capture(args, in_directory=None, print_error=True):
21 22 """Runs git, capturing output sent to stdout as a string.
22 23
23 def CaptureGit(args, in_directory=None, print_error=True): 24 Args:
24 """Runs git, capturing output sent to stdout as a string. 25 args: A sequence of command line parameters to be passed to git.
25 26 in_directory: The directory where git is to be run.
26 Args: 27
27 args: A sequence of command line parameters to be passed to git. 28 Returns:
28 in_directory: The directory where git is to be run. 29 The output sent to stdout as a string.
29 30 """
30 Returns: 31 c = [GIT.COMMAND]
31 The output sent to stdout as a string. 32 c.extend(args)
32 """ 33
33 c = [GIT_COMMAND] 34 # *Sigh*: Windows needs shell=True, or else it won't search %PATH% for
34 c.extend(args) 35 # the git.exe executable, but shell=True makes subprocess on Linux fail
35 36 # when it's called with a list because it only tries to execute the
36 # *Sigh*: Windows needs shell=True, or else it won't search %PATH% for 37 # first string ("git").
37 # the git.exe executable, but shell=True makes subprocess on Linux fail 38 stderr = None
38 # when it's called with a list because it only tries to execute the 39 if not print_error:
39 # first string ("git"). 40 stderr = subprocess.PIPE
40 stderr = None 41 return subprocess.Popen(c,
41 if not print_error: 42 cwd=in_directory,
42 stderr = subprocess.PIPE 43 shell=sys.platform.startswith('win'),
43 return subprocess.Popen(c, 44 stdout=subprocess.PIPE,
44 cwd=in_directory, 45 stderr=stderr).communicate()[0]
45 shell=sys.platform.startswith('win'), 46
46 stdout=subprocess.PIPE, 47
47 stderr=stderr).communicate()[0] 48 @staticmethod
48 49 def CaptureStatus(files, upstream_branch='origin'):
49 50 """Returns git status.
50 def CaptureGitStatus(files, upstream_branch='origin'): 51
51 """Returns git status. 52 @files can be a string (one file) or a list of files.
52 53
53 @files can be a string (one file) or a list of files. 54 Returns an array of (status, file) tuples."""
54 55 command = ["diff", "--name-status", "-r", "%s.." % upstream_branch]
55 Returns an array of (status, file) tuples.""" 56 if not files:
56 command = ["diff", "--name-status", "-r", "%s.." % upstream_branch] 57 pass
57 if not files: 58 elif isinstance(files, basestring):
58 pass 59 command.append(files)
59 elif isinstance(files, basestring): 60 else:
60 command.append(files) 61 command.extend(files)
61 else: 62
62 command.extend(files) 63 status = GIT.Capture(command).rstrip()
63 64 results = []
64 status = CaptureGit(command).rstrip() 65 if status:
65 results = [] 66 for statusline in status.split('\n'):
66 if status: 67 m = re.match('^(\w)\t(.+)$', statusline)
67 for statusline in status.split('\n'): 68 if not m:
68 m = re.match('^(\w)\t(.+)$', statusline) 69 raise Exception("status currently unsupported: %s" % statusline)
69 if not m: 70 results.append(('%s ' % m.group(1), m.group(2)))
70 raise Exception("status currently unsupported: %s" % statusline) 71 return results
71 results.append(('%s ' % m.group(1), m.group(2))) 72
72 return results 73
73 74 class SVN(object):
74 75 COMMAND = "svn"
75 # ----------------------------------------------------------------------------- 76
76 # SVN utils: 77 @staticmethod
77 78 def Run(args, in_directory):
78 79 """Runs svn, sending output to stdout.
79 def RunSVN(args, in_directory): 80
80 """Runs svn, sending output to stdout. 81 Args:
81 82 args: A sequence of command line parameters to be passed to svn.
82 Args: 83 in_directory: The directory where svn is to be run.
83 args: A sequence of command line parameters to be passed to svn. 84
84 in_directory: The directory where svn is to be run. 85 Raises:
85 86 Error: An error occurred while running the svn command.
86 Raises: 87 """
87 Error: An error occurred while running the svn command. 88 c = [SVN.COMMAND]
88 """ 89 c.extend(args)
89 c = [SVN_COMMAND] 90
90 c.extend(args) 91 gclient_utils.SubprocessCall(c, in_directory)
91 92
92 gclient_utils.SubprocessCall(c, in_directory) 93 @staticmethod
93 94 def Capture(args, in_directory=None, print_error=True):
94 95 """Runs svn, capturing output sent to stdout as a string.
95 def CaptureSVN(args, in_directory=None, print_error=True): 96
96 """Runs svn, capturing output sent to stdout as a string. 97 Args:
97 98 args: A sequence of command line parameters to be passed to svn.
98 Args: 99 in_directory: The directory where svn is to be run.
99 args: A sequence of command line parameters to be passed to svn. 100
100 in_directory: The directory where svn is to be run. 101 Returns:
101 102 The output sent to stdout as a string.
102 Returns: 103 """
103 The output sent to stdout as a string. 104 c = [SVN.COMMAND]
104 """ 105 c.extend(args)
105 c = [SVN_COMMAND] 106
106 c.extend(args) 107 # *Sigh*: Windows needs shell=True, or else it won't search %PATH% for
107 108 # the svn.exe executable, but shell=True makes subprocess on Linux fail
108 # *Sigh*: Windows needs shell=True, or else it won't search %PATH% for 109 # when it's called with a list because it only tries to execute the
109 # the svn.exe executable, but shell=True makes subprocess on Linux fail 110 # first string ("svn").
110 # when it's called with a list because it only tries to execute the 111 stderr = None
111 # first string ("svn"). 112 if not print_error:
112 stderr = None 113 stderr = subprocess.PIPE
113 if not print_error: 114 return subprocess.Popen(c,
114 stderr = subprocess.PIPE 115 cwd=in_directory,
115 return subprocess.Popen(c, 116 shell=(sys.platform == 'win32'),
116 cwd=in_directory, 117 stdout=subprocess.PIPE,
117 shell=(sys.platform == 'win32'), 118 stderr=stderr).communicate()[0]
118 stdout=subprocess.PIPE, 119
119 stderr=stderr).communicate()[0] 120 @staticmethod
120 121 def RunAndGetFileList(options, args, in_directory, file_list):
121 122 """Runs svn checkout, update, or status, output to stdout.
122 def RunSVNAndGetFileList(options, args, in_directory, file_list): 123
123 """Runs svn checkout, update, or status, output to stdout. 124 The first item in args must be either "checkout", "update", or "status".
124 125
125 The first item in args must be either "checkout", "update", or "status". 126 svn's stdout is parsed to collect a list of files checked out or updated.
126 127 These files are appended to file_list. svn's stdout is also printed to
127 svn's stdout is parsed to collect a list of files checked out or updated. 128 sys.stdout as in Run.
128 These files are appended to file_list. svn's stdout is also printed to 129
129 sys.stdout as in RunSVN. 130 Args:
130 131 options: command line options to gclient
131 Args: 132 args: A sequence of command line parameters to be passed to svn.
132 options: command line options to gclient 133 in_directory: The directory where svn is to be run.
133 args: A sequence of command line parameters to be passed to svn. 134
134 in_directory: The directory where svn is to be run. 135 Raises:
135 136 Error: An error occurred while running the svn command.
136 Raises: 137 """
137 Error: An error occurred while running the svn command. 138 command = [SVN.COMMAND]
138 """ 139 command.extend(args)
139 command = [SVN_COMMAND] 140
140 command.extend(args) 141 # svn update and svn checkout use the same pattern: the first three columns
141 142 # are for file status, property status, and lock status. This is followed
142 # svn update and svn checkout use the same pattern: the first three columns 143 # by two spaces, and then the path to the file.
143 # are for file status, property status, and lock status. This is followed 144 update_pattern = '^... (.*)$'
144 # by two spaces, and then the path to the file. 145
145 update_pattern = '^... (.*)$' 146 # The first three columns of svn status are the same as for svn update and
146 147 # svn checkout. The next three columns indicate addition-with-history,
147 # The first three columns of svn status are the same as for svn update and 148 # switch, and remote lock status. This is followed by one space, and then
148 # svn checkout. The next three columns indicate addition-with-history, 149 # the path to the file.
149 # switch, and remote lock status. This is followed by one space, and then 150 status_pattern = '^...... (.*)$'
150 # the path to the file. 151
151 status_pattern = '^...... (.*)$' 152 # args[0] must be a supported command. This will blow up if it's something
152 153 # else, which is good. Note that the patterns are only effective when
153 # args[0] must be a supported command. This will blow up if it's something 154 # these commands are used in their ordinary forms, the patterns are invalid
154 # else, which is good. Note that the patterns are only effective when 155 # for "svn status --show-updates", for example.
155 # these commands are used in their ordinary forms, the patterns are invalid 156 pattern = {
156 # for "svn status --show-updates", for example. 157 'checkout': update_pattern,
157 pattern = { 158 'status': status_pattern,
158 'checkout': update_pattern, 159 'update': update_pattern,
159 'status': status_pattern, 160 }[args[0]]
160 'update': update_pattern, 161
161 }[args[0]] 162 compiled_pattern = re.compile(pattern)
162 163
163 compiled_pattern = re.compile(pattern) 164 def CaptureMatchingLines(line):
164 165 match = compiled_pattern.search(line)
165 def CaptureMatchingLines(line): 166 if match:
166 match = compiled_pattern.search(line) 167 file_list.append(match.group(1))
167 if match: 168
168 file_list.append(match.group(1)) 169 SVN.RunAndFilterOutput(args,
169 170 in_directory,
170 RunSVNAndFilterOutput(args, 171 options.verbose,
171 in_directory, 172 True,
172 options.verbose, 173 CaptureMatchingLines)
173 True, 174
174 CaptureMatchingLines) 175 @staticmethod
175 176 def RunAndFilterOutput(args,
176 def RunSVNAndFilterOutput(args, 177 in_directory,
177 in_directory, 178 print_messages,
178 print_messages, 179 print_stdout,
179 print_stdout, 180 filter):
180 filter): 181 """Runs svn checkout, update, status, or diff, optionally outputting
181 """Runs svn checkout, update, status, or diff, optionally outputting 182 to stdout.
182 to stdout. 183
183 184 The first item in args must be either "checkout", "update",
184 The first item in args must be either "checkout", "update", 185 "status", or "diff".
185 "status", or "diff". 186
186 187 svn's stdout is passed line-by-line to the given filter function. If
187 svn's stdout is passed line-by-line to the given filter function. If 188 print_stdout is true, it is also printed to sys.stdout as in Run.
188 print_stdout is true, it is also printed to sys.stdout as in RunSVN. 189
189 190 Args:
190 Args: 191 args: A sequence of command line parameters to be passed to svn.
191 args: A sequence of command line parameters to be passed to svn. 192 in_directory: The directory where svn is to be run.
192 in_directory: The directory where svn is to be run. 193 print_messages: Whether to print status messages to stdout about
193 print_messages: Whether to print status messages to stdout about 194 which Subversion commands are being run.
194 which Subversion commands are being run. 195 print_stdout: Whether to forward Subversion's output to stdout.
195 print_stdout: Whether to forward Subversion's output to stdout. 196 filter: A function taking one argument (a string) which will be
196 filter: A function taking one argument (a string) which will be 197 passed each line (with the ending newline character removed) of
197 passed each line (with the ending newline character removed) of 198 Subversion's output for filtering.
198 Subversion's output for filtering. 199
199 200 Raises:
200 Raises: 201 Error: An error occurred while running the svn command.
201 Error: An error occurred while running the svn command. 202 """
202 """ 203 command = [SVN.COMMAND]
203 command = [SVN_COMMAND] 204 command.extend(args)
204 command.extend(args) 205
205 206 gclient_utils.SubprocessCallAndFilter(command,
206 gclient_utils.SubprocessCallAndFilter(command, 207 in_directory,
207 in_directory, 208 print_messages,
208 print_messages, 209 print_stdout,
209 print_stdout, 210 filter=filter)
210 filter=filter) 211
211 212 @staticmethod
212 def CaptureSVNInfo(relpath, in_directory=None, print_error=True): 213 def CaptureInfo(relpath, in_directory=None, print_error=True):
213 """Returns a dictionary from the svn info output for the given file. 214 """Returns a dictionary from the svn info output for the given file.
214 215
215 Args: 216 Args:
216 relpath: The directory where the working copy resides relative to 217 relpath: The directory where the working copy resides relative to
217 the directory given by in_directory. 218 the directory given by in_directory.
218 in_directory: The directory where svn is to be run. 219 in_directory: The directory where svn is to be run.
219 """ 220 """
220 output = CaptureSVN(["info", "--xml", relpath], in_directory, print_error) 221 output = SVN.Capture(["info", "--xml", relpath], in_directory, print_error)
221 dom = gclient_utils.ParseXML(output) 222 dom = gclient_utils.ParseXML(output)
222 result = {} 223 result = {}
223 if dom: 224 if dom:
224 GetNamedNodeText = gclient_utils.GetNamedNodeText 225 GetNamedNodeText = gclient_utils.GetNamedNodeText
225 GetNodeNamedAttributeText = gclient_utils.GetNodeNamedAttributeText 226 GetNodeNamedAttributeText = gclient_utils.GetNodeNamedAttributeText
226 def C(item, f): 227 def C(item, f):
227 if item is not None: return f(item) 228 if item is not None: return f(item)
228 # /info/entry/ 229 # /info/entry/
229 # url 230 # url
230 # reposityory/(root|uuid) 231 # reposityory/(root|uuid)
231 # wc-info/(schedule|depth) 232 # wc-info/(schedule|depth)
232 # commit/(author|date) 233 # commit/(author|date)
233 # str() the results because they may be returned as Unicode, which 234 # str() the results because they may be returned as Unicode, which
234 # interferes with the higher layers matching up things in the deps 235 # interferes with the higher layers matching up things in the deps
235 # dictionary. 236 # dictionary.
236 # TODO(maruel): Fix at higher level instead (!) 237 # TODO(maruel): Fix at higher level instead (!)
237 result['Repository Root'] = C(GetNamedNodeText(dom, 'root'), str) 238 result['Repository Root'] = C(GetNamedNodeText(dom, 'root'), str)
238 result['URL'] = C(GetNamedNodeText(dom, 'url'), str) 239 result['URL'] = C(GetNamedNodeText(dom, 'url'), str)
239 result['UUID'] = C(GetNamedNodeText(dom, 'uuid'), str) 240 result['UUID'] = C(GetNamedNodeText(dom, 'uuid'), str)
240 result['Revision'] = C(GetNodeNamedAttributeText(dom, 'entry', 'revision'), 241 result['Revision'] = C(GetNodeNamedAttributeText(dom, 'entry',
241 int) 242 'revision'),
242 result['Node Kind'] = C(GetNodeNamedAttributeText(dom, 'entry', 'kind'), 243 int)
243 str) 244 result['Node Kind'] = C(GetNodeNamedAttributeText(dom, 'entry', 'kind'),
244 result['Schedule'] = C(GetNamedNodeText(dom, 'schedule'), str) 245 str)
245 result['Path'] = C(GetNodeNamedAttributeText(dom, 'entry', 'path'), str) 246 # Differs across versions.
246 result['Copied From URL'] = C(GetNamedNodeText(dom, 'copy-from-url'), str) 247 if result['Node Kind'] == 'dir':
M-A Ruel 2009/11/13 21:41:07 This is new and simplify the expectations.
247 result['Copied From Rev'] = C(GetNamedNodeText(dom, 'copy-from-rev'), str) 248 result['Node Kind'] = 'directory'
248 return result 249 result['Schedule'] = C(GetNamedNodeText(dom, 'schedule'), str)
249 250 result['Path'] = C(GetNodeNamedAttributeText(dom, 'entry', 'path'), str)
250 251 result['Copied From URL'] = C(GetNamedNodeText(dom, 'copy-from-url'), str)
251 def CaptureSVNHeadRevision(url): 252 result['Copied From Rev'] = C(GetNamedNodeText(dom, 'copy-from-rev'), str)
252 """Get the head revision of a SVN repository. 253 return result
253 254
254 Returns: 255 @staticmethod
255 Int head revision 256 def CaptureHeadRevision(url):
256 """ 257 """Get the head revision of a SVN repository.
257 info = CaptureSVN(["info", "--xml", url], os.getcwd()) 258
258 dom = xml.dom.minidom.parseString(info) 259 Returns:
259 return dom.getElementsByTagName('entry')[0].getAttribute('revision') 260 Int head revision
260 261 """
261 262 info = SVN.Capture(["info", "--xml", url], os.getcwd())
262 def CaptureSVNStatus(files): 263 dom = xml.dom.minidom.parseString(info)
263 """Returns the svn 1.5 svn status emulated output. 264 return dom.getElementsByTagName('entry')[0].getAttribute('revision')
264 265
265 @files can be a string (one file) or a list of files. 266 @staticmethod
266 267 def CaptureStatus(files):
267 Returns an array of (status, file) tuples.""" 268 """Returns the svn 1.5 svn status emulated output.
268 command = ["status", "--xml"] 269
269 if not files: 270 @files can be a string (one file) or a list of files.
270 pass 271
271 elif isinstance(files, basestring): 272 Returns an array of (status, file) tuples."""
272 command.append(files) 273 command = ["status", "--xml"]
273 else: 274 if not files:
274 command.extend(files) 275 pass
275 276 elif isinstance(files, basestring):
276 status_letter = { 277 command.append(files)
277 None: ' ', 278 else:
278 '': ' ', 279 command.extend(files)
279 'added': 'A', 280
280 'conflicted': 'C', 281 status_letter = {
281 'deleted': 'D', 282 None: ' ',
282 'external': 'X', 283 '': ' ',
283 'ignored': 'I', 284 'added': 'A',
284 'incomplete': '!', 285 'conflicted': 'C',
285 'merged': 'G', 286 'deleted': 'D',
286 'missing': '!', 287 'external': 'X',
287 'modified': 'M', 288 'ignored': 'I',
288 'none': ' ', 289 'incomplete': '!',
289 'normal': ' ', 290 'merged': 'G',
290 'obstructed': '~', 291 'missing': '!',
291 'replaced': 'R', 292 'modified': 'M',
292 'unversioned': '?', 293 'none': ' ',
293 } 294 'normal': ' ',
294 dom = gclient_utils.ParseXML(CaptureSVN(command)) 295 'obstructed': '~',
295 results = [] 296 'replaced': 'R',
296 if dom: 297 'unversioned': '?',
297 # /status/target/entry/(wc-status|commit|author|date) 298 }
298 for target in dom.getElementsByTagName('target'): 299 dom = gclient_utils.ParseXML(SVN.Capture(command))
299 for entry in target.getElementsByTagName('entry'): 300 results = []
300 file_path = entry.getAttribute('path') 301 if dom:
301 wc_status = entry.getElementsByTagName('wc-status') 302 # /status/target/entry/(wc-status|commit|author|date)
302 assert len(wc_status) == 1 303 for target in dom.getElementsByTagName('target'):
303 # Emulate svn 1.5 status ouput... 304 #base_path = target.getAttribute('path')
304 statuses = [' '] * 7 305 for entry in target.getElementsByTagName('entry'):
305 # Col 0 306 file_path = entry.getAttribute('path')
306 xml_item_status = wc_status[0].getAttribute('item') 307 wc_status = entry.getElementsByTagName('wc-status')
307 if xml_item_status in status_letter: 308 assert len(wc_status) == 1
308 statuses[0] = status_letter[xml_item_status] 309 # Emulate svn 1.5 status ouput...
309 else: 310 statuses = [' '] * 7
310 raise Exception('Unknown item status "%s"; please implement me!' % 311 # Col 0
311 xml_item_status) 312 xml_item_status = wc_status[0].getAttribute('item')
312 # Col 1 313 if xml_item_status in status_letter:
313 xml_props_status = wc_status[0].getAttribute('props') 314 statuses[0] = status_letter[xml_item_status]
314 if xml_props_status == 'modified': 315 else:
315 statuses[1] = 'M' 316 raise Exception('Unknown item status "%s"; please implement me!' %
316 elif xml_props_status == 'conflicted': 317 xml_item_status)
317 statuses[1] = 'C' 318 # Col 1
318 elif (not xml_props_status or xml_props_status == 'none' or 319 xml_props_status = wc_status[0].getAttribute('props')
319 xml_props_status == 'normal'): 320 if xml_props_status == 'modified':
320 pass 321 statuses[1] = 'M'
321 else: 322 elif xml_props_status == 'conflicted':
322 raise Exception('Unknown props status "%s"; please implement me!' % 323 statuses[1] = 'C'
323 xml_props_status) 324 elif (not xml_props_status or xml_props_status == 'none' or
324 # Col 2 325 xml_props_status == 'normal'):
325 if wc_status[0].getAttribute('wc-locked') == 'true': 326 pass
326 statuses[2] = 'L' 327 else:
327 # Col 3 328 raise Exception('Unknown props status "%s"; please implement me!' %
328 if wc_status[0].getAttribute('copied') == 'true': 329 xml_props_status)
329 statuses[3] = '+' 330 # Col 2
330 # Col 4 331 if wc_status[0].getAttribute('wc-locked') == 'true':
331 if wc_status[0].getAttribute('switched') == 'true': 332 statuses[2] = 'L'
332 statuses[4] = 'S' 333 # Col 3
333 # TODO(maruel): Col 5 and 6 334 if wc_status[0].getAttribute('copied') == 'true':
334 item = (''.join(statuses), file_path) 335 statuses[3] = '+'
335 results.append(item) 336 # Col 4
336 return results 337 if wc_status[0].getAttribute('switched') == 'true':
338 statuses[4] = 'S'
339 # TODO(maruel): Col 5 and 6
340 item = (''.join(statuses), file_path)
341 results.append(item)
342 return results
343
344 @staticmethod
345 def IsMoved(filename):
346 """Determine if a file has been added through svn mv"""
347 info = SVN.CaptureInfo(filename)
348 return (info.get('Copied From URL') and
349 info.get('Copied From Rev') and
350 info.get('Schedule') == 'add')
351
352 @staticmethod
353 def GetFileProperty(file, property_name):
354 """Returns the value of an SVN property for the given file.
355
356 Args:
357 file: The file to check
358 property_name: The name of the SVN property, e.g. "svn:mime-type"
359
360 Returns:
361 The value of the property, which will be the empty string if the property
362 is not set on the file. If the file is not under version control, the
363 empty string is also returned.
364 """
365 output = SVN.Run(["propget", property_name, file], None)
366 if (output.startswith("svn: ") and
367 output.endswith("is not under version control")):
368 return ""
369 else:
370 return output
371
372 @staticmethod
373 def DiffItem(filename):
M-A Ruel 2009/11/13 21:41:07 Extracted from trychange.py
374 """Diff a single file"""
375 # Use svn info output instead of os.path.isdir because the latter fails
376 # when the file is deleted.
377 if SVN.CaptureInfo(filename).get("Node Kind") == "directory":
378 return None
379 # If the user specified a custom diff command in their svn config file,
380 # then it'll be used when we do svn diff, which we don't want to happen
381 # since we want the unified diff. Using --diff-cmd=diff doesn't always
382 # work, since they can have another diff executable in their path that
383 # gives different line endings. So we use a bogus temp directory as the
384 # config directory, which gets around these problems.
385 if sys.platform.startswith("win"):
386 parent_dir = tempfile.gettempdir()
387 else:
388 parent_dir = sys.path[0] # tempdir is not secure.
389 bogus_dir = os.path.join(parent_dir, "temp_svn_config")
390 if not os.path.exists(bogus_dir):
391 os.mkdir(bogus_dir)
392 # Grabs the diff data.
393 data = SVN.Capture(["diff", "--config-dir", bogus_dir, filename], None)
394
395 # We know the diff will be incorrectly formatted. Fix it.
396 if SVN.IsMoved(filename):
397 # The file is "new" in the patch sense. Generate a homebrew diff.
398 # We can't use ReadFile() since it's not using binary mode.
399 file_handle = open(filename, 'rb')
400 file_content = file_handle.read()
401 file_handle.close()
402 # Prepend '+' to every lines.
403 file_content = ['+' + i for i in file_content.splitlines(True)]
404 nb_lines = len(file_content)
405 # We need to use / since patch on unix will fail otherwise.
406 filename = filename.replace('\\', '/')
407 data = "Index: %s\n" % filename
408 data += ("============================================================="
409 "======\n")
410 # Note: Should we use /dev/null instead?
411 data += "--- %s\n" % filename
412 data += "+++ %s\n" % filename
413 data += "@@ -0,0 +1,%d @@\n" % nb_lines
414 data += ''.join(file_content)
415 return data
OLDNEW
« gclient.py ('K') | « revert.py ('k') | tests/gcl_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698