comparison mercurial/patch.py @ 7517:49f34b43cf90

patch: handle git patches that remove symlinks (issue1438)
author Brendan Cully <brendan@kublai.com>
date Sun, 14 Dec 2008 23:04:29 -0800
parents 8e76e9f67cb3
children ca044918fdf1 fa23d169a895
comparison
equal deleted inserted replaced
7516:a8376f2aa3b1 7517:49f34b43cf90
208 gp.oldpath = line[10:].rstrip() 208 gp.oldpath = line[10:].rstrip()
209 elif line.startswith('copy to '): 209 elif line.startswith('copy to '):
210 gp.path = line[8:].rstrip() 210 gp.path = line[8:].rstrip()
211 elif line.startswith('deleted file'): 211 elif line.startswith('deleted file'):
212 gp.op = 'DELETE' 212 gp.op = 'DELETE'
213 # is the deleted file a symlink?
214 gp.setmode(int(line.rstrip()[-6:], 8))
213 elif line.startswith('new file mode '): 215 elif line.startswith('new file mode '):
214 gp.op = 'ADD' 216 gp.op = 'ADD'
215 gp.setmode(int(line.rstrip()[-6:], 8)) 217 gp.setmode(int(line.rstrip()[-6:], 8))
216 elif line.startswith('new mode '): 218 elif line.startswith('new mode '):
217 gp.setmode(int(line.rstrip()[-6:], 8)) 219 gp.setmode(int(line.rstrip()[-6:], 8))
362 if self.exists and h.createfile(): 364 if self.exists and h.createfile():
363 self.ui.warn(_("file %s already exists\n") % self.fname) 365 self.ui.warn(_("file %s already exists\n") % self.fname)
364 self.rej.append(h) 366 self.rej.append(h)
365 return -1 367 return -1
366 368
367 if isinstance(h, binhunk): 369 if isinstance(h, githunk):
368 if h.rmfile(): 370 if h.rmfile():
369 self.unlink(self.fname) 371 self.unlink(self.fname)
370 else: 372 else:
371 self.lines[:] = h.new() 373 self.lines[:] = h.new()
372 self.offset += len(h.new()) 374 self.offset += len(h.new())
652 return res 654 return res
653 655
654 def new(self, fuzz=0, toponly=False): 656 def new(self, fuzz=0, toponly=False):
655 return self.fuzzit(self.b, fuzz, toponly) 657 return self.fuzzit(self.b, fuzz, toponly)
656 658
657 class binhunk: 659 class githunk(object):
658 'A binary patch file. Only understands literals so far.' 660 """A git hunk"""
659 def __init__(self, gitpatch): 661 def __init__(self, gitpatch):
660 self.gitpatch = gitpatch 662 self.gitpatch = gitpatch
661 self.text = None 663 self.text = None
662 self.hunk = ['GIT binary patch\n'] 664 self.hunk = []
663 665
664 def createfile(self): 666 def createfile(self):
665 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY') 667 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
666 668
667 def rmfile(self): 669 def rmfile(self):
670 def complete(self): 672 def complete(self):
671 return self.text is not None 673 return self.text is not None
672 674
673 def new(self): 675 def new(self):
674 return [self.text] 676 return [self.text]
677
678 class binhunk(githunk):
679 'A binary patch file. Only understands literals so far.'
680 def __init__(self, gitpatch):
681 super(binhunk, self).__init__(gitpatch)
682 self.hunk = ['GIT binary patch\n']
675 683
676 def extract(self, lr): 684 def extract(self, lr):
677 line = lr.readline() 685 line = lr.readline()
678 self.hunk.append(line) 686 self.hunk.append(line)
679 while line and not line.startswith('literal '): 687 while line and not line.startswith('literal '):
698 if len(text) != size: 706 if len(text) != size:
699 raise PatchError(_('binary patch is %d bytes, not %d') % 707 raise PatchError(_('binary patch is %d bytes, not %d') %
700 len(text), size) 708 len(text), size)
701 self.text = text 709 self.text = text
702 710
711 class symlinkhunk(githunk):
712 """A git symlink hunk"""
713 def __init__(self, gitpatch, hunk):
714 super(symlinkhunk, self).__init__(gitpatch)
715 self.hunk = hunk
716
717 def complete(self):
718 return True
719
720 def fix_newline(self):
721 return
722
703 def parsefilename(str): 723 def parsefilename(str):
704 # --- filename \t|space stuff 724 # --- filename \t|space stuff
705 s = str[4:].rstrip('\r\n') 725 s = str[4:].rstrip('\r\n')
706 i = s.find('\t') 726 i = s.find('\t')
707 if i < 0: 727 if i < 0:
857 context = True 877 context = True
858 gpatch = changed.get(bfile) 878 gpatch = changed.get(bfile)
859 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD' 879 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
860 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE' 880 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
861 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove) 881 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
882 if remove:
883 gpatch = changed.get(afile[2:])
884 if gpatch and gpatch.mode[0]:
885 current_hunk = symlinkhunk(gpatch, current_hunk)
862 except PatchError, err: 886 except PatchError, err:
863 ui.debug(err) 887 ui.debug(err)
864 current_hunk = None 888 current_hunk = None
865 continue 889 continue
866 hunknum += 1 890 hunknum += 1
1038 dst = os.path.join(repo.root, gp.path) 1062 dst = os.path.join(repo.root, gp.path)
1039 # patch won't create empty files 1063 # patch won't create empty files
1040 if gp.op == 'ADD' and not os.path.exists(dst): 1064 if gp.op == 'ADD' and not os.path.exists(dst):
1041 flags = (isexec and 'x' or '') + (islink and 'l' or '') 1065 flags = (isexec and 'x' or '') + (islink and 'l' or '')
1042 repo.wwrite(gp.path, '', flags) 1066 repo.wwrite(gp.path, '', flags)
1043 else: 1067 elif gp.op != 'DELETE':
1044 util.set_flags(dst, islink, isexec) 1068 util.set_flags(dst, islink, isexec)
1045 cmdutil.addremove(repo, cfiles, similarity=similarity) 1069 cmdutil.addremove(repo, cfiles, similarity=similarity)
1046 files = patches.keys() 1070 files = patches.keys()
1047 files.extend([r for r in removes if r not in files]) 1071 files.extend([r for r in removes if r not in files])
1048 return util.sort(files) 1072 return util.sort(files)