patch: fix eolmode=auto with new files
If target file does not exist or has no eol, current code was normalizing eols
to LF. Preserve patch file eols instead.
--- a/doc/hgrc.5.txt Wed Dec 23 19:18:03 2009 +0100
+++ b/doc/hgrc.5.txt Wed Dec 23 19:31:47 2009 +0100
@@ -646,12 +646,13 @@
``eol``
When set to 'strict' patch content and patched files end of lines
- are preserved. When set to ``lf`` or ``crlf``, both files end of lines
- are ignored when patching and the result line endings are
+ are preserved. When set to ``lf`` or ``crlf``, both files end of
+ lines are ignored when patching and the result line endings are
normalized to either LF (Unix) or CRLF (Windows). When set to
``auto``, end of lines are again ignored while patching but line
endings in patched files are normalized to their original setting
- on a per-file basis.
+ on a per-file basis. If target file does not exist or has no end
+ of line, patch line endings are preserved.
Default: strict.
--- a/mercurial/patch.py Wed Dec 23 19:18:03 2009 +0100
+++ b/mercurial/patch.py Wed Dec 23 19:31:47 2009 +0100
@@ -321,14 +321,14 @@
else:
fp = self.opener(fname, 'w')
try:
- if self.eolmode == 'auto' and self.eol:
+ if self.eolmode == 'auto':
eol = self.eol
elif self.eolmode == 'crlf':
eol = '\r\n'
else:
eol = '\n'
- if self.eolmode != 'strict' and eol != '\n':
+ if self.eolmode != 'strict' and eol and eol != '\n':
for l in lines:
if l and l[-1] == '\n':
l = l[:-1] + eol
@@ -433,6 +433,15 @@
self.dirty = 1
return 0
+ horig = h
+ if self.eolmode == 'auto' and self.eol:
+ # If eolmode == 'auto' and target file exists and has line
+ # endings we have to normalize input data before patching.
+ # Otherwise, patchfile operates in 'strict' mode. If
+ # eolmode is set to 'crlf' or 'lf', input hunk is already
+ # normalized to avoid data copy.
+ h = h.getnormalized()
+
# fast case first, no offsets, no fuzz
old = h.old()
# patch starts counting at 1 unless we are adding the file
@@ -488,7 +497,7 @@
return fuzzlen
self.printfile(True)
self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
- self.rej.append(h)
+ self.rej.append(horig)
return -1
class hunk(object):
@@ -500,13 +509,39 @@
self.b = []
self.starta = self.lena = None
self.startb = self.lenb = None
- if context:
- self.read_context_hunk(lr)
- else:
- self.read_unified_hunk(lr)
+ if lr is not None:
+ if context:
+ self.read_context_hunk(lr)
+ else:
+ self.read_unified_hunk(lr)
self.create = create
self.remove = remove and not create
+ def getnormalized(self):
+ """Return a copy with line endings normalized to LF."""
+
+ def normalize(lines):
+ nlines = []
+ for line in lines:
+ if line.endswith('\r\n'):
+ line = line[:-2] + '\n'
+ nlines.append(line)
+ return nlines
+
+ # Dummy object, it is rebuilt manually
+ nh = hunk(self.desc, self.number, None, None, False, False)
+ nh.number = self.number
+ nh.desc = self.desc
+ nh.a = normalize(self.a)
+ nh.b = normalize(self.b)
+ nh.starta = self.starta
+ nh.startb = self.startb
+ nh.lena = self.lena
+ nh.lenb = self.lenb
+ nh.create = self.create
+ nh.remove = self.remove
+ return nh
+
def read_unified_hunk(self, lr):
m = unidesc.match(self.desc)
if not m:
@@ -974,7 +1009,10 @@
current_file = None
gitpatches = None
opener = util.opener(os.getcwd())
- textmode = eolmode != 'strict'
+ # In 'auto' mode, we must preserve original eols if target file
+ # eols are undefined. Otherwise, hunk data will be normalized
+ # later.
+ textmode = eolmode not in ('strict', 'auto')
def closefile():
if not current_file:
--- a/tests/test-import-eol Wed Dec 23 19:18:03 2009 +0100
+++ b/tests/test-import-eol Wed Dec 23 19:31:47 2009 +0100
@@ -57,6 +57,21 @@
python -c 'print repr(file("a","rb").read())'
hg st
+echo % auto EOL on new file or source without any EOL
+python -c 'file("noeol", "wb").write("noeol")'
+hg add noeol
+hg commit -m 'add noeol'
+python -c 'file("noeol", "wb").write("noeol\r\nnoeol\n")'
+python -c 'file("neweol", "wb").write("neweol\nneweol\r\n")'
+hg add neweol
+hg diff --git > noeol.diff
+hg revert --no-backup noeol neweol
+rm neweol
+hg --traceback --config patch.eol='auto' import -m noeol noeol.diff
+python -c 'print repr(file("noeol","rb").read())'
+python -c 'print repr(file("neweol","rb").read())'
+hg st
+
# Test --eol and binary patches
python -c 'file("b", "wb").write("a\x00\nb")'
hg ci -Am addb
--- a/tests/test-import-eol.out Wed Dec 23 19:18:03 2009 +0100
+++ b/tests/test-import-eol.out Wed Dec 23 19:31:47 2009 +0100
@@ -17,6 +17,10 @@
% auto EOL on CRLF file
applying eol.diff
'a\r\nyyyy\r\ncc\r\n\r\nd\r\ne'
+% auto EOL on new file or source without any EOL
+applying noeol.diff
+'noeol\r\nnoeol\n'
+'neweol\nneweol\r\n'
adding b
% binary patch with --eol
applying bin.diff