Issue 882: add standard hook to reject text files with CRLF.
authorJesse Glick <jesse.glick@sun.com>
Wed, 19 Dec 2007 17:02:31 -0500
changeset 5675 a5fe27b83a4a
parent 5674 659955e2e366
child 5676 9ed6575897f7
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.
hgext/win32text.py
tests/test-win32text
tests/test-win32text.out
--- 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
+
+
+