comparison mercurial/patch.py @ 14366:992a7e398ddd

patch: stop changing current directory before patching _applydiff() patcher argument was added to help hgsubversion like extension monkeypatching the patching process. While it could be removed at this point, I prefer to leave it until patch.py is completely refactored and there is a valid and tested alternative.
author Patrick Mezard <pmezard@gmail.com>
date Wed, 18 May 2011 23:48:13 +0200
parents 077cdf172580
children 468d7d1744b4
comparison
equal deleted inserted replaced
14365:a8e3931e3fb5 14366:992a7e398ddd
400 class fsbackend(abstractbackend): 400 class fsbackend(abstractbackend):
401 def __init__(self, ui, basedir): 401 def __init__(self, ui, basedir):
402 super(fsbackend, self).__init__(ui) 402 super(fsbackend, self).__init__(ui)
403 self.opener = scmutil.opener(basedir) 403 self.opener = scmutil.opener(basedir)
404 404
405 def _join(self, f):
406 return os.path.join(self.opener.base, f)
407
405 def readlines(self, fname): 408 def readlines(self, fname):
406 if os.path.islink(fname): 409 if os.path.islink(self._join(fname)):
407 return [os.readlink(fname)] 410 return [os.readlink(self._join(fname))]
408 fp = self.opener(fname, 'r') 411 fp = self.opener(fname, 'r')
409 try: 412 try:
410 return list(fp) 413 return list(fp)
411 finally: 414 finally:
412 fp.close() 415 fp.close()
414 def writelines(self, fname, lines): 417 def writelines(self, fname, lines):
415 # Ensure supplied data ends in fname, being a regular file or 418 # Ensure supplied data ends in fname, being a regular file or
416 # a symlink. _updatedir will -too magically- take care 419 # a symlink. _updatedir will -too magically- take care
417 # of setting it to the proper type afterwards. 420 # of setting it to the proper type afterwards.
418 st_mode = None 421 st_mode = None
419 islink = os.path.islink(fname) 422 islink = os.path.islink(self._join(fname))
420 if islink: 423 if islink:
421 fp = cStringIO.StringIO() 424 fp = cStringIO.StringIO()
422 else: 425 else:
423 try: 426 try:
424 st_mode = os.lstat(fname).st_mode & 0777 427 st_mode = os.lstat(self._join(fname)).st_mode & 0777
425 except OSError, e: 428 except OSError, e:
426 if e.errno != errno.ENOENT: 429 if e.errno != errno.ENOENT:
427 raise 430 raise
428 fp = self.opener(fname, 'w') 431 fp = self.opener(fname, 'w')
429 try: 432 try:
430 fp.writelines(lines) 433 fp.writelines(lines)
431 if islink: 434 if islink:
432 self.opener.symlink(fp.getvalue(), fname) 435 self.opener.symlink(fp.getvalue(), fname)
433 if st_mode is not None: 436 if st_mode is not None:
434 os.chmod(fname, st_mode) 437 os.chmod(self._join(fname), st_mode)
435 finally: 438 finally:
436 fp.close() 439 fp.close()
437 440
438 def unlink(self, fname): 441 def unlink(self, fname):
439 os.unlink(fname) 442 os.unlink(self._join(fname))
440 443
441 def writerej(self, fname, failed, total, lines): 444 def writerej(self, fname, failed, total, lines):
442 fname = fname + ".rej" 445 fname = fname + ".rej"
443 self.ui.warn( 446 self.ui.warn(
444 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") % 447 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
463 _("cannot create %s: unable to create destination directory") 466 _("cannot create %s: unable to create destination directory")
464 % dst) 467 % dst)
465 util.copyfile(abssrc, absdst) 468 util.copyfile(abssrc, absdst)
466 469
467 def exists(self, fname): 470 def exists(self, fname):
468 return os.path.lexists(fname) 471 return os.path.lexists(self._join(fname))
469 472
470 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1 473 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
471 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@') 474 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
472 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)') 475 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
473 eolmodes = ['strict', 'crlf', 'lf', 'auto'] 476 eolmodes = ['strict', 'crlf', 'lf', 'auto']
1142 newfile = False 1145 newfile = False
1143 emitfile = True 1146 emitfile = True
1144 state = BFILE 1147 state = BFILE
1145 hunknum = 0 1148 hunknum = 0
1146 1149
1147 def applydiff(ui, fp, changed, strip=1, eolmode='strict'): 1150 def applydiff(ui, fp, changed, backend, strip=1, eolmode='strict'):
1148 """Reads a patch from fp and tries to apply it. 1151 """Reads a patch from fp and tries to apply it.
1149 1152
1150 The dict 'changed' is filled in with all of the filenames changed 1153 The dict 'changed' is filled in with all of the filenames changed
1151 by the patch. Returns 0 for a clean patch, -1 if any rejects were 1154 by the patch. Returns 0 for a clean patch, -1 if any rejects were
1152 found and 1 if there was any fuzz. 1155 found and 1 if there was any fuzz.
1156 patching then normalized according to 'eolmode'. 1159 patching then normalized according to 'eolmode'.
1157 1160
1158 Callers probably want to call '_updatedir' after this to 1161 Callers probably want to call '_updatedir' after this to
1159 apply certain categories of changes not done by this function. 1162 apply certain categories of changes not done by this function.
1160 """ 1163 """
1161 return _applydiff(ui, fp, patchfile, changed, strip=strip, 1164 return _applydiff(ui, fp, patchfile, backend, changed, strip=strip,
1162 eolmode=eolmode) 1165 eolmode=eolmode)
1163 1166
1164 def _applydiff(ui, fp, patcher, changed, strip=1, eolmode='strict'): 1167 def _applydiff(ui, fp, patcher, backend, changed, strip=1, eolmode='strict'):
1165 rejects = 0 1168 rejects = 0
1166 err = 0 1169 err = 0
1167 current_file = None 1170 current_file = None
1168 backend = fsbackend(ui, os.getcwd())
1169 1171
1170 for state, values in iterhunks(fp): 1172 for state, values in iterhunks(fp):
1171 if state == 'hunk': 1173 if state == 'hunk':
1172 if not current_file: 1174 if not current_file:
1173 continue 1175 continue
1301 eolmode = ui.config('patch', 'eol', 'strict') 1303 eolmode = ui.config('patch', 'eol', 'strict')
1302 if eolmode.lower() not in eolmodes: 1304 if eolmode.lower() not in eolmodes:
1303 raise util.Abort(_('unsupported line endings type: %s') % eolmode) 1305 raise util.Abort(_('unsupported line endings type: %s') % eolmode)
1304 eolmode = eolmode.lower() 1306 eolmode = eolmode.lower()
1305 1307
1308 backend = fsbackend(ui, cwd)
1306 try: 1309 try:
1307 fp = open(patchobj, 'rb') 1310 fp = open(patchobj, 'rb')
1308 except TypeError: 1311 except TypeError:
1309 fp = patchobj 1312 fp = patchobj
1310 if cwd:
1311 curdir = os.getcwd()
1312 os.chdir(cwd)
1313 try: 1313 try:
1314 ret = applydiff(ui, fp, files, strip=strip, eolmode=eolmode) 1314 ret = applydiff(ui, fp, files, backend, strip=strip, eolmode=eolmode)
1315 finally: 1315 finally:
1316 if cwd:
1317 os.chdir(curdir)
1318 if fp != patchobj: 1316 if fp != patchobj:
1319 fp.close() 1317 fp.close()
1320 touched = _updatedir(ui, repo, files, similarity) 1318 touched = _updatedir(ui, repo, files, similarity)
1321 files.update(dict.fromkeys(touched)) 1319 files.update(dict.fromkeys(touched))
1322 if ret < 0: 1320 if ret < 0: