comparison mercurial/patch.py @ 9585:ea1935e2020a

patch: handle symlinks without symlinkhunk Symlink creations and deletions were handled with a special symlinkhunk object, working like a binary hunk. However, this model does not support symlink updates or replacements, so we teach regular hunks how to handle symlinks.
author Patrick Mezard <pmezard@gmail.com>
date Thu, 15 Oct 2009 23:15:30 +0200
parents b8352a3617f3
children d08099e74b81
comparison
equal deleted inserted replaced
9575:5e44d9e562bc 9585:ea1935e2020a
290 self.fileprinted = False 290 self.fileprinted = False
291 self.printfile(False) 291 self.printfile(False)
292 self.hunks = 0 292 self.hunks = 0
293 293
294 def readlines(self, fname): 294 def readlines(self, fname):
295 if os.path.islink(fname):
296 return [os.readlink(fname)]
295 fp = self.opener(fname, 'r') 297 fp = self.opener(fname, 'r')
296 try: 298 try:
297 return list(linereader(fp, self.eol is not None)) 299 return list(linereader(fp, self.eol is not None))
298 finally: 300 finally:
299 fp.close() 301 fp.close()
300 302
301 def writelines(self, fname, lines): 303 def writelines(self, fname, lines):
302 fp = self.opener(fname, 'w') 304 fp = self.opener(fname, 'w')
303 try: 305 try:
304 if self.eol and self.eol != '\n': 306 if self.eol and self.eol != '\n':
305 for l in lines: 307 for l in lines:
306 if l and l[-1] == '\n': 308 if l and l[-1] == '\n':
403 if self.exists and h.createfile(): 405 if self.exists and h.createfile():
404 self.ui.warn(_("file %s already exists\n") % self.fname) 406 self.ui.warn(_("file %s already exists\n") % self.fname)
405 self.rej.append(h) 407 self.rej.append(h)
406 return -1 408 return -1
407 409
408 if isinstance(h, githunk): 410 if isinstance(h, binhunk):
409 if h.rmfile(): 411 if h.rmfile():
410 self.unlink(self.fname) 412 self.unlink(self.fname)
411 else: 413 else:
412 self.lines[:] = h.new() 414 self.lines[:] = h.new()
413 self.offset += len(h.new()) 415 self.offset += len(h.new())
694 return res 696 return res
695 697
696 def new(self, fuzz=0, toponly=False): 698 def new(self, fuzz=0, toponly=False):
697 return self.fuzzit(self.b, fuzz, toponly) 699 return self.fuzzit(self.b, fuzz, toponly)
698 700
699 class githunk(object): 701 class binhunk:
700 """A git hunk""" 702 'A binary patch file. Only understands literals so far.'
701 def __init__(self, gitpatch): 703 def __init__(self, gitpatch):
702 self.gitpatch = gitpatch 704 self.gitpatch = gitpatch
703 self.text = None 705 self.text = None
704 self.hunk = [] 706 self.hunk = ['GIT binary patch\n']
705 707
706 def createfile(self): 708 def createfile(self):
707 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY') 709 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
708 710
709 def rmfile(self): 711 def rmfile(self):
712 def complete(self): 714 def complete(self):
713 return self.text is not None 715 return self.text is not None
714 716
715 def new(self): 717 def new(self):
716 return [self.text] 718 return [self.text]
717
718 class binhunk(githunk):
719 'A binary patch file. Only understands literals so far.'
720 def __init__(self, gitpatch):
721 super(binhunk, self).__init__(gitpatch)
722 self.hunk = ['GIT binary patch\n']
723 719
724 def extract(self, lr): 720 def extract(self, lr):
725 line = lr.readline() 721 line = lr.readline()
726 self.hunk.append(line) 722 self.hunk.append(line)
727 while line and not line.startswith('literal '): 723 while line and not line.startswith('literal '):
746 if len(text) != size: 742 if len(text) != size:
747 raise PatchError(_('binary patch is %d bytes, not %d') % 743 raise PatchError(_('binary patch is %d bytes, not %d') %
748 len(text), size) 744 len(text), size)
749 self.text = text 745 self.text = text
750 746
751 class symlinkhunk(githunk):
752 """A git symlink hunk"""
753 def __init__(self, gitpatch, hunk):
754 super(symlinkhunk, self).__init__(gitpatch)
755 self.hunk = hunk
756
757 def complete(self):
758 return True
759
760 def fix_newline(self):
761 return
762
763 def parsefilename(str): 747 def parsefilename(str):
764 # --- filename \t|space stuff 748 # --- filename \t|space stuff
765 s = str[4:].rstrip('\r\n') 749 s = str[4:].rstrip('\r\n')
766 i = s.find('\t') 750 i = s.find('\t')
767 if i < 0: 751 if i < 0:
895 context = True 879 context = True
896 gpatch = changed.get(bfile) 880 gpatch = changed.get(bfile)
897 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD' 881 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
898 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE' 882 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
899 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove) 883 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
900 if remove:
901 gpatch = changed.get(afile[2:])
902 if gpatch and gpatch.mode[0]:
903 current_hunk = symlinkhunk(gpatch, current_hunk)
904 except PatchError, err: 884 except PatchError, err:
905 ui.debug(err) 885 ui.debug(err)
906 current_hunk = None 886 current_hunk = None
907 continue 887 continue
908 hunknum += 1 888 hunknum += 1