##// 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 838 return l
839 839 return self.fp.readline()
840 840
841 def applydiff(ui, fp, changed, strip=1, sourcefile=None, reverse=False,
842 rejmerge=None, updatedir=None):
843 """reads a patch from fp and tries to apply it. The dict 'changed' is
844 filled in with all of the filenames changed by the patch. Returns 0
845 for a clean patch, -1 if any rejects were found and 1 if there was
846 any fuzz."""
841 def iterhunks(ui, fp, sourcefile=None):
842 """Read a patch and yield the following events:
843 - ("file", afile, bfile, firsthunk): select a new target file.
844 - ("hunk", hunk): a new hunk is ready to be applied, follows a
845 "file" event.
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 851 '''git patches can modify a file, then copy that file to
850 852 a new file, but expect the source to be the unmodified form.
851 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 860 fp = cStringIO.StringIO(fp.read())
859 861
860 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 863 fp.seek(pos)
866 864
867 865 return fp, dopatch, gitpatches
868 866
867 changed = {}
869 868 current_hunk = None
870 current_file = None
871 869 afile = ""
872 870 bfile = ""
873 871 state = None
874 872 hunknum = 0
875 rejects = 0
873 emitfile = False
876 874
877 875 git = False
878 876 gitre = re.compile('diff --git (a/.*) (b/.*)')
879 877
880 878 # our states
881 879 BFILE = 1
882 err = 0
883 880 context = None
884 881 lr = linereader(fp)
885 882 dopatch = True
886 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 885 while True:
902 886 newfile = False
903 887 x = lr.readline()
@@ -906,11 +890,7 b' def applydiff(ui, fp, changed, strip=1, '
906 890 if current_hunk:
907 891 if x.startswith('\ '):
908 892 current_hunk.fix_newline()
909 ret = current_file.apply(current_hunk, reverse)
910 if ret >= 0:
911 changed.setdefault(current_file.fname, (None, None))
912 if ret > 0:
913 err = 1
893 yield 'hunk', current_hunk
914 894 current_hunk = None
915 895 gitworkdone = False
916 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 904 current_hunk = None
925 905 continue
926 906 hunknum += 1
927 if not current_file:
928 current_file = getpatchfile(afile, bfile, current_hunk)
929 if not current_file:
930 current_file, current_hunk = None, None
931 rejects += 1
932 continue
907 if emitfile:
908 emitfile = False
909 yield 'file', (afile, bfile, current_hunk)
933 910 elif state == BFILE and x.startswith('GIT binary patch'):
934 911 current_hunk = binhunk(changed[bfile[2:]][1])
935 912 hunknum += 1
936 if not current_file:
937 current_file = getpatchfile(afile, bfile, current_hunk)
938 if not current_file:
939 current_file, current_hunk = None, None
940 rejects += 1
941 continue
913 if emitfile:
914 emitfile = False
915 yield 'file', (afile, bfile, current_hunk)
942 916 current_hunk.extract(fp)
943 917 elif x.startswith('diff --git'):
944 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 922 if not git:
949 923 git = True
950 924 fp, dopatch, gitpatches = scangitpatch(fp, x)
925 yield 'git', gitpatches
951 926 for gp in gitpatches:
952 927 changed[gp.path] = (gp.op, gp)
953 928 # else error?
@@ -984,34 +959,76 b' def applydiff(ui, fp, changed, strip=1, '
984 959 bfile = parsefilename(l2)
985 960
986 961 if newfile:
987 if current_file:
988 current_file.close()
989 if rejmerge:
990 rejmerge(current_file)
991 rejects += len(current_file.rej)
962 emitfile = True
992 963 state = BFILE
993 current_file = None
994 964 hunknum = 0
995 965 if current_hunk:
996 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 1000 ret = current_file.apply(current_hunk, reverse)
998 1001 if ret >= 0:
999 1002 changed.setdefault(current_file.fname, (None, None))
1000 1003 if ret > 0:
1001 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 1026 else:
1003 fname = current_file and current_file.fname or None
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)
1027 raise util.Abort(_('unsupported parser state: %s') % state)
1011 1028
1012 if not rejects and hunknum == 0 and dopatch and not gitworkdone:
1013 raise NoHunks
1014 if updatedir and git:
1029 rejects += closefile()
1030
1031 if updatedir and gitpatches:
1015 1032 updatedir(gitpatches)
1016 1033 if rejects:
1017 1034 return -1
@@ -3,7 +3,6 b' Patch queue now empty'
3 3 % push patch with missing target
4 4 applying changeb
5 5 unable to find b or b for patching
6 unable to find b or b for patching
7 6 patch failed, unable to continue (try -v)
8 7 patch failed, rejects left in working dir
9 8 Errors during apply, please fix and refresh changeb
General Comments 0
You need to be logged in to leave comments. Login now