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 | 607 | Optional. It's the hostname that the sender can use to identify |
|
608 | 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 | 621 | [[paths]] |
|
611 | 622 | paths:: |
|
612 | 623 | Assigns symbolic names to repositories. The left side is the |
@@ -485,10 +485,10 b' def reposetup(ui, repo):' | |||
|
485 | 485 | release(lock, wlock) |
|
486 | 486 | |
|
487 | 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 | 489 | '''Monkeypatch/wrap patch.patchfile.__init__ to avoid |
|
490 | 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 | 492 | # shrink keywords read from working dir |
|
493 | 493 | self.lines = kwt.shrinklines(self.fname, self.lines) |
|
494 | 494 |
@@ -1764,7 +1764,7 b' def import_(ui, repo, patch1, *patches, ' | |||
|
1764 | 1764 | files = {} |
|
1765 | 1765 | try: |
|
1766 | 1766 | patch.patch(tmpname, ui, strip=strip, cwd=repo.root, |
|
1767 | files=files) | |
|
1767 | files=files, eolmode=None) | |
|
1768 | 1768 | finally: |
|
1769 | 1769 | files = patch.updatedir(ui, repo, files, similarity=sim/100.) |
|
1770 | 1770 | if not opts.get('no_commit'): |
@@ -228,13 +228,42 b' def readgitpatch(lr):' | |||
|
228 | 228 | |
|
229 | 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 | 259 | # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1 |
|
232 | 260 | unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@') |
|
233 | 261 | contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)') |
|
234 | 262 | |
|
235 | 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 | 265 | self.fname = fname |
|
266 | self.eol = eol | |
|
238 | 267 | self.opener = opener |
|
239 | 268 | self.ui = ui |
|
240 | 269 | self.lines = [] |
@@ -260,13 +289,19 b' class patchfile(object):' | |||
|
260 | 289 | def readlines(self, fname): |
|
261 | 290 | fp = self.opener(fname, 'r') |
|
262 | 291 | try: |
|
263 | return fp.readlines() | |
|
292 | return list(linereader(fp, self.eol is not None)) | |
|
264 | 293 | finally: |
|
265 | 294 | fp.close() |
|
266 | 295 | |
|
267 | 296 | def writelines(self, fname, lines): |
|
268 | 297 | fp = self.opener(fname, 'w') |
|
269 | 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 | 305 | fp.writelines(lines) |
|
271 | 306 | finally: |
|
272 | 307 | fp.close() |
@@ -782,28 +817,6 b' def selectfile(afile_orig, bfile_orig, h' | |||
|
782 | 817 | |
|
783 | 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 | 820 | def scangitpatch(lr, firstline): |
|
808 | 821 | """ |
|
809 | 822 | Git patches can emit: |
@@ -824,19 +837,21 b' def scangitpatch(lr, firstline):' | |||
|
824 | 837 | fp = lr.fp |
|
825 | 838 | except IOError: |
|
826 | 839 | fp = cStringIO.StringIO(lr.fp.read()) |
|
827 | gitlr = linereader(fp) | |
|
840 | gitlr = linereader(fp, lr.textmode) | |
|
828 | 841 | gitlr.push(firstline) |
|
829 | 842 | (dopatch, gitpatches) = readgitpatch(gitlr) |
|
830 | 843 | fp.seek(pos) |
|
831 | 844 | return dopatch, gitpatches |
|
832 | 845 | |
|
833 | def iterhunks(ui, fp, sourcefile=None): | |
|
846 | def iterhunks(ui, fp, sourcefile=None, textmode=False): | |
|
834 | 847 | """Read a patch and yield the following events: |
|
835 | 848 | - ("file", afile, bfile, firsthunk): select a new target file. |
|
836 | 849 | - ("hunk", hunk): a new hunk is ready to be applied, follows a |
|
837 | 850 | "file" event. |
|
838 | 851 | - ("git", gitchanges): current diff is in git format, gitchanges |
|
839 | 852 | maps filenames to gitpatch records. Unique event. |
|
853 | ||
|
854 | If textmode is True, input line-endings are normalized to LF. | |
|
840 | 855 | """ |
|
841 | 856 | changed = {} |
|
842 | 857 | current_hunk = None |
@@ -850,7 +865,7 b' def iterhunks(ui, fp, sourcefile=None):' | |||
|
850 | 865 | # our states |
|
851 | 866 | BFILE = 1 |
|
852 | 867 | context = None |
|
853 | lr = linereader(fp) | |
|
868 | lr = linereader(fp, textmode) | |
|
854 | 869 | dopatch = True |
|
855 | 870 | # gitworkdone is True if a git operation (copy, rename, ...) was |
|
856 | 871 | # performed already for the current file. Useful when the file |
@@ -954,17 +969,25 b' def iterhunks(ui, fp, sourcefile=None):' | |||
|
954 | 969 | if hunknum == 0 and dopatch and not gitworkdone: |
|
955 | 970 | raise NoHunks |
|
956 | 971 | |
|
957 |
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 | |
|
959 | filled in with all of the filenames changed by the patch. Returns 0 | |
|
960 | for a clean patch, -1 if any rejects were found and 1 if there was | |
|
961 | any fuzz.""" | |
|
972 | def applydiff(ui, fp, changed, strip=1, sourcefile=None, reverse=False, | |
|
973 | eol=None): | |
|
974 | """ | |
|
975 | Reads a patch from fp and tries to apply it. | |
|
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 | 985 | rejects = 0 |
|
964 | 986 | err = 0 |
|
965 | 987 | current_file = None |
|
966 | 988 | gitpatches = None |
|
967 | 989 | opener = util.opener(os.getcwd()) |
|
990 | textmode = eol is not None | |
|
968 | 991 | |
|
969 | 992 | def closefile(): |
|
970 | 993 | if not current_file: |
@@ -972,7 +995,7 b' def applydiff(ui, fp, changed, strip=1, ' | |||
|
972 | 995 | current_file.close() |
|
973 | 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 | 999 | if state == 'hunk': |
|
977 | 1000 | if not current_file: |
|
978 | 1001 | continue |
@@ -987,11 +1010,11 b' def applydiff(ui, fp, changed, strip=1, ' | |||
|
987 | 1010 | afile, bfile, first_hunk = values |
|
988 | 1011 | try: |
|
989 | 1012 | if sourcefile: |
|
990 | current_file = patchfile(ui, sourcefile, opener) | |
|
1013 | current_file = patchfile(ui, sourcefile, opener, eol=eol) | |
|
991 | 1014 | else: |
|
992 | 1015 | current_file, missing = selectfile(afile, bfile, first_hunk, |
|
993 | 1016 | strip, reverse) |
|
994 | current_file = patchfile(ui, current_file, opener, missing) | |
|
1017 | current_file = patchfile(ui, current_file, opener, missing, eol) | |
|
995 | 1018 | except PatchError, err: |
|
996 | 1019 | ui.warn(str(err) + '\n') |
|
997 | 1020 | current_file, current_hunk = None, None |
@@ -1104,9 +1127,17 b' def externalpatch(patcher, args, patchna' | |||
|
1104 | 1127 | util.explain_exit(code)[0]) |
|
1105 | 1128 | return fuzz |
|
1106 | 1129 | |
|
1107 | def internalpatch(patchobj, ui, strip, cwd, files={}): | |
|
1130 | def internalpatch(patchobj, ui, strip, cwd, files={}, eolmode='strict'): | |
|
1108 | 1131 | """use builtin patch to apply <patchobj> to the working directory. |
|
1109 | 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 | 1141 | try: |
|
1111 | 1142 | fp = file(patchobj, 'rb') |
|
1112 | 1143 | except TypeError: |
@@ -1115,7 +1146,7 b' def internalpatch(patchobj, ui, strip, c' | |||
|
1115 | 1146 | curdir = os.getcwd() |
|
1116 | 1147 | os.chdir(cwd) |
|
1117 | 1148 | try: |
|
1118 | ret = applydiff(ui, fp, files, strip=strip) | |
|
1149 | ret = applydiff(ui, fp, files, strip=strip, eol=eol) | |
|
1119 | 1150 | finally: |
|
1120 | 1151 | if cwd: |
|
1121 | 1152 | os.chdir(curdir) |
@@ -1123,9 +1154,18 b' def internalpatch(patchobj, ui, strip, c' | |||
|
1123 | 1154 | raise PatchError |
|
1124 | 1155 | return ret > 0 |
|
1125 | 1156 | |
|
1126 | def patch(patchname, ui, strip=1, cwd=None, files={}): | |
|
1127 |
""" |
|
|
1128 | returns whether patch was applied with fuzz factor.""" | |
|
1157 | def patch(patchname, ui, strip=1, cwd=None, files={}, eolmode='strict'): | |
|
1158 | """Apply <patchname> to the working directory. | |
|
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 | 1169 | patcher = ui.config('ui', 'patch') |
|
1130 | 1170 | args = [] |
|
1131 | 1171 | try: |
@@ -1134,7 +1174,7 b' def patch(patchname, ui, strip=1, cwd=No' | |||
|
1134 | 1174 | files) |
|
1135 | 1175 | else: |
|
1136 | 1176 | try: |
|
1137 | return internalpatch(patchname, ui, strip, cwd, files) | |
|
1177 | return internalpatch(patchname, ui, strip, cwd, files, eolmode) | |
|
1138 | 1178 | except NoHunks: |
|
1139 | 1179 | patcher = util.find_exe('gpatch') or util.find_exe('patch') or 'patch' |
|
1140 | 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