Issue 882: add standard hook to reject text files with CRLF.
While the win32text extension does LF <-> CRLF conversion, and will issue a
warning in case a file already in the repository uses CRLF, it provides no
mechanism for verifying that incoming changes use LF. In a large development
team with some Windows users, it is virtually guaranteed that someone will
forget to set up the encode filter correctly and accidentally check in a file
using CRLF, which can cause warnings for other Windows users when they next
fetch changes. Since this is a general problem it is desirable to have a
pre-commit (or -push) hook available to reject such accidents earlier rather
than trying to fix them up after the fact.
--- a/hgext/win32text.py Sun Dec 16 22:55:23 2007 +0100
+++ b/hgext/win32text.py Wed Dec 19 17:02:31 2007 -0500
@@ -1,5 +1,30 @@
+# win32text.py - LF <-> CRLF translation utilities for Windows users
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+#
+# To perform automatic newline conversion, use:
+#
+# [extensions]
+# hgext.win32text =
+# [encode]
+# ** = cleverencode:
+# [decode]
+# ** = cleverdecode:
+#
+# If not doing conversion, to make sure you do not commit CRLF by accident:
+#
+# [hooks]
+# pretxncommit.crlf = python:hgext.win32text.forbidcrlf
+#
+# To do the same check on a server to prevent CRLF from being pushed or pulled:
+#
+# [hooks]
+# pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf
+
from mercurial import util, ui
from mercurial.i18n import gettext as _
+from mercurial.node import *
import re
# regexp for single LF without CR preceding.
@@ -43,3 +68,34 @@
'cleverdecode:': cleverdecode,
'cleverencode:': cleverencode,
})
+
+def forbidcrlf(ui, repo, hooktype, node, **kwargs):
+ halt = False
+ for rev in xrange(repo.changelog.rev(bin(node)), repo.changelog.count()):
+ c = repo.changectx(rev)
+ for f in c.files():
+ if f not in c:
+ continue
+ data = c[f].data()
+ if '\0' not in data and '\r\n' in data:
+ if not halt:
+ ui.warn(_('Attempt to commit or push text file(s) '
+ 'using CRLF line endings\n'))
+ ui.warn(_('in %s: %s\n') % (short(c.node()), f))
+ halt = True
+ if halt and hooktype == 'pretxnchangegroup':
+ 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'
+ '\n'
+ 'and also consider adding:\n'
+ '\n'
+ '[extensions]\n'
+ 'hgext.win32text =\n'
+ '[encode]\n'
+ '** = cleverencode:\n'
+ '[decode]\n'
+ '** = cleverdecode:\n'))
+ return halt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-win32text Wed Dec 19 17:02:31 2007 -0500
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+hg init
+echo '[hooks]' >> .hg/hgrc
+echo 'pretxncommit.crlf = python:hgext.win32text.forbidcrlf' >> .hg/hgrc
+echo 'pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf' >> .hg/hgrc
+cat .hg/hgrc
+echo
+
+echo hello > f
+hg add f
+hg ci -m 1 -d'0 0'
+echo
+
+unix2dos f
+hg ci -m 2 -d'0 0'
+hg revert -a
+echo
+
+mkdir d
+echo hello > d/f2
+unix2dos 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
+
+printf 'hello\x00\x0D\x0A' > bin
+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
+unix2dos 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
+
+# XXX missing tests for encode/decode hooks
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-win32text.out Wed Dec 19 17:02:31 2007 -0500
@@ -0,0 +1,157 @@
+[hooks]
+pretxncommit.crlf = python:hgext.win32text.forbidcrlf
+pretxnchangegroup.crlf = python:hgext.win32text.forbidcrlf
+
+
+Attempt to commit or push text file(s) using CRLF line endings
+in b1aa5cde7ff4: f
+transaction abort!
+rollback completed
+abort: pretxncommit.crlf hook failed
+reverting f
+
+Attempt to commit or push text file(s) using CRLF line endings
+in 88b17af74937: d/f2
+transaction abort!
+rollback completed
+abort: pretxncommit.crlf hook failed
+forgetting d/f2
+
+
+changeset: 2:a55cab36df04
+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
+
+
+
+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:81c49ee61396
+tag: tip
+user: test
+date: Thu Jan 01 00:00:00 1970 +0000
+files: d
+description:
+d
+
+
+changeset: 4:02184785bcac
+user: test
+date: Thu Jan 01 00:00:00 1970 +0000
+files: b c
+description:
+b/c
+
+
+changeset: 3:36e70ffe2c3d
+user: test
+date: Thu Jan 01 00:00:00 1970 +0000
+files: a
+description:
+a
+
+
+changeset: 2:a55cab36df04
+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 CRLF line endings
+in 02184785bcac: b
+in 02184785bcac: c
+in 81c49ee61396: d
+
+To prevent this mistake in your local repository,
+add to Mercurial.ini or .hg/hgrc:
+
+[hooks]
+pretxncommit.crlf = python:hgext.win32text.forbidcrlf
+
+and also consider adding:
+
+[extensions]
+hgext.win32text =
+[encode]
+** = cleverencode:
+[decode]
+** = cleverdecode:
+transaction abort!
+rollback completed
+abort: pretxnchangegroup.crlf hook failed
+
+changeset: 2:a55cab36df04
+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
+
+
+