diff --git a/hgext/win32text.py b/hgext/win32text.py --- a/hgext/win32text.py +++ b/hgext/win32text.py @@ -1,4 +1,4 @@ -# win32text.py - LF <-> CRLF translation utilities for Windows users +# win32text.py - LF <-> CRLF/CR translation utilities for Windows/Mac users # # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. @@ -9,18 +9,23 @@ # hgext.win32text = # [encode] # ** = cleverencode: +# # or ** = macencode: # [decode] # ** = cleverdecode: +# # or ** = macdecode: # -# If not doing conversion, to make sure you do not commit CRLF by accident: +# If not doing conversion, to make sure you do not commit CRLF/CR by accident: # # [hooks] # pretxncommit.crlf = python:hgext.win32text.forbidcrlf +# # or pretxncommit.cr = python:hgext.win32text.forbidcr # -# To do the same check on a server to prevent CRLF from being pushed or pulled: +# To do the same check on a server to prevent CRLF/CR from being pushed or +# pulled: # # [hooks] # pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf +# # or pretxnchangegroup.cr = python:hgext.win32text.forbidcr from mercurial import util from mercurial.i18n import gettext as _ @@ -30,23 +35,36 @@ import re # regexp for single LF without CR preceding. re_single_lf = re.compile('(^|[^\r])\n', re.MULTILINE) -def dumbdecode(s, cmd, ui=None, repo=None, filename=None, **kwargs): - # warn if already has CRLF in repository. +newlinestr = {'\r\n': 'CRLF', '\r': 'CR'} +filterstr = {'\r\n': 'clever', '\r': 'mac'} + +def checknewline(s, newline, ui=None, repo=None, filename=None): + # warn if already has 'newline' in repository. # it might cause unexpected eol conversion. # see issue 302: # http://www.selenic.com/mercurial/bts/issue302 - if '\r\n' in s and ui and filename and repo: - ui.warn(_('WARNING: %s already has CRLF line endings\n' + if newline in s and ui and filename and repo: + ui.warn(_('WARNING: %s already has %s line endings\n' 'and does not need EOL conversion by the win32text plugin.\n' 'Before your next commit, please reconsider your ' 'encode/decode settings in \nMercurial.ini or %s.\n') % - (filename, repo.join('hgrc'))) + (filename, newlinestr[newline], repo.join('hgrc'))) + +def dumbdecode(s, cmd, **kwargs): + checknewline(s, '\r\n', **kwargs) # replace single LF to CRLF return re_single_lf.sub('\\1\r\n', s) def dumbencode(s, cmd): return s.replace('\r\n', '\n') +def macdumbdecode(s, cmd, **kwargs): + checknewline(s, '\r', **kwargs) + return s.replace('\n', '\r') + +def macdumbencode(s, cmd): + return s.replace('\r', '\n') + def cleverdecode(s, cmd, **kwargs): if util.binary(s): return s @@ -57,14 +75,28 @@ def cleverencode(s, cmd): return s return dumbencode(s, cmd) +def macdecode(s, cmd, **kwargs): + if util.binary(s): + return s + return macdumbdecode(s, cmd, **kwargs) + +def macencode(s, cmd): + if util.binary(s): + return s + return macdumbencode(s, cmd) + _filters = { 'dumbdecode:': dumbdecode, 'dumbencode:': dumbencode, 'cleverdecode:': cleverdecode, 'cleverencode:': cleverencode, + 'macdumbdecode:': macdumbdecode, + 'macdumbencode:': macdumbencode, + 'macdecode:': macdecode, + 'macencode:': macencode, } -def forbidcrlf(ui, repo, hooktype, node, **kwargs): +def forbidcrlforcr(ui, repo, hooktype, node, newline, **kwargs): halt = False for rev in xrange(repo.changelog.rev(bin(node)), repo.changelog.count()): c = repo.changectx(rev) @@ -72,29 +104,38 @@ def forbidcrlf(ui, repo, hooktype, node, if f not in c: continue data = c[f].data() - if not util.binary(data) and '\r\n' in data: + if not util.binary(data) and newline in data: if not halt: ui.warn(_('Attempt to commit or push text file(s) ' - 'using CRLF line endings\n')) + 'using %s line endings\n') % + newlinestr[newline]) ui.warn(_('in %s: %s\n') % (short(c.node()), f)) halt = True if halt and hooktype == 'pretxnchangegroup': + crlf = newlinestr[newline].lower() + filter = filterstr[newline] ui.warn(_('\nTo prevent this mistake in your local repository,\n' 'add to Mercurial.ini or .hg/hgrc:\n' '\n' '[hooks]\n' - 'pretxncommit.crlf = python:hgext.win32text.forbidcrlf\n' + 'pretxncommit.%s = python:hgext.win32text.forbid%s\n' '\n' 'and also consider adding:\n' '\n' '[extensions]\n' 'hgext.win32text =\n' '[encode]\n' - '** = cleverencode:\n' + '** = %sencode:\n' '[decode]\n' - '** = cleverdecode:\n')) + '** = %sdecode:\n') % (crlf, crlf, filter, filter)) return halt +def forbidcrlf(ui, repo, hooktype, node, **kwargs): + return forbidcrlforcr(ui, repo, hooktype, node, '\r\n', **kwargs) + +def forbidcr(ui, repo, hooktype, node, **kwargs): + return forbidcrlforcr(ui, repo, hooktype, node, '\r', **kwargs) + def reposetup(ui, repo): if not repo.local(): return diff --git a/tests/test-mactext b/tests/test-mactext new file mode 100755 --- /dev/null +++ b/tests/test-mactext @@ -0,0 +1,115 @@ +#!/bin/sh + +cat > unix2mac.py < print.py <').replace('\r', '').replace('\0', '')) +EOF + +hg init +echo '[hooks]' >> .hg/hgrc +echo 'pretxncommit.cr = python:hgext.win32text.forbidcr' >> .hg/hgrc +echo 'pretxnchangegroup.cr = python:hgext.win32text.forbidcr' >> .hg/hgrc +cat .hg/hgrc +echo + +echo hello > f +hg add f +hg ci -m 1 -d'0 0' +echo + +python unix2mac.py f +hg ci -m 2 -d'0 0' +hg revert -a +echo + +mkdir d +echo hello > d/f2 +python unix2mac.py d/f2 +hg add d/f2 +hg ci -m 3 -d'0 0' +hg revert -a +rm d/f2 +echo + +hg rem f +hg ci -m 4 -d'0 0' +echo + +python -c 'file("bin", "wb").write("hello\x00\x0D")' +hg add bin +hg ci -m 5 -d'0 0' +hg log -v +echo + +hg clone . dupe +echo +for x in a b c d; do echo content > dupe/$x; done +hg -R dupe add +python unix2mac.py dupe/b dupe/c dupe/d +hg -R dupe ci -m a -d'0 0' dupe/a +hg -R dupe ci -m b/c -d'0 0' dupe/[bc] +hg -R dupe ci -m d -d'0 0' dupe/d +hg -R dupe log -v +echo + +hg pull dupe +echo + +hg log -v +echo + +rm .hg/hgrc +(echo some; echo text) > f3 +python -c 'file("f4.bat", "wb").write("rem empty\x0D")' +hg add f3 f4.bat +hg ci -m 6 -d'0 0' + +python print.py < bin +python print.py < f3 +python print.py < f4.bat +echo + +echo '[extensions]' >> .hg/hgrc +echo 'win32text = ' >> .hg/hgrc +echo '[decode]' >> .hg/hgrc +echo '** = macdecode:' >> .hg/hgrc +echo '[encode]' >> .hg/hgrc +echo '** = macencode:' >> .hg/hgrc +cat .hg/hgrc +echo + +rm f3 f4.bat bin +hg co 2>&1 | python -c 'import sys, os; sys.stdout.write(sys.stdin.read().replace(os.getcwd(), "...."))' +python print.py < bin +python print.py < f3 +python print.py < f4.bat +echo + +python -c 'file("f5.sh", "wb").write("# empty\x0D")' +hg add f5.sh +hg ci -m 7 -d'0 0' +python print.py < f5.sh +hg cat f5.sh | python print.py + +echo '% just linefeed' > linefeed +hg ci -qAm 8 linefeed +python print.py < linefeed +hg cat linefeed | python print.py +hg st -q +hg revert -a linefeed +python print.py < linefeed +hg st -q +echo modified >> linefeed +hg st -q +hg revert -a +hg st -q +python print.py < linefeed diff --git a/tests/test-mactext.out b/tests/test-mactext.out new file mode 100644 --- /dev/null +++ b/tests/test-mactext.out @@ -0,0 +1,187 @@ +[hooks] +pretxncommit.cr = python:hgext.win32text.forbidcr +pretxnchangegroup.cr = python:hgext.win32text.forbidcr + + +Attempt to commit or push text file(s) using CR line endings +in dea860dc51ec: f +transaction abort! +rollback completed +abort: pretxncommit.cr hook failed +reverting f + +Attempt to commit or push text file(s) using CR line endings +in 8aa1d87b4e2f: d/f2 +transaction abort! +rollback completed +abort: pretxncommit.cr hook failed +forgetting d/f2 + + +changeset: 2:d7599e43717c +tag: tip +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: bin +description: +5 + + +changeset: 1:c72a7d1d0907 +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: f +description: +4 + + +changeset: 0:fcf06d5c4e1d +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: f +description: +1 + + + +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved + +adding dupe/a +adding dupe/b +adding dupe/c +adding dupe/d +changeset: 5:ebbcbe52b20e +tag: tip +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: d +description: +d + + +changeset: 4:ceeb93d6508a +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: b c +description: +b/c + + +changeset: 3:c5404edbb872 +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: a +description: +a + + +changeset: 2:d7599e43717c +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: bin +description: +5 + + +changeset: 1:c72a7d1d0907 +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: f +description: +4 + + +changeset: 0:fcf06d5c4e1d +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: f +description: +1 + + + +pulling from dupe +searching for changes +adding changesets +adding manifests +adding file changes +added 3 changesets with 4 changes to 4 files +Attempt to commit or push text file(s) using CR line endings +in ceeb93d6508a: b +in ceeb93d6508a: c +in ebbcbe52b20e: d + +To prevent this mistake in your local repository, +add to Mercurial.ini or .hg/hgrc: + +[hooks] +pretxncommit.cr = python:hgext.win32text.forbidcr + +and also consider adding: + +[extensions] +hgext.win32text = +[encode] +** = macencode: +[decode] +** = macdecode: +transaction abort! +rollback completed +abort: pretxnchangegroup.cr hook failed + +changeset: 2:d7599e43717c +tag: tip +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: bin +description: +5 + + +changeset: 1:c72a7d1d0907 +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: f +description: +4 + + +changeset: 0:fcf06d5c4e1d +user: test +date: Thu Jan 01 00:00:00 1970 +0000 +files: f +description: +1 + + + +hello +sometext +rem empty + +[extensions] +win32text = +[decode] +** = macdecode: +[encode] +** = macencode: + +WARNING: f4.bat already has CR line endings +and does not need EOL conversion by the win32text plugin. +Before your next commit, please reconsider your encode/decode settings in +Mercurial.ini or ..../.hg/hgrc. +3 files updated, 0 files merged, 0 files removed, 0 files unresolved +hello +sometext +rem empty + +# empty +# empty +% just linefeed +% just linefeed +no changes needed to linefeed +% just linefeed +M linefeed +reverting linefeed +% just linefeed