Show More
@@ -0,0 +1,53 b'' | |||||
|
1 | #!/bin/sh | |||
|
2 | ||||
|
3 | cat > makepatch.py <<EOF | |||
|
4 | f = file('eol.diff', 'wb') | |||
|
5 | w = f.write | |||
|
6 | w('test message\n') | |||
|
7 | w('diff --git a/a b/a\n') | |||
|
8 | w('--- a/a\n') | |||
|
9 | w('+++ b/a\n') | |||
|
10 | w('@@ -1,5 +1,5 @@\n') | |||
|
11 | w(' a\n') | |||
|
12 | w('-b\r\n') | |||
|
13 | w('+y\r\n') | |||
|
14 | w(' c\r\n') | |||
|
15 | w(' d\n') | |||
|
16 | w('-e\n') | |||
|
17 | w('\ No newline at end of file\n') | |||
|
18 | w('+z\r\n') | |||
|
19 | w('\ No newline at end of file\r\n') | |||
|
20 | EOF | |||
|
21 | ||||
|
22 | hg init repo | |||
|
23 | cd repo | |||
|
24 | echo '\.diff' > .hgignore | |||
|
25 | ||||
|
26 | # Test different --eol values | |||
|
27 | python -c 'file("a", "wb").write("a\nb\nc\nd\ne")' | |||
|
28 | hg ci -Am adda | |||
|
29 | python ../makepatch.py | |||
|
30 | echo % invalid eol | |||
|
31 | hg --config patch.eol='LFCR' import eol.diff | |||
|
32 | hg revert -a | |||
|
33 | echo % force LF | |||
|
34 | hg --traceback --config patch.eol='LF' import eol.diff | |||
|
35 | python -c 'print repr(file("a","rb").read())' | |||
|
36 | hg st | |||
|
37 | echo % force CRLF | |||
|
38 | hg up -C 0 | |||
|
39 | hg --traceback --config patch.eol='CRLF' import eol.diff | |||
|
40 | python -c 'print repr(file("a","rb").read())' | |||
|
41 | hg st | |||
|
42 | ||||
|
43 | # Test --eol and binary patches | |||
|
44 | python -c 'file("b", "wb").write("a\x00\nb")' | |||
|
45 | hg ci -Am addb | |||
|
46 | python -c 'file("b", "wb").write("a\x00\nc")' | |||
|
47 | hg diff --git > bin.diff | |||
|
48 | hg revert --no-backup b | |||
|
49 | echo % binary patch with --eol | |||
|
50 | hg import --config patch.eol='CRLF' -m changeb bin.diff | |||
|
51 | python -c 'print repr(file("b","rb").read())' | |||
|
52 | hg st | |||
|
53 | cd .. |
@@ -0,0 +1,16 b'' | |||||
|
1 | adding .hgignore | |||
|
2 | adding a | |||
|
3 | % invalid eol | |||
|
4 | applying eol.diff | |||
|
5 | abort: Unsupported line endings type: LFCR | |||
|
6 | % force LF | |||
|
7 | applying eol.diff | |||
|
8 | 'a\ny\nc\nd\nz' | |||
|
9 | % force CRLF | |||
|
10 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | |||
|
11 | applying eol.diff | |||
|
12 | 'a\r\ny\r\nc\r\nd\r\nz' | |||
|
13 | adding b | |||
|
14 | % binary patch with --eol | |||
|
15 | applying bin.diff | |||
|
16 | 'a\x00\nc' |
@@ -607,6 +607,17 b' smtp::' | |||||
607 | Optional. It's the hostname that the sender can use to identify |
|
607 | Optional. It's the hostname that the sender can use to identify | |
608 | itself to the MTA. |
|
608 | itself to the MTA. | |
609 |
|
609 | |||
|
610 | [[patch]] | |||
|
611 | patch:: | |||
|
612 | Settings used when applying patches, for instance through the 'import' | |||
|
613 | command or with Mercurial Queues extension. | |||
|
614 | eol;; | |||
|
615 | When set to 'strict' patch content and patched files end of lines | |||
|
616 | are preserved. When set to 'lf' or 'crlf', both files end of lines | |||
|
617 | are ignored when patching and the result line endings are | |||
|
618 | normalized to either LF (Unix) or CRLF (Windows). | |||
|
619 | Default: strict. | |||
|
620 | ||||
610 | [[paths]] |
|
621 | [[paths]] | |
611 | paths:: |
|
622 | paths:: | |
612 | Assigns symbolic names to repositories. The left side is the |
|
623 | Assigns symbolic names to repositories. The left side is the |
@@ -485,10 +485,10 b' def reposetup(ui, repo):' | |||||
485 | release(lock, wlock) |
|
485 | release(lock, wlock) | |
486 |
|
486 | |||
487 | # monkeypatches |
|
487 | # monkeypatches | |
488 | def kwpatchfile_init(orig, self, ui, fname, opener, missing=False): |
|
488 | def kwpatchfile_init(orig, self, ui, fname, opener, missing=False, eol=None): | |
489 | '''Monkeypatch/wrap patch.patchfile.__init__ to avoid |
|
489 | '''Monkeypatch/wrap patch.patchfile.__init__ to avoid | |
490 | rejects or conflicts due to expanded keywords in working dir.''' |
|
490 | rejects or conflicts due to expanded keywords in working dir.''' | |
491 | orig(self, ui, fname, opener, missing) |
|
491 | orig(self, ui, fname, opener, missing, eol) | |
492 | # shrink keywords read from working dir |
|
492 | # shrink keywords read from working dir | |
493 | self.lines = kwt.shrinklines(self.fname, self.lines) |
|
493 | self.lines = kwt.shrinklines(self.fname, self.lines) | |
494 |
|
494 |
@@ -1764,7 +1764,7 b' def import_(ui, repo, patch1, *patches, ' | |||||
1764 | files = {} |
|
1764 | files = {} | |
1765 | try: |
|
1765 | try: | |
1766 | patch.patch(tmpname, ui, strip=strip, cwd=repo.root, |
|
1766 | patch.patch(tmpname, ui, strip=strip, cwd=repo.root, | |
1767 | files=files) |
|
1767 | files=files, eolmode=None) | |
1768 | finally: |
|
1768 | finally: | |
1769 | files = patch.updatedir(ui, repo, files, similarity=sim/100.) |
|
1769 | files = patch.updatedir(ui, repo, files, similarity=sim/100.) | |
1770 | if not opts.get('no_commit'): |
|
1770 | if not opts.get('no_commit'): |
@@ -228,13 +228,42 b' def readgitpatch(lr):' | |||||
228 |
|
228 | |||
229 | return (dopatch, gitpatches) |
|
229 | return (dopatch, gitpatches) | |
230 |
|
230 | |||
|
231 | class linereader: | |||
|
232 | # simple class to allow pushing lines back into the input stream | |||
|
233 | def __init__(self, fp, textmode=False): | |||
|
234 | self.fp = fp | |||
|
235 | self.buf = [] | |||
|
236 | self.textmode = textmode | |||
|
237 | ||||
|
238 | def push(self, line): | |||
|
239 | if line is not None: | |||
|
240 | self.buf.append(line) | |||
|
241 | ||||
|
242 | def readline(self): | |||
|
243 | if self.buf: | |||
|
244 | l = self.buf[0] | |||
|
245 | del self.buf[0] | |||
|
246 | return l | |||
|
247 | l = self.fp.readline() | |||
|
248 | if self.textmode and l.endswith('\r\n'): | |||
|
249 | l = l[:-2] + '\n' | |||
|
250 | return l | |||
|
251 | ||||
|
252 | def __iter__(self): | |||
|
253 | while 1: | |||
|
254 | l = self.readline() | |||
|
255 | if not l: | |||
|
256 | break | |||
|
257 | yield l | |||
|
258 | ||||
231 | # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1 |
|
259 | # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1 | |
232 | unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@') |
|
260 | unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@') | |
233 | contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)') |
|
261 | contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)') | |
234 |
|
262 | |||
235 | class patchfile(object): |
|
263 | class patchfile(object): | |
236 | def __init__(self, ui, fname, opener, missing=False): |
|
264 | def __init__(self, ui, fname, opener, missing=False, eol=None): | |
237 | self.fname = fname |
|
265 | self.fname = fname | |
|
266 | self.eol = eol | |||
238 | self.opener = opener |
|
267 | self.opener = opener | |
239 | self.ui = ui |
|
268 | self.ui = ui | |
240 | self.lines = [] |
|
269 | self.lines = [] | |
@@ -260,13 +289,19 b' class patchfile(object):' | |||||
260 | def readlines(self, fname): |
|
289 | def readlines(self, fname): | |
261 | fp = self.opener(fname, 'r') |
|
290 | fp = self.opener(fname, 'r') | |
262 | try: |
|
291 | try: | |
263 | return fp.readlines() |
|
292 | return list(linereader(fp, self.eol is not None)) | |
264 | finally: |
|
293 | finally: | |
265 | fp.close() |
|
294 | fp.close() | |
266 |
|
295 | |||
267 | def writelines(self, fname, lines): |
|
296 | def writelines(self, fname, lines): | |
268 | fp = self.opener(fname, 'w') |
|
297 | fp = self.opener(fname, 'w') | |
269 | try: |
|
298 | try: | |
|
299 | if self.eol and self.eol != '\n': | |||
|
300 | for l in lines: | |||
|
301 | if l and l[-1] == '\n': | |||
|
302 | l = l[:1] + self.eol | |||
|
303 | fp.write(l) | |||
|
304 | else: | |||
270 | fp.writelines(lines) |
|
305 | fp.writelines(lines) | |
271 | finally: |
|
306 | finally: | |
272 | fp.close() |
|
307 | fp.close() | |
@@ -782,28 +817,6 b' def selectfile(afile_orig, bfile_orig, h' | |||||
782 |
|
817 | |||
783 | return fname, missing |
|
818 | return fname, missing | |
784 |
|
819 | |||
785 | class linereader(object): |
|
|||
786 | # simple class to allow pushing lines back into the input stream |
|
|||
787 | def __init__(self, fp): |
|
|||
788 | self.fp = fp |
|
|||
789 | self.buf = [] |
|
|||
790 |
|
||||
791 | def push(self, line): |
|
|||
792 | if line is not None: |
|
|||
793 | self.buf.append(line) |
|
|||
794 |
|
||||
795 | def readline(self): |
|
|||
796 | if self.buf: |
|
|||
797 | return self.buf.pop(0) |
|
|||
798 | return self.fp.readline() |
|
|||
799 |
|
||||
800 | def __iter__(self): |
|
|||
801 | while 1: |
|
|||
802 | l = self.readline() |
|
|||
803 | if not l: |
|
|||
804 | break |
|
|||
805 | yield l |
|
|||
806 |
|
||||
807 | def scangitpatch(lr, firstline): |
|
820 | def scangitpatch(lr, firstline): | |
808 | """ |
|
821 | """ | |
809 | Git patches can emit: |
|
822 | Git patches can emit: | |
@@ -824,19 +837,21 b' def scangitpatch(lr, firstline):' | |||||
824 | fp = lr.fp |
|
837 | fp = lr.fp | |
825 | except IOError: |
|
838 | except IOError: | |
826 | fp = cStringIO.StringIO(lr.fp.read()) |
|
839 | fp = cStringIO.StringIO(lr.fp.read()) | |
827 | gitlr = linereader(fp) |
|
840 | gitlr = linereader(fp, lr.textmode) | |
828 | gitlr.push(firstline) |
|
841 | gitlr.push(firstline) | |
829 | (dopatch, gitpatches) = readgitpatch(gitlr) |
|
842 | (dopatch, gitpatches) = readgitpatch(gitlr) | |
830 | fp.seek(pos) |
|
843 | fp.seek(pos) | |
831 | return dopatch, gitpatches |
|
844 | return dopatch, gitpatches | |
832 |
|
845 | |||
833 | def iterhunks(ui, fp, sourcefile=None): |
|
846 | def iterhunks(ui, fp, sourcefile=None, textmode=False): | |
834 | """Read a patch and yield the following events: |
|
847 | """Read a patch and yield the following events: | |
835 | - ("file", afile, bfile, firsthunk): select a new target file. |
|
848 | - ("file", afile, bfile, firsthunk): select a new target file. | |
836 | - ("hunk", hunk): a new hunk is ready to be applied, follows a |
|
849 | - ("hunk", hunk): a new hunk is ready to be applied, follows a | |
837 | "file" event. |
|
850 | "file" event. | |
838 | - ("git", gitchanges): current diff is in git format, gitchanges |
|
851 | - ("git", gitchanges): current diff is in git format, gitchanges | |
839 | maps filenames to gitpatch records. Unique event. |
|
852 | maps filenames to gitpatch records. Unique event. | |
|
853 | ||||
|
854 | If textmode is True, input line-endings are normalized to LF. | |||
840 | """ |
|
855 | """ | |
841 | changed = {} |
|
856 | changed = {} | |
842 | current_hunk = None |
|
857 | current_hunk = None | |
@@ -850,7 +865,7 b' def iterhunks(ui, fp, sourcefile=None):' | |||||
850 | # our states |
|
865 | # our states | |
851 | BFILE = 1 |
|
866 | BFILE = 1 | |
852 | context = None |
|
867 | context = None | |
853 | lr = linereader(fp) |
|
868 | lr = linereader(fp, textmode) | |
854 | dopatch = True |
|
869 | dopatch = True | |
855 | # gitworkdone is True if a git operation (copy, rename, ...) was |
|
870 | # gitworkdone is True if a git operation (copy, rename, ...) was | |
856 | # performed already for the current file. Useful when the file |
|
871 | # performed already for the current file. Useful when the file | |
@@ -954,17 +969,25 b' def iterhunks(ui, fp, sourcefile=None):' | |||||
954 | if hunknum == 0 and dopatch and not gitworkdone: |
|
969 | if hunknum == 0 and dopatch and not gitworkdone: | |
955 | raise NoHunks |
|
970 | raise NoHunks | |
956 |
|
971 | |||
957 |
def applydiff(ui, fp, changed, strip=1, sourcefile=None, reverse=False |
|
972 | def applydiff(ui, fp, changed, strip=1, sourcefile=None, reverse=False, | |
958 | """reads a patch from fp and tries to apply it. The dict 'changed' is |
|
973 | eol=None): | |
959 | filled in with all of the filenames changed by the patch. Returns 0 |
|
974 | """ | |
960 | for a clean patch, -1 if any rejects were found and 1 if there was |
|
975 | Reads a patch from fp and tries to apply it. | |
961 | any fuzz.""" |
|
|||
962 |
|
|
976 | ||
|
977 | The dict 'changed' is filled in with all of the filenames changed | |||
|
978 | by the patch. Returns 0 for a clean patch, -1 if any rejects were | |||
|
979 | found and 1 if there was any fuzz. | |||
|
980 | ||||
|
981 | If 'eol' is None, the patch content and patched file are read in | |||
|
982 | binary mode. Otherwise, line endings are ignored when patching then | |||
|
983 | normalized to 'eol' (usually '\n' or \r\n'). | |||
|
984 | """ | |||
963 | rejects = 0 |
|
985 | rejects = 0 | |
964 | err = 0 |
|
986 | err = 0 | |
965 | current_file = None |
|
987 | current_file = None | |
966 | gitpatches = None |
|
988 | gitpatches = None | |
967 | opener = util.opener(os.getcwd()) |
|
989 | opener = util.opener(os.getcwd()) | |
|
990 | textmode = eol is not None | |||
968 |
|
991 | |||
969 | def closefile(): |
|
992 | def closefile(): | |
970 | if not current_file: |
|
993 | if not current_file: | |
@@ -972,7 +995,7 b' def applydiff(ui, fp, changed, strip=1, ' | |||||
972 | current_file.close() |
|
995 | current_file.close() | |
973 | return len(current_file.rej) |
|
996 | return len(current_file.rej) | |
974 |
|
997 | |||
975 | for state, values in iterhunks(ui, fp, sourcefile): |
|
998 | for state, values in iterhunks(ui, fp, sourcefile, textmode): | |
976 | if state == 'hunk': |
|
999 | if state == 'hunk': | |
977 | if not current_file: |
|
1000 | if not current_file: | |
978 | continue |
|
1001 | continue | |
@@ -987,11 +1010,11 b' def applydiff(ui, fp, changed, strip=1, ' | |||||
987 | afile, bfile, first_hunk = values |
|
1010 | afile, bfile, first_hunk = values | |
988 | try: |
|
1011 | try: | |
989 | if sourcefile: |
|
1012 | if sourcefile: | |
990 | current_file = patchfile(ui, sourcefile, opener) |
|
1013 | current_file = patchfile(ui, sourcefile, opener, eol=eol) | |
991 | else: |
|
1014 | else: | |
992 | current_file, missing = selectfile(afile, bfile, first_hunk, |
|
1015 | current_file, missing = selectfile(afile, bfile, first_hunk, | |
993 | strip, reverse) |
|
1016 | strip, reverse) | |
994 | current_file = patchfile(ui, current_file, opener, missing) |
|
1017 | current_file = patchfile(ui, current_file, opener, missing, eol) | |
995 | except PatchError, err: |
|
1018 | except PatchError, err: | |
996 | ui.warn(str(err) + '\n') |
|
1019 | ui.warn(str(err) + '\n') | |
997 | current_file, current_hunk = None, None |
|
1020 | current_file, current_hunk = None, None | |
@@ -1104,9 +1127,17 b' def externalpatch(patcher, args, patchna' | |||||
1104 | util.explain_exit(code)[0]) |
|
1127 | util.explain_exit(code)[0]) | |
1105 | return fuzz |
|
1128 | return fuzz | |
1106 |
|
1129 | |||
1107 | def internalpatch(patchobj, ui, strip, cwd, files={}): |
|
1130 | def internalpatch(patchobj, ui, strip, cwd, files={}, eolmode='strict'): | |
1108 | """use builtin patch to apply <patchobj> to the working directory. |
|
1131 | """use builtin patch to apply <patchobj> to the working directory. | |
1109 | returns whether patch was applied with fuzz factor.""" |
|
1132 | returns whether patch was applied with fuzz factor.""" | |
|
1133 | ||||
|
1134 | if eolmode is None: | |||
|
1135 | eolmode = ui.config('patch', 'eol', 'strict') | |||
|
1136 | try: | |||
|
1137 | eol = {'strict': None, 'crlf': '\r\n', 'lf': '\n'}[eolmode.lower()] | |||
|
1138 | except KeyError: | |||
|
1139 | raise util.Abort(_('Unsupported line endings type: %s') % eolmode) | |||
|
1140 | ||||
1110 | try: |
|
1141 | try: | |
1111 | fp = file(patchobj, 'rb') |
|
1142 | fp = file(patchobj, 'rb') | |
1112 | except TypeError: |
|
1143 | except TypeError: | |
@@ -1115,7 +1146,7 b' def internalpatch(patchobj, ui, strip, c' | |||||
1115 | curdir = os.getcwd() |
|
1146 | curdir = os.getcwd() | |
1116 | os.chdir(cwd) |
|
1147 | os.chdir(cwd) | |
1117 | try: |
|
1148 | try: | |
1118 | ret = applydiff(ui, fp, files, strip=strip) |
|
1149 | ret = applydiff(ui, fp, files, strip=strip, eol=eol) | |
1119 | finally: |
|
1150 | finally: | |
1120 | if cwd: |
|
1151 | if cwd: | |
1121 | os.chdir(curdir) |
|
1152 | os.chdir(curdir) | |
@@ -1123,9 +1154,18 b' def internalpatch(patchobj, ui, strip, c' | |||||
1123 | raise PatchError |
|
1154 | raise PatchError | |
1124 | return ret > 0 |
|
1155 | return ret > 0 | |
1125 |
|
1156 | |||
1126 | def patch(patchname, ui, strip=1, cwd=None, files={}): |
|
1157 | def patch(patchname, ui, strip=1, cwd=None, files={}, eolmode='strict'): | |
1127 |
""" |
|
1158 | """Apply <patchname> to the working directory. | |
1128 | returns whether patch was applied with fuzz factor.""" |
|
1159 | ||
|
1160 | 'eolmode' specifies how end of lines should be handled. It can be: | |||
|
1161 | - 'strict': inputs are read in binary mode, EOLs are preserved | |||
|
1162 | - 'crlf': EOLs are ignored when patching and reset to CRLF | |||
|
1163 | - 'lf': EOLs are ignored when patching and reset to LF | |||
|
1164 | - None: get it from user settings, default to 'strict' | |||
|
1165 | 'eolmode' is ignored when using an external patcher program. | |||
|
1166 | ||||
|
1167 | Returns whether patch was applied with fuzz factor. | |||
|
1168 | """ | |||
1129 | patcher = ui.config('ui', 'patch') |
|
1169 | patcher = ui.config('ui', 'patch') | |
1130 | args = [] |
|
1170 | args = [] | |
1131 | try: |
|
1171 | try: | |
@@ -1134,7 +1174,7 b' def patch(patchname, ui, strip=1, cwd=No' | |||||
1134 | files) |
|
1174 | files) | |
1135 | else: |
|
1175 | else: | |
1136 | try: |
|
1176 | try: | |
1137 | return internalpatch(patchname, ui, strip, cwd, files) |
|
1177 | return internalpatch(patchname, ui, strip, cwd, files, eolmode) | |
1138 | except NoHunks: |
|
1178 | except NoHunks: | |
1139 | patcher = util.find_exe('gpatch') or util.find_exe('patch') or 'patch' |
|
1179 | patcher = util.find_exe('gpatch') or util.find_exe('patch') or 'patch' | |
1140 | ui.debug(_('no valid hunks found; trying with %r instead\n') % |
|
1180 | ui.debug(_('no valid hunks found; trying with %r instead\n') % |
General Comments 0
You need to be logged in to leave comments.
Login now