# HG changeset patch # User Jesse Glick # Date 2007-12-19 22:02:31 # Node ID a5fe27b83a4a1a75fe3507e4f595e496edb58f2f # Parent 659955e2e36686a0a6a23aa31fbde23c525df0ef 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. diff --git a/hgext/win32text.py b/hgext/win32text.py --- a/hgext/win32text.py +++ b/hgext/win32text.py @@ -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 @@ util.filtertable.update({ '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 diff --git a/tests/test-win32text b/tests/test-win32text new file mode 100644 --- /dev/null +++ b/tests/test-win32text @@ -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 diff --git a/tests/test-win32text.out b/tests/test-win32text.out new file mode 100644 --- /dev/null +++ b/tests/test-win32text.out @@ -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 + + +