##// END OF EJS Templates
purge: abort with missing files avoiding problems with name-mangling fs...
Emanuele Aina -
r4310:c8919eb0 default
parent child Browse files
Show More
@@ -1,114 +1,159 b''
1 1 # Copyright (C) 2006 - Marco Barisione <marco@barisione.org>
2 2 #
3 3 # This is a small extension for Mercurial (http://www.selenic.com/mercurial)
4 4 # that removes files not known to mercurial
5 5 #
6 6 # This program was inspired by the "cvspurge" script contained in CVS utilities
7 7 # (http://www.red-bean.com/cvsutils/).
8 8 #
9 9 # To enable the "purge" extension put these lines in your ~/.hgrc:
10 10 # [extensions]
11 11 # purge = /path/to/purge.py
12 12 #
13 13 # For help on the usage of "hg purge" use:
14 14 # hg help purge
15 15 #
16 16 # This program is free software; you can redistribute it and/or modify
17 17 # it under the terms of the GNU General Public License as published by
18 18 # the Free Software Foundation; either version 2 of the License, or
19 19 # (at your option) any later version.
20 20 #
21 21 # This program is distributed in the hope that it will be useful,
22 22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 24 # GNU General Public License for more details.
25 25 #
26 26 # You should have received a copy of the GNU General Public License
27 27 # along with this program; if not, write to the Free Software
28 28 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 29
30 30 from mercurial import hg, util
31 31 from mercurial.i18n import _
32 32 import os
33 33
34 def dopurge(ui, repo, dirs=None, act=True, abort_on_err=False, eol='\n'):
34 def dopurge(ui, repo, dirs=None, act=True, abort_on_err=False, eol='\n',
35 force=False):
35 36 def error(msg):
36 37 if abort_on_err:
37 38 raise util.Abort(msg)
38 39 else:
39 40 ui.warn(_('warning: %s\n') % msg)
40 41
41 42 def remove(remove_func, name):
42 43 if act:
43 44 try:
44 45 remove_func(os.path.join(repo.root, name))
45 46 except OSError, e:
46 47 error(_('%s cannot be removed') % name)
47 48 else:
48 49 ui.write('%s%s' % (name, eol))
49 50
50 51 directories = []
51 52 files = []
53 missing = []
52 54 roots, match, anypats = util.cmdmatcher(repo.root, repo.getcwd(), dirs)
53 55 for src, f, st in repo.dirstate.statwalk(files=roots, match=match,
54 56 ignored=True, directories=True):
55 57 if src == 'd':
56 58 directories.append(f)
59 elif src == 'm':
60 missing.append(f)
57 61 elif src == 'f' and f not in repo.dirstate:
58 62 files.append(f)
59 63
64 _check_missing(ui, repo, missing, force)
65
60 66 directories.sort()
61 67
62 68 for f in files:
63 69 if f not in repo.dirstate:
64 70 ui.note(_('Removing file %s\n') % f)
65 71 remove(os.remove, f)
66 72
67 73 for f in directories[::-1]:
68 74 if not os.listdir(repo.wjoin(f)):
69 75 ui.note(_('Removing directory %s\n') % f)
70 76 remove(os.rmdir, f)
71 77
78 def _check_missing(ui, repo, missing, force=False):
79 """Abort if there is the chance of having problems with name-mangling fs
80
81 In a name mangling filesystem (e.g. a case insensitive one)
82 dirstate.walk() can yield filenames different from the ones
83 stored in the dirstate. This already confuses the status and
84 add commands, but with purge this may cause data loss.
85
86 To prevent this, _check_missing will abort if there are missing
87 files. The force option will let the user skip the check if he
88 knows it is safe.
89
90 Even with the force option this function will check if any of the
91 missing files is still available in the working dir: if so there
92 may be some problem with the underlying filesystem, so it
93 aborts unconditionally."""
94
95 found = [f for f in missing if util.lexists(repo.wjoin(f))]
96
97 if found:
98 if not ui.quiet:
99 ui.warn(_("The following tracked files weren't listed by the "
100 "filesystem, but could still be found:\n"))
101 for f in found:
102 ui.warn("%s\n" % f)
103 if util.checkfolding(repo.path):
104 ui.warn(_("This is probably due to a case-insensitive "
105 "filesystem\n"))
106 raise util.Abort(_("purging on name mangling filesystems is not "
107 "yet fully supported"))
108
109 if missing and not force:
110 raise util.Abort(_("there are missing files in the working dir and "
111 "purge still has problems with them due to name "
112 "mangling filesystems. "
113 "Use --force if you know what you are doing"))
114
72 115
73 116 def purge(ui, repo, *dirs, **opts):
74 117 '''removes files not tracked by mercurial
75 118
76 119 Delete files not known to mercurial, this is useful to test local and
77 120 uncommitted changes in the otherwise clean source tree.
78 121
79 122 This means that purge will delete:
80 123 - Unknown files: files marked with "?" by "hg status"
81 124 - Ignored files: files usually ignored by Mercurial because they match
82 125 a pattern in a ".hgignore" file
83 126 - Empty directories: in fact Mercurial ignores directories unless they
84 127 contain files under source control managment
85 128 But it will leave untouched:
86 129 - Unmodified tracked files
87 130 - Modified tracked files
88 131 - New files added to the repository (with "hg add")
89 132
90 133 If directories are given on the command line, only files in these
91 134 directories are considered.
92 135
93 136 Be careful with purge, you could irreversibly delete some files you
94 137 forgot to add to the repository. If you only want to print the list of
95 138 files that this program would delete use the --print option.
96 139 '''
97 140 act = not opts['print']
98 141 abort_on_err = bool(opts['abort_on_err'])
99 142 eol = opts['print0'] and '\0' or '\n'
100 143 if eol == '\0':
101 144 # --print0 implies --print
102 145 act = False
103 dopurge(ui, repo, dirs, act, abort_on_err, eol)
146 force = bool(opts['force'])
147 dopurge(ui, repo, dirs, act, abort_on_err, eol, force)
104 148
105 149
106 150 cmdtable = {
107 151 'purge':
108 152 (purge,
109 153 [('a', 'abort-on-err', None, _('abort if an error occurs')),
154 ('f', 'force', None, _('purge even when missing files are detected')),
110 155 ('p', 'print', None, _('print the file names instead of deleting them')),
111 156 ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
112 157 ' (implies -p)'))],
113 158 _('hg purge [OPTION]... [DIR]...'))
114 159 }
@@ -1,76 +1,99 b''
1 1 #!/bin/sh
2 2
3 3 cat <<EOF >> $HGRCPATH
4 4 [extensions]
5 5 purge=${TESTDIR}/../contrib/purge/purge.py
6 6 EOF
7 7
8 8 echo % init
9 9 hg init t
10 10 cd t
11 11
12 12 echo % setup
13 13 echo r1 > r1
14 14 hg ci -qAmr1 -d'0 0'
15 15 mkdir directory
16 16 echo r2 > directory/r2
17 17 hg ci -qAmr2 -d'1 0'
18 18 echo 'ignored' > .hgignore
19 19 hg ci -qAmr3 -d'2 0'
20 20
21 21 echo % delete an empty directory
22 22 mkdir empty_dir
23 23 hg purge -p
24 24 hg purge -v
25 25 ls
26 26
27 27 echo % delete an untracked directory
28 28 mkdir untracked_dir
29 29 touch untracked_dir/untracked_file1
30 30 touch untracked_dir/untracked_file2
31 31 hg purge -p
32 32 hg purge -v
33 33 ls
34 34
35 35 echo % delete an untracked file
36 36 touch untracked_file
37 37 hg purge -p
38 38 hg purge -v
39 39 ls
40 40
41 41 echo % delete an untracked file in a tracked directory
42 42 touch directory/untracked_file
43 43 hg purge -p
44 44 hg purge -v
45 45 ls
46 46
47 47 echo % delete nested directories
48 48 mkdir -p untracked_directory/nested_directory
49 49 hg purge -p
50 50 hg purge -v
51 51 ls
52 52
53 53 echo % delete nested directories from a subdir
54 54 mkdir -p untracked_directory/nested_directory
55 55 cd directory
56 56 hg purge -p
57 57 hg purge -v
58 58 cd ..
59 59 ls
60 60
61 61 echo % delete only part of the tree
62 62 mkdir -p untracked_directory/nested_directory
63 63 touch directory/untracked_file
64 64 cd directory
65 65 hg purge -p ../untracked_directory
66 66 hg purge -v ../untracked_directory
67 67 cd ..
68 68 ls
69 69 ls directory/untracked_file
70 70 rm directory/untracked_file
71 71
72 72 echo % delete ignored files
73 73 touch ignored
74 74 hg purge -p
75 75 hg purge -v
76 76 ls
77
78 echo % abort with missing files until we support name mangling filesystems
79 touch untracked_file
80 rm r1
81 # hide error messages to avoid changing the output when the text changes
82 hg purge -p 2> /dev/null
83 if [ $? -ne 0 ]; then
84 echo "refused to run"
85 fi
86 if [ -f untracked_file ]; then
87 echo "untracked_file still around"
88 fi
89 hg purge -p --force
90 hg purge -v 2> /dev/null
91 if [ $? -ne 0 ]; then
92 echo "refused to run"
93 fi
94 if [ -f untracked_file ]; then
95 echo "untracked_file still around"
96 fi
97 hg purge -v --force
98 hg revert --all --quiet
99 ls
@@ -1,49 +1,58 b''
1 1 % init
2 2 % setup
3 3 % delete an empty directory
4 4 empty_dir
5 5 Removing directory empty_dir
6 6 directory
7 7 r1
8 8 % delete an untracked directory
9 9 untracked_dir/untracked_file1
10 10 untracked_dir/untracked_file2
11 11 Removing file untracked_dir/untracked_file1
12 12 Removing file untracked_dir/untracked_file2
13 13 Removing directory untracked_dir
14 14 directory
15 15 r1
16 16 % delete an untracked file
17 17 untracked_file
18 18 Removing file untracked_file
19 19 directory
20 20 r1
21 21 % delete an untracked file in a tracked directory
22 22 directory/untracked_file
23 23 Removing file directory/untracked_file
24 24 directory
25 25 r1
26 26 % delete nested directories
27 27 untracked_directory/nested_directory
28 28 Removing directory untracked_directory/nested_directory
29 29 Removing directory untracked_directory
30 30 directory
31 31 r1
32 32 % delete nested directories from a subdir
33 33 untracked_directory/nested_directory
34 34 Removing directory untracked_directory/nested_directory
35 35 Removing directory untracked_directory
36 36 directory
37 37 r1
38 38 % delete only part of the tree
39 39 untracked_directory/nested_directory
40 40 Removing directory untracked_directory/nested_directory
41 41 Removing directory untracked_directory
42 42 directory
43 43 r1
44 44 directory/untracked_file
45 45 % delete ignored files
46 46 ignored
47 47 Removing file ignored
48 48 directory
49 49 r1
50 % abort with missing files until we support name mangling filesystems
51 refused to run
52 untracked_file still around
53 untracked_file
54 refused to run
55 untracked_file still around
56 Removing file untracked_file
57 directory
58 r1
General Comments 0
You need to be logged in to leave comments. Login now