# HG changeset patch # User Martin Geisler # Date 2010-05-31 19:37:01 # Node ID 0bb67503ad4b05a91531e6e6a066e2b4682a9de6 # Parent 8f5ad12db28e25c7d2f247fab1c951adaaf10c81 eol: extension for managing file EOLs diff --git a/hgext/eol.py b/hgext/eol.py new file mode 100644 --- /dev/null +++ b/hgext/eol.py @@ -0,0 +1,256 @@ +"""automatically manage newlines in repository files + +This extension allows you to manage the type of line endings (CRLF or +LF) that are used in the repository and in the local working +directory. That way you can get CRLF line endings on Windows and LF on +Unix/Mac, thereby letting everybody use their OS native line endings. + +The extension reads its configuration from a versioned ``.hgeol`` +configuration file every time you run an ``hg`` command. The +``.hgeol`` file use the same syntax as all other Mercurial +configuration files. It uses two sections, ``[patterns]`` and +``[repository]``. + +The ``[patterns]`` section specifies the line endings used in the +working directory. The format is specified by a file pattern. The +first match is used, so put more specific patterns first. The +available line endings are ``LF``, ``CRLF``, and ``BIN``. + +Files with the declared format of ``CRLF`` or ``LF`` are always +checked out in that format and files declared to be binary (``BIN``) +are left unchanged. Additionally, ``native`` is an alias for the +platform's default line ending: ``LF`` on Unix (including Mac OS X) +and ``CRLF`` on Windows. Note that ``BIN`` (do nothing to line +endings) is Mercurial's default behaviour; it is only needed if you +need to override a later, more general pattern. + +The optional ``[repository]`` section specifies the line endings to +use for files stored in the repository. It has a single setting, +``native``, which determines the storage line endings for files +declared as ``native`` in the ``[patterns]`` section. It can be set to +``LF`` or ``CRLF``. The default is ``LF``. For example, this means +that on Windows, files configured as ``native`` (``CRLF`` by default) +will be converted to ``LF`` when stored in the repository. Files +declared as ``LF``, ``CRLF``, or ``BIN`` in the ``[patterns]`` section +are always stored as-is in the repository. + +Example versioned ``.hgeol`` file:: + + [patterns] + **.py = native + **.vcproj = CRLF + **.txt = native + Makefile = LF + **.jpg = BIN + + [repository] + native = LF + +The extension uses an optional ``[eol]`` section in your hgrc file +(not the ``.hgeol`` file) for settings that control the overall +behavior. There are two settings: + +- ``eol.native`` (default ``os.linesep``) can be set to ``LF`` or + ``CRLF`` override the default interpretation of ``native`` for + checkout. This can be used with :hg:`archive` on Unix, say, to + generate an archive where files have line endings for Windows. + +- ``eol.only-consistent`` (default True) can be set to False to make + the extension convert files with inconsistent EOLs. Inconsistent + means that there is both ``CRLF`` and ``LF`` present in the file. + Such files are normally not touched under the assumption that they + have mixed EOLs on purpose. + +See :hg:`help patterns` for more information about the glob patterns +used. +""" + +from mercurial.i18n import _ +from mercurial import util, config, extensions, commands, match, cmdutil +import re, os + +# Matches a lone LF, i.e., one that is not part of CRLF. +singlelf = re.compile('(^|[^\r])\n') +# Matches a single EOL which can either be a CRLF where repeated CR +# are removed or a LF. We do not care about old Machintosh files, so a +# stray CR is an error. +eolre = re.compile('\r*\n') + + +def inconsistenteol(data): + return '\r\n' in data and singlelf.search(data) + +def tolf(s, params, ui, **kwargs): + """Filter to convert to LF EOLs.""" + if util.binary(s): + return s + if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s): + return s + return eolre.sub('\n', s) + +def tocrlf(s, params, ui, **kwargs): + """Filter to convert to CRLF EOLs.""" + if util.binary(s): + return s + if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s): + return s + return eolre.sub('\r\n', s) + +def isbinary(s, params): + """Filter to do nothing with the file.""" + return s + +filters = { + 'to-lf': tolf, + 'to-crlf': tocrlf, + 'is-binary': isbinary, +} + + +def hook(ui, repo, node, hooktype, **kwargs): + """verify that files have expected EOLs""" + files = set() + for rev in xrange(repo[node].rev(), len(repo)): + files.update(repo[rev].files()) + tip = repo['tip'] + for f in files: + if f not in tip: + continue + for pattern, target in ui.configitems('encode'): + if match.match(repo.root, '', [pattern])(f): + data = tip[f].data() + if target == "to-lf" and "\r\n" in data: + raise util.Abort(_("%s should not have CRLF line endings") + % f) + elif target == "to-crlf" and singlelf.search(data): + raise util.Abort(_("%s should not have LF line endings") + % f) + + +def preupdate(ui, repo, hooktype, parent1, parent2): + #print "preupdate for %s: %s -> %s" % (repo.root, parent1, parent2) + repo.readhgeol(parent1) + return False + +def uisetup(ui): + ui.setconfig('hooks', 'preupdate.eol', preupdate) + +def extsetup(ui): + try: + extensions.find('win32text') + raise util.Abort(_("the eol extension is incompatible with the " + "win32text extension")) + except KeyError: + pass + + +def reposetup(ui, repo): + #print "reposetup for", repo.root + + if not repo.local(): + return + for name, fn in filters.iteritems(): + repo.adddatafilter(name, fn) + + ui.setconfig('patch', 'eol', 'auto') + + class eolrepo(repo.__class__): + + _decode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'} + _encode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'} + + def readhgeol(self, node=None, data=None): + if data is None: + try: + if node is None: + data = self.wfile('.hgeol').read() + else: + data = self[node]['.hgeol'].data() + except (IOError, LookupError): + return None + + if self.ui.config('eol', 'native', os.linesep) in ('LF', '\n'): + self._decode['NATIVE'] = 'to-lf' + else: + self._decode['NATIVE'] = 'to-crlf' + + eol = config.config() + eol.parse('.hgeol', data) + + if eol.get('repository', 'native') == 'CRLF': + self._encode['NATIVE'] = 'to-crlf' + else: + self._encode['NATIVE'] = 'to-lf' + + for pattern, style in eol.items('patterns'): + key = style.upper() + try: + self.ui.setconfig('decode', pattern, self._decode[key]) + self.ui.setconfig('encode', pattern, self._encode[key]) + except KeyError: + self.ui.warn(_("ignoring unknown EOL style '%s' from %s\n") + % (style, eol.source('patterns', pattern))) + + include = [] + exclude = [] + for pattern, style in eol.items('patterns'): + key = style.upper() + if key == 'BIN': + exclude.append(pattern) + else: + include.append(pattern) + + # This will match the files for which we need to care + # about inconsistent newlines. + return match.match(self.root, '', [], include, exclude) + + def _hgcleardirstate(self): + self._eolfile = self.readhgeol() or self.readhgeol('tip') + + if not self._eolfile: + self._eolfile = util.never + return + + try: + cachemtime = os.path.getmtime(self.join("eol.cache")) + except OSError: + cachemtime = 0 + + try: + eolmtime = os.path.getmtime(self.wjoin(".hgeol")) + except OSError: + eolmtime = 0 + + if eolmtime > cachemtime: + ui.debug("eol: detected change in .hgeol\n") + # TODO: we could introduce a method for this in dirstate. + wlock = None + try: + wlock = self.wlock() + for f, e in self.dirstate._map.iteritems(): + self.dirstate._map[f] = (e[0], e[1], -1, 0) + self.dirstate._dirty = True + # Touch the cache to update mtime. TODO: are we sure this + # always enought to update the mtime, or should we write a + # bit to the file? + self.opener("eol.cache", "w").close() + finally: + if wlock is not None: + wlock.release() + + def commitctx(self, ctx, error=False): + for f in sorted(ctx.added() + ctx.modified()): + if not self._eolfile(f): + continue + data = ctx[f].data() + if util.binary(data): + # We should not abort here, since the user should + # be able to say "** = native" to automatically + # have all non-binary files taken care of. + continue + if inconsistenteol(data): + raise util.Abort(_("inconsistent newline style " + "in %s\n" % f)) + return super(eolrepo, self).commitctx(ctx, error) + repo.__class__ = eolrepo + repo._hgcleardirstate() diff --git a/tests/test-eol b/tests/test-eol new file mode 100755 --- /dev/null +++ b/tests/test-eol @@ -0,0 +1,180 @@ +#!/bin/sh + +cat > $HGRCPATH < switch-eol.py < .hgeol < a.txt + hg commit --addremove -m 'checkin' + echo + cd .. +} + +dotest () { + seteol $1 + echo "% hg clone repo repo-$1" + hg clone --noupdate repo repo-$1 + cd repo-$1 + + cat > .hg/hgrc <> a.txt + echo '% printrepr.py a.txt' + python $TESTDIR/printrepr.py < a.txt + hg diff | python $TESTDIR/printrepr.py + + python ../switch-eol.py $1 a.txt + echo '% hg diff only reports a single changed line:' + hg diff | python $TESTDIR/printrepr.py + + echo "% reverting back to $1 format" + hg revert a.txt + python $TESTDIR/printrepr.py < a.txt + + printf "first\r\nsecond\n" > mixed.txt + hg add mixed.txt + echo "% hg commit of inconsistent .txt file marked as binary (should work)" + hg commit -m 'binary file' + + echo "% hg commit of inconsistent .txt file marked as native (should fail)" + printf "first\nsecond\r\nthird\nfourth\r\n" > a.txt + hg commit -m 'inconsistent file' + + echo "% hg commit --config eol.only-consistent=False (should work)" + hg commit --config eol.only-consistent=False -m 'inconsistent file' + + echo "% hg commit of binary .txt file marked as native (binary files always okay)" + printf "first${EOL}\0${EOL}third${EOL}" > a.txt + hg commit -m 'binary file' + + cd .. + rm -r repo-$1 +} + +makerepo LF +dotest LF +dotest CRLF +rm -r repo + +makerepo CRLF +dotest LF +dotest CRLF +rm -r repo + + +makemixedrepo () { + echo + echo "# setup $1 repository" + hg init mixed + cd mixed + printf "foo\r\nbar\r\nbaz\r\n" > win.txt + printf "foo\nbar\nbaz\n" > unix.txt + #printf "foo\r\nbar\nbaz\r\n" > mixed.txt + + hg commit --addremove -m 'created mixed files' + + echo "# setting repository-native EOLs to $1" + cat > .hgeol < .hg/hgrc < $HGRCPATH < a.txt + hg commit -d '100 0' --addremove -m 'LF commit' + cd .. +} + +dotest () { + seteol $1 + + echo + echo "% hg clone repo repo-$1" + hg clone repo repo-$1 + cd repo-$1 + + cat > .hg/hgrc < .hgeol < $HGRCPATH < .hgeol < a.txt +hg commit --addremove -m 'checkin' +cd .. + +echo "% hg clone repo repo-2" +hg clone repo repo-2 +cd repo-2 + +echo '% printrepr.py a.txt' +python $TESTDIR/printrepr.py < a.txt +echo '% hg cat a.txt' +hg cat a.txt | python $TESTDIR/printrepr.py + +hg remove .hgeol +hg commit -m 'remove eol' +hg push --quiet + +cd .. + +# Test clone of repo with .hgeol in working dir, but no .hgeol in tip +echo "% hg clone repo repo-3" +hg clone repo repo-3 +cd repo-3 + +echo '% printrepr.py a.txt' +python $TESTDIR/printrepr.py < a.txt + +cd .. + +# Test clone of revision with .hgeol +echo "% hg clone -r 1 repo repo-4" +hg clone -r 0 repo repo-4 +cd repo-4 + +echo '% cat .hgeol' +cat .hgeol + +echo '% printrepr.py a.txt' +python $TESTDIR/printrepr.py < a.txt + +cd .. diff --git a/tests/test-eol-clone.out b/tests/test-eol-clone.out new file mode 100644 --- /dev/null +++ b/tests/test-eol-clone.out @@ -0,0 +1,36 @@ +% setup repository +adding .hgeol +adding a.txt +% hg clone repo repo-2 +updating to branch default +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +% printrepr.py a.txt +first\r +second\r +third\r +% hg cat a.txt +first +second +third +% hg clone repo repo-3 +updating to branch default +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +% printrepr.py a.txt +first +second +third +% hg clone -r 1 repo repo-4 +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 2 changes to 2 files +updating to branch default +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +% cat .hgeol +[patterns] +**.txt = native +% printrepr.py a.txt +first\r +second\r +third\r diff --git a/tests/test-eol-hook b/tests/test-eol-hook new file mode 100755 --- /dev/null +++ b/tests/test-eol-hook @@ -0,0 +1,47 @@ +#!/bin/sh + +cat > $HGRCPATH < main/.hg/hgrc < .hgeol < a.txt +hg add a.txt +echo "% hg commit (LF a.txt)" +hg commit -m 'LF a.txt' +echo "% hg push" +hg push ../main + +printf "first\r\nsecond\r\nthird\n" > a.txt +echo "% hg commit (CRLF a.txt)" +hg commit -m 'CRLF a.txt' +echo "% hg push" +hg push ../main + + +echo "% hg commit (LF a.txt)" +printf "first\nsecond\nthird\n" > a.txt +hg commit -m 'LF a.txt (fixed)' +echo "% hg push" +hg push ../main diff --git a/tests/test-eol-hook.out b/tests/test-eol-hook.out new file mode 100644 --- /dev/null +++ b/tests/test-eol-hook.out @@ -0,0 +1,30 @@ +updating to branch default +0 files updated, 0 files merged, 0 files removed, 0 files unresolved +% hg commit (LF a.txt) +% hg push +pushing to ../main +searching for changes +adding changesets +adding manifests +adding file changes +added 2 changesets with 2 changes to 2 files +% hg commit (CRLF a.txt) +% hg push +pushing to ../main +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +error: pretxnchangegroup hook failed: a.txt should not have CRLF line endings +transaction abort! +rollback completed +abort: a.txt should not have CRLF line endings +% hg commit (LF a.txt) +% hg push +pushing to ../main +searching for changes +adding changesets +adding manifests +adding file changes +added 2 changesets with 2 changes to 1 files diff --git a/tests/test-eol-patch b/tests/test-eol-patch new file mode 100755 --- /dev/null +++ b/tests/test-eol-patch @@ -0,0 +1,103 @@ +#!/bin/sh + +cat > $HGRCPATH < .hgeol < win.txt + printf "first\nsecond\nthird\n" > unix.txt + printf "first${EOL}second${EOL}third${EOL}" > native.txt + hg commit --addremove -m 'checkin' + cd .. +} + +dotest () { + seteol $1 + + echo + echo "% hg clone repo repo-$1" + hg clone --noupdate repo repo-$1 + cd repo-$1 + + cat > .hg/hgrc < native.txt + printf "first\r\nthird\r\n" > win.txt + printf "first\nthird\n" > unix.txt + + echo '% hg diff' + hg diff > p + python $TESTDIR/printrepr.py < p + + echo '% hg revert' + hg revert --all + + echo '% hg import' + hg import -m 'patch' p + + echo '% printrepr.py native.txt' + python $TESTDIR/printrepr.py < native.txt + echo '% printrepr.py unix.txt' + python $TESTDIR/printrepr.py < unix.txt + echo '% printrepr.py win.txt' + python $TESTDIR/printrepr.py < win.txt + + echo '% hg diff -c tip' + hg diff -c tip | python $TESTDIR/printrepr.py + + cd .. + rm -r repo-$1 +} + +makerepo LF +dotest LF +dotest CRLF +rm -r repo + +makerepo CRLF +dotest LF +dotest CRLF +rm -r repo diff --git a/tests/test-eol-patch.out b/tests/test-eol-patch.out new file mode 100644 --- /dev/null +++ b/tests/test-eol-patch.out @@ -0,0 +1,310 @@ + +# ==== setup LF repository ==== +% hg init +adding .hgeol +adding native.txt +adding unix.txt +adding win.txt + +% hg clone repo repo-LF +4 files updated, 0 files merged, 0 files removed, 0 files unresolved +% printrepr.py native.txt +first +second +third +% printrepr.py unix.txt +first +second +third +% printrepr.py win.txt +first\r +second\r +third\r +% hg diff +diff --git a/native.txt b/native.txt +--- a/native.txt ++++ b/native.txt +@@ -1,3 +1,2 @@ + first +-second + third +diff --git a/unix.txt b/unix.txt +--- a/unix.txt ++++ b/unix.txt +@@ -1,3 +1,2 @@ + first +-second + third +diff --git a/win.txt b/win.txt +--- a/win.txt ++++ b/win.txt +@@ -1,3 +1,2 @@ + first\r +-second\r + third\r +% hg revert +reverting native.txt +reverting unix.txt +reverting win.txt +% hg import +applying p +% printrepr.py native.txt +first +third +% printrepr.py unix.txt +first +third +% printrepr.py win.txt +first\r +third\r +% hg diff -c tip +diff --git a/native.txt b/native.txt +--- a/native.txt ++++ b/native.txt +@@ -1,3 +1,2 @@ + first +-second + third +diff --git a/unix.txt b/unix.txt +--- a/unix.txt ++++ b/unix.txt +@@ -1,3 +1,2 @@ + first +-second + third +diff --git a/win.txt b/win.txt +--- a/win.txt ++++ b/win.txt +@@ -1,3 +1,2 @@ + first\r +-second\r + third\r + +% hg clone repo repo-CRLF +4 files updated, 0 files merged, 0 files removed, 0 files unresolved +% printrepr.py native.txt +first\r +second\r +third\r +% printrepr.py unix.txt +first +second +third +% printrepr.py win.txt +first\r +second\r +third\r +% hg diff +diff --git a/native.txt b/native.txt +--- a/native.txt ++++ b/native.txt +@@ -1,3 +1,2 @@ + first +-second + third +diff --git a/unix.txt b/unix.txt +--- a/unix.txt ++++ b/unix.txt +@@ -1,3 +1,2 @@ + first +-second + third +diff --git a/win.txt b/win.txt +--- a/win.txt ++++ b/win.txt +@@ -1,3 +1,2 @@ + first\r +-second\r + third\r +% hg revert +reverting native.txt +reverting unix.txt +reverting win.txt +% hg import +applying p +% printrepr.py native.txt +first\r +third\r +% printrepr.py unix.txt +first +third +% printrepr.py win.txt +first\r +third\r +% hg diff -c tip +diff --git a/native.txt b/native.txt +--- a/native.txt ++++ b/native.txt +@@ -1,3 +1,2 @@ + first +-second + third +diff --git a/unix.txt b/unix.txt +--- a/unix.txt ++++ b/unix.txt +@@ -1,3 +1,2 @@ + first +-second + third +diff --git a/win.txt b/win.txt +--- a/win.txt ++++ b/win.txt +@@ -1,3 +1,2 @@ + first\r +-second\r + third\r + +# ==== setup CRLF repository ==== +% hg init +adding .hgeol +adding native.txt +adding unix.txt +adding win.txt + +% hg clone repo repo-LF +4 files updated, 0 files merged, 0 files removed, 0 files unresolved +% printrepr.py native.txt +first +second +third +% printrepr.py unix.txt +first +second +third +% printrepr.py win.txt +first\r +second\r +third\r +% hg diff +diff --git a/native.txt b/native.txt +--- a/native.txt ++++ b/native.txt +@@ -1,3 +1,2 @@ + first\r +-second\r + third\r +diff --git a/unix.txt b/unix.txt +--- a/unix.txt ++++ b/unix.txt +@@ -1,3 +1,2 @@ + first +-second + third +diff --git a/win.txt b/win.txt +--- a/win.txt ++++ b/win.txt +@@ -1,3 +1,2 @@ + first\r +-second\r + third\r +% hg revert +reverting native.txt +reverting unix.txt +reverting win.txt +% hg import +applying p +% printrepr.py native.txt +first +third +% printrepr.py unix.txt +first +third +% printrepr.py win.txt +first\r +third\r +% hg diff -c tip +diff --git a/native.txt b/native.txt +--- a/native.txt ++++ b/native.txt +@@ -1,3 +1,2 @@ + first\r +-second\r + third\r +diff --git a/unix.txt b/unix.txt +--- a/unix.txt ++++ b/unix.txt +@@ -1,3 +1,2 @@ + first +-second + third +diff --git a/win.txt b/win.txt +--- a/win.txt ++++ b/win.txt +@@ -1,3 +1,2 @@ + first\r +-second\r + third\r + +% hg clone repo repo-CRLF +4 files updated, 0 files merged, 0 files removed, 0 files unresolved +% printrepr.py native.txt +first\r +second\r +third\r +% printrepr.py unix.txt +first +second +third +% printrepr.py win.txt +first\r +second\r +third\r +% hg diff +diff --git a/native.txt b/native.txt +--- a/native.txt ++++ b/native.txt +@@ -1,3 +1,2 @@ + first\r +-second\r + third\r +diff --git a/unix.txt b/unix.txt +--- a/unix.txt ++++ b/unix.txt +@@ -1,3 +1,2 @@ + first +-second + third +diff --git a/win.txt b/win.txt +--- a/win.txt ++++ b/win.txt +@@ -1,3 +1,2 @@ + first\r +-second\r + third\r +% hg revert +reverting native.txt +reverting unix.txt +reverting win.txt +% hg import +applying p +% printrepr.py native.txt +first\r +third\r +% printrepr.py unix.txt +first +third +% printrepr.py win.txt +first\r +third\r +% hg diff -c tip +diff --git a/native.txt b/native.txt +--- a/native.txt ++++ b/native.txt +@@ -1,3 +1,2 @@ + first\r +-second\r + third\r +diff --git a/unix.txt b/unix.txt +--- a/unix.txt ++++ b/unix.txt +@@ -1,3 +1,2 @@ + first +-second + third +diff --git a/win.txt b/win.txt +--- a/win.txt ++++ b/win.txt +@@ -1,3 +1,2 @@ + first\r +-second\r + third\r diff --git a/tests/test-eol-update b/tests/test-eol-update new file mode 100755 --- /dev/null +++ b/tests/test-eol-update @@ -0,0 +1,83 @@ +#!/bin/sh + +cat > $HGRCPATH < .hgeol < a.txt + hg commit --addremove -m 'LF commit' + + cat > .hgeol < a.txt + hg commit -m 'CRLF commit' + + cd .. +} + +dotest () { + seteol $1 + + echo + echo "% hg clone repo repo-$1" + hg clone --noupdate repo repo-$1 + cd repo-$1 + + cat > .hg/hgrc < a.txt + + echo '% printrepr.py a.txt (after)' + python $TESTDIR/printrepr.py < a.txt + echo '% hg diff' + hg diff | python $TESTDIR/printrepr.py + + echo '% hg update 0' + hg update 0 + + echo '% printrepr.py a.txt' + python $TESTDIR/printrepr.py < a.txt + echo '% hg diff' + hg diff | python $TESTDIR/printrepr.py + + + cd .. + rm -r repo-$1 +} + +makerepo +dotest LF +dotest CRLF +rm -r repo diff --git a/tests/test-eol-update.out b/tests/test-eol-update.out new file mode 100644 --- /dev/null +++ b/tests/test-eol-update.out @@ -0,0 +1,69 @@ + +# ==== setup repository ==== +% hg init +adding .hgeol +adding a.txt + +% hg clone repo repo-LF +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +% printrepr.py a.txt (before) +first\r +second\r +third\r +% printrepr.py a.txt (after) +first +third +% hg diff +diff --git a/a.txt b/a.txt +--- a/a.txt ++++ b/a.txt +@@ -1,3 +1,2 @@ + first\r +-second\r + third\r +% hg update 0 +merging a.txt +1 files updated, 1 files merged, 0 files removed, 0 files unresolved +% printrepr.py a.txt +first\r +third\r +% hg diff +diff --git a/a.txt b/a.txt +--- a/a.txt ++++ b/a.txt +@@ -1,3 +1,2 @@ + first +-second + third + +% hg clone repo repo-CRLF +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +% printrepr.py a.txt (before) +first\r +second\r +third\r +% printrepr.py a.txt (after) +first\r +third\r +% hg diff +diff --git a/a.txt b/a.txt +--- a/a.txt ++++ b/a.txt +@@ -1,3 +1,2 @@ + first\r +-second\r + third\r +% hg update 0 +merging a.txt +1 files updated, 1 files merged, 0 files removed, 0 files unresolved +% printrepr.py a.txt +first\r +third\r +% hg diff +diff --git a/a.txt b/a.txt +--- a/a.txt ++++ b/a.txt +@@ -1,3 +1,2 @@ + first +-second + third diff --git a/tests/test-eol.out b/tests/test-eol.out new file mode 100644 --- /dev/null +++ b/tests/test-eol.out @@ -0,0 +1,228 @@ +% setup LF repository +adding .hgeol +adding a.txt + +% hg clone repo repo-LF +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +% printrepr.py a.txt +first +second +third +% hg cat a.txt +first +second +third +% printrepr.py a.txt +first +second +third +fourth +diff --git a/a.txt b/a.txt +--- a/a.txt ++++ b/a.txt +@@ -1,3 +1,4 @@ + first + second + third ++fourth +% switching encoding from '\n' to '\r\n' +% hg diff only reports a single changed line: +diff --git a/a.txt b/a.txt +--- a/a.txt ++++ b/a.txt +@@ -1,3 +1,4 @@ + first + second + third ++fourth +% reverting back to LF format +first +second +third +% hg commit of inconsistent .txt file marked as binary (should work) +% hg commit of inconsistent .txt file marked as native (should fail) +abort: inconsistent newline style in a.txt + +% hg commit --config eol.only-consistent=False (should work) +% hg commit of binary .txt file marked as native (binary files always okay) +% hg clone repo repo-CRLF +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +% printrepr.py a.txt +first\r +second\r +third\r +% hg cat a.txt +first +second +third +% printrepr.py a.txt +first\r +second\r +third\r +fourth\r +diff --git a/a.txt b/a.txt +--- a/a.txt ++++ b/a.txt +@@ -1,3 +1,4 @@ + first + second + third ++fourth +% switching encoding from '\r\n' to '\n' +% hg diff only reports a single changed line: +diff --git a/a.txt b/a.txt +--- a/a.txt ++++ b/a.txt +@@ -1,3 +1,4 @@ + first + second + third ++fourth +% reverting back to CRLF format +first\r +second\r +third\r +% hg commit of inconsistent .txt file marked as binary (should work) +% hg commit of inconsistent .txt file marked as native (should fail) +abort: inconsistent newline style in a.txt + +% hg commit --config eol.only-consistent=False (should work) +% hg commit of binary .txt file marked as native (binary files always okay) +% setup CRLF repository +adding .hgeol +adding a.txt + +% hg clone repo repo-LF +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +% printrepr.py a.txt +first +second +third +% hg cat a.txt +first\r +second\r +third\r +% printrepr.py a.txt +first +second +third +fourth +diff --git a/a.txt b/a.txt +--- a/a.txt ++++ b/a.txt +@@ -1,3 +1,4 @@ + first\r + second\r + third\r ++fourth\r +% switching encoding from '\n' to '\r\n' +% hg diff only reports a single changed line: +diff --git a/a.txt b/a.txt +--- a/a.txt ++++ b/a.txt +@@ -1,3 +1,4 @@ + first\r + second\r + third\r ++fourth\r +% reverting back to LF format +first +second +third +% hg commit of inconsistent .txt file marked as binary (should work) +% hg commit of inconsistent .txt file marked as native (should fail) +abort: inconsistent newline style in a.txt + +% hg commit --config eol.only-consistent=False (should work) +% hg commit of binary .txt file marked as native (binary files always okay) +% hg clone repo repo-CRLF +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +% printrepr.py a.txt +first\r +second\r +third\r +% hg cat a.txt +first\r +second\r +third\r +% printrepr.py a.txt +first\r +second\r +third\r +fourth\r +diff --git a/a.txt b/a.txt +--- a/a.txt ++++ b/a.txt +@@ -1,3 +1,4 @@ + first\r + second\r + third\r ++fourth\r +% switching encoding from '\r\n' to '\n' +% hg diff only reports a single changed line: +diff --git a/a.txt b/a.txt +--- a/a.txt ++++ b/a.txt +@@ -1,3 +1,4 @@ + first\r + second\r + third\r ++fourth\r +% reverting back to CRLF format +first\r +second\r +third\r +% hg commit of inconsistent .txt file marked as binary (should work) +% hg commit of inconsistent .txt file marked as native (should fail) +abort: inconsistent newline style in a.txt + +% hg commit --config eol.only-consistent=False (should work) +% hg commit of binary .txt file marked as native (binary files always okay) + +# setup LF repository +adding unix.txt +adding win.txt +# setting repository-native EOLs to LF +adding .hgeol + +% hg clone mixed mixed-LF +updating to branch default +3 files updated, 0 files merged, 0 files removed, 0 files unresolved +% hg status (eol extension not yet activated) +% hg status (eol activated) +M win.txt +% hg commit +% hg status + +% hg clone mixed mixed-CRLF +updating to branch default +3 files updated, 0 files merged, 0 files removed, 0 files unresolved +% hg status (eol extension not yet activated) +% hg status (eol activated) +M win.txt +% hg commit +% hg status + +# setup CRLF repository +adding unix.txt +adding win.txt +# setting repository-native EOLs to CRLF +adding .hgeol + +% hg clone mixed mixed-LF +updating to branch default +3 files updated, 0 files merged, 0 files removed, 0 files unresolved +% hg status (eol extension not yet activated) +% hg status (eol activated) +M unix.txt +% hg commit +% hg status + +% hg clone mixed mixed-CRLF +updating to branch default +3 files updated, 0 files merged, 0 files removed, 0 files unresolved +% hg status (eol extension not yet activated) +% hg status (eol activated) +M unix.txt +% hg commit +% hg status