Mercurial > hg
changeset 4313:61ab822a9e88
Merge with Matt
author | Brendan Cully <brendan@kublai.com> |
---|---|
date | Thu, 05 Apr 2007 15:20:42 -0700 |
parents | 82be6af21697 (diff) 5e05007d3857 (current diff) |
children | aa26759c6fb3 |
files | |
diffstat | 10 files changed, 234 insertions(+), 143 deletions(-) [+] |
line wrap: on
line diff
--- a/contrib/purge/purge.py Thu Apr 05 17:09:31 2007 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,114 +0,0 @@ -# Copyright (C) 2006 - Marco Barisione <marco@barisione.org> -# -# This is a small extension for Mercurial (http://www.selenic.com/mercurial) -# that removes files not known to mercurial -# -# This program was inspired by the "cvspurge" script contained in CVS utilities -# (http://www.red-bean.com/cvsutils/). -# -# To enable the "purge" extension put these lines in your ~/.hgrc: -# [extensions] -# purge = /path/to/purge.py -# -# For help on the usage of "hg purge" use: -# hg help purge -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -from mercurial import hg, util -from mercurial.i18n import _ -import os - -def dopurge(ui, repo, dirs=None, act=True, abort_on_err=False, eol='\n'): - def error(msg): - if abort_on_err: - raise util.Abort(msg) - else: - ui.warn(_('warning: %s\n') % msg) - - def remove(remove_func, name): - if act: - try: - remove_func(os.path.join(repo.root, name)) - except OSError, e: - error(_('%s cannot be removed') % name) - else: - ui.write('%s%s' % (name, eol)) - - directories = [] - files = [] - roots, match, anypats = util.cmdmatcher(repo.root, repo.getcwd(), dirs) - for src, f, st in repo.dirstate.statwalk(files=roots, match=match, - ignored=True, directories=True): - if src == 'd': - directories.append(f) - elif src == 'f' and f not in repo.dirstate: - files.append(f) - - directories.sort() - - for f in files: - if f not in repo.dirstate: - ui.note(_('Removing file %s\n') % f) - remove(os.remove, f) - - for f in directories[::-1]: - if not os.listdir(repo.wjoin(f)): - ui.note(_('Removing directory %s\n') % f) - remove(os.rmdir, f) - - -def purge(ui, repo, *dirs, **opts): - '''removes files not tracked by mercurial - - Delete files not known to mercurial, this is useful to test local and - uncommitted changes in the otherwise clean source tree. - - This means that purge will delete: - - Unknown files: files marked with "?" by "hg status" - - Ignored files: files usually ignored by Mercurial because they match - a pattern in a ".hgignore" file - - Empty directories: in fact Mercurial ignores directories unless they - contain files under source control managment - But it will leave untouched: - - Unmodified tracked files - - Modified tracked files - - New files added to the repository (with "hg add") - - If directories are given on the command line, only files in these - directories are considered. - - Be careful with purge, you could irreversibly delete some files you - forgot to add to the repository. If you only want to print the list of - files that this program would delete use the --print option. - ''' - act = not opts['print'] - abort_on_err = bool(opts['abort_on_err']) - eol = opts['print0'] and '\0' or '\n' - if eol == '\0': - # --print0 implies --print - act = False - dopurge(ui, repo, dirs, act, abort_on_err, eol) - - -cmdtable = { - 'purge': - (purge, - [('a', 'abort-on-err', None, _('abort if an error occurs')), - ('p', 'print', None, _('print the file names instead of deleting them')), - ('0', 'print0', None, _('end filenames with NUL, for use with xargs' - ' (implies -p)'))], - _('hg purge [OPTION]... [DIR]...')) -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/purge.py Thu Apr 05 15:20:42 2007 -0700 @@ -0,0 +1,159 @@ +# Copyright (C) 2006 - Marco Barisione <marco@barisione.org> +# +# This is a small extension for Mercurial (http://www.selenic.com/mercurial) +# that removes files not known to mercurial +# +# This program was inspired by the "cvspurge" script contained in CVS utilities +# (http://www.red-bean.com/cvsutils/). +# +# To enable the "purge" extension put these lines in your ~/.hgrc: +# [extensions] +# hgext.purge = +# +# For help on the usage of "hg purge" use: +# hg help purge +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +from mercurial import hg, util +from mercurial.i18n import _ +import os + +def dopurge(ui, repo, dirs=None, act=True, abort_on_err=False, eol='\n', + force=False): + def error(msg): + if abort_on_err: + raise util.Abort(msg) + else: + ui.warn(_('warning: %s\n') % msg) + + def remove(remove_func, name): + if act: + try: + remove_func(os.path.join(repo.root, name)) + except OSError, e: + error(_('%s cannot be removed') % name) + else: + ui.write('%s%s' % (name, eol)) + + directories = [] + files = [] + missing = [] + roots, match, anypats = util.cmdmatcher(repo.root, repo.getcwd(), dirs) + for src, f, st in repo.dirstate.statwalk(files=roots, match=match, + ignored=True, directories=True): + if src == 'd': + directories.append(f) + elif src == 'm': + missing.append(f) + elif src == 'f' and f not in repo.dirstate: + files.append(f) + + _check_missing(ui, repo, missing, force) + + directories.sort() + + for f in files: + if f not in repo.dirstate: + ui.note(_('Removing file %s\n') % f) + remove(os.remove, f) + + for f in directories[::-1]: + if not os.listdir(repo.wjoin(f)): + ui.note(_('Removing directory %s\n') % f) + remove(os.rmdir, f) + +def _check_missing(ui, repo, missing, force=False): + """Abort if there is the chance of having problems with name-mangling fs + + In a name mangling filesystem (e.g. a case insensitive one) + dirstate.walk() can yield filenames different from the ones + stored in the dirstate. This already confuses the status and + add commands, but with purge this may cause data loss. + + To prevent this, _check_missing will abort if there are missing + files. The force option will let the user skip the check if he + knows it is safe. + + Even with the force option this function will check if any of the + missing files is still available in the working dir: if so there + may be some problem with the underlying filesystem, so it + aborts unconditionally.""" + + found = [f for f in missing if util.lexists(repo.wjoin(f))] + + if found: + if not ui.quiet: + ui.warn(_("The following tracked files weren't listed by the " + "filesystem, but could still be found:\n")) + for f in found: + ui.warn("%s\n" % f) + if util.checkfolding(repo.path): + ui.warn(_("This is probably due to a case-insensitive " + "filesystem\n")) + raise util.Abort(_("purging on name mangling filesystems is not " + "yet fully supported")) + + if missing and not force: + raise util.Abort(_("there are missing files in the working dir and " + "purge still has problems with them due to name " + "mangling filesystems. " + "Use --force if you know what you are doing")) + + +def purge(ui, repo, *dirs, **opts): + '''removes files not tracked by mercurial + + Delete files not known to mercurial, this is useful to test local and + uncommitted changes in the otherwise clean source tree. + + This means that purge will delete: + - Unknown files: files marked with "?" by "hg status" + - Ignored files: files usually ignored by Mercurial because they match + a pattern in a ".hgignore" file + - Empty directories: in fact Mercurial ignores directories unless they + contain files under source control managment + But it will leave untouched: + - Unmodified tracked files + - Modified tracked files + - New files added to the repository (with "hg add") + + If directories are given on the command line, only files in these + directories are considered. + + Be careful with purge, you could irreversibly delete some files you + forgot to add to the repository. If you only want to print the list of + files that this program would delete use the --print option. + ''' + act = not opts['print'] + abort_on_err = bool(opts['abort_on_err']) + eol = opts['print0'] and '\0' or '\n' + if eol == '\0': + # --print0 implies --print + act = False + force = bool(opts['force']) + dopurge(ui, repo, dirs, act, abort_on_err, eol, force) + + +cmdtable = { + 'purge': + (purge, + [('a', 'abort-on-err', None, _('abort if an error occurs')), + ('f', 'force', None, _('purge even when missing files are detected')), + ('p', 'print', None, _('print the file names instead of deleting them')), + ('0', 'print0', None, _('end filenames with NUL, for use with xargs' + ' (implies -p)'))], + _('hg purge [OPTION]... [DIR]...')) +}
--- a/mercurial/commands.py Thu Apr 05 17:09:31 2007 -0500 +++ b/mercurial/commands.py Thu Apr 05 15:20:42 2007 -0700 @@ -1658,7 +1658,10 @@ ret = 1 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node, + badmatch=util.always, default='relglob'): + if src == 'b': + continue if not node and repo.dirstate.state(abs) == '?': continue if opts['fullpath']:
--- a/mercurial/util.py Thu Apr 05 17:09:31 2007 -0500 +++ b/mercurial/util.py Thu Apr 05 15:20:42 2007 -0700 @@ -447,7 +447,7 @@ if c in _globchars: return True return False - def regex(kind, name): + def regex(kind, name, tail): '''convert a pattern into a regular expression''' if not name: return '' @@ -456,23 +456,23 @@ elif kind == 'path': return '^' + re.escape(name) + '(?:/|$)' elif kind == 'relglob': - return globre(name, '(?:|.*/)', '(?:/|$)') + return globre(name, '(?:|.*/)', tail) elif kind == 'relpath': return re.escape(name) + '(?:/|$)' elif kind == 'relre': if name.startswith('^'): return name return '.*' + name - return globre(name, '', '(?:/|$)') + return globre(name, '', tail) - def matchfn(pats): + def matchfn(pats, tail): """build a matching function from a set of patterns""" if not pats: return matches = [] for k, p in pats: try: - pat = '(?:%s)' % regex(k, p) + pat = '(?:%s)' % regex(k, p, tail) matches.append(re.compile(pat).match) except re.error: if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p)) @@ -520,15 +520,15 @@ roots, pats, anypats = normalizepats(names, dflt_pat) - patmatch = matchfn(pats) or always + patmatch = matchfn(pats, '$') or always incmatch = always if inc: dummy, inckinds, dummy = normalizepats(inc, 'glob') - incmatch = matchfn(inckinds) + incmatch = matchfn(inckinds, '(?:/|$)') excmatch = lambda fn: False if exc: dummy, exckinds, dummy = normalizepats(exc, 'glob') - excmatch = matchfn(exckinds) + excmatch = matchfn(exckinds, '(?:/|$)') if not names and inc and not exc: # common case: hgignore patterns
--- a/tests/test-locate Thu Apr 05 17:09:31 2007 -0500 +++ b/tests/test-locate Thu Apr 05 15:20:42 2007 -0700 @@ -19,6 +19,8 @@ echo 0 > t/x echo 0 > t/b echo 0 > t/e.h +mkdir dir.h +echo 0 > dir.h/foo hg ci -A -m m -d "1000000 0" touch nottracked hglocate a && echo locate succeeded || echo locate failed @@ -28,9 +30,11 @@ hg ci -m m -d "1000000 0" hglocate a hglocate NONEXISTENT +hglocate relpath:NONEXISTENT hglocate hglocate -r 0 a hglocate -r 0 NONEXISTENT +hglocate -r 0 relpath:NONEXISTENT hglocate -r 0 echo % -I/-X with relative path should work cd t @@ -39,14 +43,14 @@ # test issue294 cd .. rm -r t -hglocate t +hglocate 't/**' mkdir otherdir cd otherdir hglocate b hglocate '*.h' hglocate path:t/x -hglocate 're:.*\.h' +hglocate 're:.*\.h$' hglocate -r 0 b hglocate -r 0 '*.h' hglocate -r 0 path:t/x -hglocate -r 0 're:.*\.h' +hglocate -r 0 're:.*\.h$'
--- a/tests/test-locate.out Thu Apr 05 17:09:31 2007 -0500 +++ b/tests/test-locate.out Thu Apr 05 15:20:42 2007 -0700 @@ -1,5 +1,6 @@ adding a adding b +adding dir.h/foo adding t.h adding t/b adding t/e.h @@ -14,6 +15,7 @@ hg locate a b +dir.h/foo t.h t/b t/e.h @@ -23,8 +25,11 @@ hg locate NONEXISTENT +hg locate relpath:NONEXISTENT + hg locate b +dir.h/foo t.h t/b t/e.h @@ -35,9 +40,12 @@ hg locate -r 0 NONEXISTENT +hg locate -r 0 relpath:NONEXISTENT + hg locate -r 0 a b +dir.h/foo t.h t/b t/e.h @@ -46,6 +54,7 @@ % -I/-X with relative path should work hg locate b +dir.h/foo t.h t/b t/e.h @@ -56,7 +65,7 @@ t/e.h t/x -hg locate t +hg locate t/** t/b t/e.h t/x @@ -72,7 +81,7 @@ hg locate path:t/x ../t/x -hg locate re:.*\.h +hg locate re:.*\.h$ ../t.h ../t/e.h @@ -87,7 +96,7 @@ hg locate -r 0 path:t/x ../t/x -hg locate -r 0 re:.*\.h +hg locate -r 0 re:.*\.h$ ../t.h ../t/e.h
--- a/tests/test-purge Thu Apr 05 17:09:31 2007 -0500 +++ b/tests/test-purge Thu Apr 05 15:20:42 2007 -0700 @@ -2,7 +2,7 @@ cat <<EOF >> $HGRCPATH [extensions] -purge=${TESTDIR}/../contrib/purge/purge.py +hgext.purge= EOF echo % init @@ -74,3 +74,26 @@ hg purge -p hg purge -v ls + +echo % abort with missing files until we support name mangling filesystems +touch untracked_file +rm r1 +# hide error messages to avoid changing the output when the text changes +hg purge -p 2> /dev/null +if [ $? -ne 0 ]; then + echo "refused to run" +fi +if [ -f untracked_file ]; then + echo "untracked_file still around" +fi +hg purge -p --force +hg purge -v 2> /dev/null +if [ $? -ne 0 ]; then + echo "refused to run" +fi +if [ -f untracked_file ]; then + echo "untracked_file still around" +fi +hg purge -v --force +hg revert --all --quiet +ls
--- a/tests/test-purge.out Thu Apr 05 17:09:31 2007 -0500 +++ b/tests/test-purge.out Thu Apr 05 15:20:42 2007 -0700 @@ -47,3 +47,12 @@ Removing file ignored directory r1 +% abort with missing files until we support name mangling filesystems +refused to run +untracked_file still around +untracked_file +refused to run +untracked_file still around +Removing file untracked_file +directory +r1
--- a/tests/test-walk Thu Apr 05 17:09:31 2007 -0500 +++ b/tests/test-walk Thu Apr 05 15:20:42 2007 -0700 @@ -72,11 +72,13 @@ #debugwalk `pwd`/beans #debugwalk `pwd`/.. debugwalk glob:\* +debugwalk 'glob:**e' debugwalk 're:.*[kb]$' debugwalk path:beans/black debugwalk path:beans//black debugwalk relglob:Procyonidae -debugwalk relglob:Procyonidae/ fennel +debugwalk 'relglob:Procyonidae/**' +debugwalk 'relglob:Procyonidae/**' fennel debugwalk beans 'glob:beans/*' debugwalk 'glob:mamm**' debugwalk 'glob:mamm**' fennel
--- a/tests/test-walk.out Thu Apr 05 17:09:31 2007 -0500 +++ b/tests/test-walk.out Thu Apr 05 15:20:42 2007 -0700 @@ -210,20 +210,14 @@ beans/.hg: No such file or directory hg debugwalk glob:* -f beans/black beans/black -f beans/borlotti beans/borlotti -f beans/kidney beans/kidney -f beans/navy beans/navy -f beans/pinto beans/pinto +f fennel fennel +f fenugreek fenugreek +f fiddlehead fiddlehead +f glob:glob glob:glob + +hg debugwalk glob:**e f beans/turtle beans/turtle -f fennel fennel -f fenugreek fenugreek -f fiddlehead fiddlehead -f glob:glob glob:glob f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle -f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi -f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon -f mammals/skunk mammals/skunk hg debugwalk re:.*[kb]$ f beans/black beans/black @@ -238,11 +232,13 @@ f beans/black beans/black exact hg debugwalk relglob:Procyonidae + +hg debugwalk relglob:Procyonidae/** f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon -hg debugwalk relglob:Procyonidae/ fennel +hg debugwalk relglob:Procyonidae/** fennel f fennel fennel exact f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi