##// END OF EJS Templates
patch: move diff parsing in iterhunks generator
Patrick Mezard -
r5650:5d3e2f91 default
parent child Browse files
Show More
@@ -838,14 +838,16 b' class linereader:'
838 return l
838 return l
839 return self.fp.readline()
839 return self.fp.readline()
840
840
841 def applydiff(ui, fp, changed, strip=1, sourcefile=None, reverse=False,
841 def iterhunks(ui, fp, sourcefile=None):
842 rejmerge=None, updatedir=None):
842 """Read a patch and yield the following events:
843 """reads a patch from fp and tries to apply it. The dict 'changed' is
843 - ("file", afile, bfile, firsthunk): select a new target file.
844 filled in with all of the filenames changed by the patch. Returns 0
844 - ("hunk", hunk): a new hunk is ready to be applied, follows a
845 for a clean patch, -1 if any rejects were found and 1 if there was
845 "file" event.
846 any fuzz."""
846 - ("git", gitchanges): current diff is in git format, gitchanges
847 maps filenames to gitpatch records. Unique event.
848 """
847
849
848 def scangitpatch(fp, firstline, cwd=None):
850 def scangitpatch(fp, firstline):
849 '''git patches can modify a file, then copy that file to
851 '''git patches can modify a file, then copy that file to
850 a new file, but expect the source to be the unmodified form.
852 a new file, but expect the source to be the unmodified form.
851 So we scan the patch looking for that case so we can do
853 So we scan the patch looking for that case so we can do
@@ -858,46 +860,28 b' def applydiff(ui, fp, changed, strip=1, '
858 fp = cStringIO.StringIO(fp.read())
860 fp = cStringIO.StringIO(fp.read())
859
861
860 (dopatch, gitpatches) = readgitpatch(fp, firstline)
862 (dopatch, gitpatches) = readgitpatch(fp, firstline)
861 for gp in gitpatches:
862 if gp.op in ('COPY', 'RENAME'):
863 copyfile(gp.oldpath, gp.path, basedir=cwd)
864
865 fp.seek(pos)
863 fp.seek(pos)
866
864
867 return fp, dopatch, gitpatches
865 return fp, dopatch, gitpatches
868
866
867 changed = {}
869 current_hunk = None
868 current_hunk = None
870 current_file = None
871 afile = ""
869 afile = ""
872 bfile = ""
870 bfile = ""
873 state = None
871 state = None
874 hunknum = 0
872 hunknum = 0
875 rejects = 0
873 emitfile = False
876
874
877 git = False
875 git = False
878 gitre = re.compile('diff --git (a/.*) (b/.*)')
876 gitre = re.compile('diff --git (a/.*) (b/.*)')
879
877
880 # our states
878 # our states
881 BFILE = 1
879 BFILE = 1
882 err = 0
883 context = None
880 context = None
884 lr = linereader(fp)
881 lr = linereader(fp)
885 dopatch = True
882 dopatch = True
886 gitworkdone = False
883 gitworkdone = False
887
884
888 def getpatchfile(afile, bfile, hunk):
889 try:
890 if sourcefile:
891 targetfile = patchfile(ui, sourcefile)
892 else:
893 targetfile = selectfile(afile, bfile, hunk,
894 strip, reverse)
895 targetfile = patchfile(ui, targetfile)
896 return targetfile
897 except PatchError, err:
898 ui.warn(str(err) + '\n')
899 return None
900
901 while True:
885 while True:
902 newfile = False
886 newfile = False
903 x = lr.readline()
887 x = lr.readline()
@@ -906,11 +890,7 b' def applydiff(ui, fp, changed, strip=1, '
906 if current_hunk:
890 if current_hunk:
907 if x.startswith('\ '):
891 if x.startswith('\ '):
908 current_hunk.fix_newline()
892 current_hunk.fix_newline()
909 ret = current_file.apply(current_hunk, reverse)
893 yield 'hunk', current_hunk
910 if ret >= 0:
911 changed.setdefault(current_file.fname, (None, None))
912 if ret > 0:
913 err = 1
914 current_hunk = None
894 current_hunk = None
915 gitworkdone = False
895 gitworkdone = False
916 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
896 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
@@ -924,21 +904,15 b' def applydiff(ui, fp, changed, strip=1, '
924 current_hunk = None
904 current_hunk = None
925 continue
905 continue
926 hunknum += 1
906 hunknum += 1
927 if not current_file:
907 if emitfile:
928 current_file = getpatchfile(afile, bfile, current_hunk)
908 emitfile = False
929 if not current_file:
909 yield 'file', (afile, bfile, current_hunk)
930 current_file, current_hunk = None, None
931 rejects += 1
932 continue
933 elif state == BFILE and x.startswith('GIT binary patch'):
910 elif state == BFILE and x.startswith('GIT binary patch'):
934 current_hunk = binhunk(changed[bfile[2:]][1])
911 current_hunk = binhunk(changed[bfile[2:]][1])
935 hunknum += 1
912 hunknum += 1
936 if not current_file:
913 if emitfile:
937 current_file = getpatchfile(afile, bfile, current_hunk)
914 emitfile = False
938 if not current_file:
915 yield 'file', (afile, bfile, current_hunk)
939 current_file, current_hunk = None, None
940 rejects += 1
941 continue
942 current_hunk.extract(fp)
916 current_hunk.extract(fp)
943 elif x.startswith('diff --git'):
917 elif x.startswith('diff --git'):
944 # check for git diff, scanning the whole patch file if needed
918 # check for git diff, scanning the whole patch file if needed
@@ -948,6 +922,7 b' def applydiff(ui, fp, changed, strip=1, '
948 if not git:
922 if not git:
949 git = True
923 git = True
950 fp, dopatch, gitpatches = scangitpatch(fp, x)
924 fp, dopatch, gitpatches = scangitpatch(fp, x)
925 yield 'git', gitpatches
951 for gp in gitpatches:
926 for gp in gitpatches:
952 changed[gp.path] = (gp.op, gp)
927 changed[gp.path] = (gp.op, gp)
953 # else error?
928 # else error?
@@ -984,34 +959,76 b' def applydiff(ui, fp, changed, strip=1, '
984 bfile = parsefilename(l2)
959 bfile = parsefilename(l2)
985
960
986 if newfile:
961 if newfile:
987 if current_file:
962 emitfile = True
988 current_file.close()
989 if rejmerge:
990 rejmerge(current_file)
991 rejects += len(current_file.rej)
992 state = BFILE
963 state = BFILE
993 current_file = None
994 hunknum = 0
964 hunknum = 0
995 if current_hunk:
965 if current_hunk:
996 if current_hunk.complete():
966 if current_hunk.complete():
967 yield 'hunk', current_hunk
968 else:
969 raise PatchError(_("malformed patch %s %s") % (afile,
970 current_hunk.desc))
971
972 if hunknum == 0 and dopatch and not gitworkdone:
973 raise NoHunks
974
975 def applydiff(ui, fp, changed, strip=1, sourcefile=None, reverse=False,
976 rejmerge=None, updatedir=None):
977 """reads a patch from fp and tries to apply it. The dict 'changed' is
978 filled in with all of the filenames changed by the patch. Returns 0
979 for a clean patch, -1 if any rejects were found and 1 if there was
980 any fuzz."""
981
982 rejects = 0
983 err = 0
984 current_file = None
985 gitpatches = None
986
987 def closefile():
988 if not current_file:
989 return 0
990 current_file.close()
991 if rejmerge:
992 rejmerge(current_file)
993 return len(current_file.rej)
994
995 for state, values in iterhunks(ui, fp, sourcefile):
996 if state == 'hunk':
997 if not current_file:
998 continue
999 current_hunk = values
997 ret = current_file.apply(current_hunk, reverse)
1000 ret = current_file.apply(current_hunk, reverse)
998 if ret >= 0:
1001 if ret >= 0:
999 changed.setdefault(current_file.fname, (None, None))
1002 changed.setdefault(current_file.fname, (None, None))
1000 if ret > 0:
1003 if ret > 0:
1001 err = 1
1004 err = 1
1005 elif state == 'file':
1006 rejects += closefile()
1007 afile, bfile, first_hunk = values
1008 try:
1009 if sourcefile:
1010 current_file = patchfile(ui, sourcefile)
1011 else:
1012 current_file = selectfile(afile, bfile, first_hunk,
1013 strip, reverse)
1014 current_file = patchfile(ui, current_file)
1015 except PatchError, err:
1016 ui.warn(str(err) + '\n')
1017 current_file, current_hunk = None, None
1018 rejects += 1
1019 continue
1020 elif state == 'git':
1021 gitpatches = values
1022 for gp in gitpatches:
1023 if gp.op in ('COPY', 'RENAME'):
1024 copyfile(gp.oldpath, gp.path)
1025 changed[gp.path] = (gp.op, gp)
1002 else:
1026 else:
1003 fname = current_file and current_file.fname or None
1027 raise util.Abort(_('unsupported parser state: %s') % state)
1004 raise PatchError(_("malformed patch %s %s") % (fname,
1005 current_hunk.desc))
1006 if current_file:
1007 current_file.close()
1008 if rejmerge:
1009 rejmerge(current_file)
1010 rejects += len(current_file.rej)
1011
1028
1012 if not rejects and hunknum == 0 and dopatch and not gitworkdone:
1029 rejects += closefile()
1013 raise NoHunks
1030
1014 if updatedir and git:
1031 if updatedir and gitpatches:
1015 updatedir(gitpatches)
1032 updatedir(gitpatches)
1016 if rejects:
1033 if rejects:
1017 return -1
1034 return -1
@@ -3,7 +3,6 b' Patch queue now empty'
3 % push patch with missing target
3 % push patch with missing target
4 applying changeb
4 applying changeb
5 unable to find b or b for patching
5 unable to find b or b for patching
6 unable to find b or b for patching
7 patch failed, unable to continue (try -v)
6 patch failed, unable to continue (try -v)
8 patch failed, rejects left in working dir
7 patch failed, rejects left in working dir
9 Errors during apply, please fix and refresh changeb
8 Errors during apply, please fix and refresh changeb
General Comments 0
You need to be logged in to leave comments. Login now