# HG changeset patch # User Patrick Mezard # Date 1261593107 -3600 # Node ID d8214e944b84a1a8b2e3fc0fe9d2c97963c096a2 # Parent 78b8acae2088fca2b459adb7d288c15fec6816d5 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. diff -r 78b8acae2088 -r d8214e944b84 doc/hgrc.5.txt --- 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. diff -r 78b8acae2088 -r d8214e944b84 mercurial/patch.py --- 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: diff -r 78b8acae2088 -r d8214e944b84 tests/test-import-eol --- 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 diff -r 78b8acae2088 -r d8214e944b84 tests/test-import-eol.out --- 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