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