##// END OF EJS Templates
purge: remove read-only files under Windows (issue583)...
Patrick Mezard -
r8043:b777dd8f default
parent child Browse files
Show More
@@ -1,99 +1,106 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, stat
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
37 Delete files not known to Mercurial. This is useful to test local
38 and uncommitted changes in an otherwise-clean source tree.
38 and uncommitted changes in an 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 - Empty directories: in fact Mercurial ignores directories unless
42 - Empty directories: in fact Mercurial ignores directories unless
43 they contain files under source control managment
43 they contain files under source control managment
44 But it will leave untouched:
44 But it will leave untouched:
45 - Modified and unmodified tracked files
45 - Modified and unmodified tracked files
46 - Ignored files (unless --all is specified)
46 - Ignored files (unless --all is specified)
47 - New files added to the repository (with "hg add")
47 - New files added to the repository (with "hg add")
48
48
49 If directories are given on the command line, only files in these
49 If directories are given on the command line, only files in these
50 directories are considered.
50 directories are considered.
51
51
52 Be careful with purge, as you could irreversibly delete some files
52 Be careful with purge, as you could irreversibly delete some files
53 you forgot to add to the repository. If you only want to print the
53 you forgot to add to the repository. If you only want to print the
54 list of files that this program would delete, use the --print
54 list of files that this program would delete, use the --print
55 option.
55 option.
56 '''
56 '''
57 act = not opts['print']
57 act = not opts['print']
58 eol = '\n'
58 eol = '\n'
59 if opts['print0']:
59 if opts['print0']:
60 eol = '\0'
60 eol = '\0'
61 act = False # --print0 implies --print
61 act = False # --print0 implies --print
62
62
63 def remove(remove_func, name):
63 def remove(remove_func, name):
64 if act:
64 if act:
65 try:
65 try:
66 remove_func(repo.wjoin(name))
66 remove_func(repo.wjoin(name))
67 except OSError:
67 except OSError:
68 m = _('%s cannot be removed') % name
68 m = _('%s cannot be removed') % name
69 if opts['abort_on_err']:
69 if opts['abort_on_err']:
70 raise util.Abort(m)
70 raise util.Abort(m)
71 ui.warn(_('warning: %s\n') % m)
71 ui.warn(_('warning: %s\n') % m)
72 else:
72 else:
73 ui.write('%s%s' % (name, eol))
73 ui.write('%s%s' % (name, eol))
74
74
75 def removefile(path):
76 # read-only files cannot be unlinked under Windows
77 s = os.stat(path)
78 if (s.st_dev & stat.S_IWRITE) == 0:
79 os.chmod(path, s.st_mode | stat.S_IWRITE)
80 os.remove(path)
81
75 directories = []
82 directories = []
76 match = cmdutil.match(repo, dirs, opts)
83 match = cmdutil.match(repo, dirs, opts)
77 match.dir = directories.append
84 match.dir = directories.append
78 status = repo.status(match=match, ignored=opts['all'], unknown=True)
85 status = repo.status(match=match, ignored=opts['all'], unknown=True)
79
86
80 for f in util.sort(status[4] + status[5]):
87 for f in util.sort(status[4] + status[5]):
81 ui.note(_('Removing file %s\n') % f)
88 ui.note(_('Removing file %s\n') % f)
82 remove(os.remove, f)
89 remove(removefile, f)
83
90
84 for f in util.sort(directories)[::-1]:
91 for f in util.sort(directories)[::-1]:
85 if match(f) and not os.listdir(repo.wjoin(f)):
92 if match(f) and not os.listdir(repo.wjoin(f)):
86 ui.note(_('Removing directory %s\n') % f)
93 ui.note(_('Removing directory %s\n') % f)
87 remove(os.rmdir, f)
94 remove(os.rmdir, f)
88
95
89 cmdtable = {
96 cmdtable = {
90 'purge|clean':
97 'purge|clean':
91 (purge,
98 (purge,
92 [('a', 'abort-on-err', None, _('abort if an error occurs')),
99 [('a', 'abort-on-err', None, _('abort if an error occurs')),
93 ('', 'all', None, _('purge ignored files too')),
100 ('', 'all', None, _('purge ignored files too')),
94 ('p', 'print', None, _('print the file names instead of deleting them')),
101 ('p', 'print', None, _('print the file names instead of deleting them')),
95 ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
102 ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
96 ' (implies -p)')),
103 ' (implies -p)')),
97 ] + commands.walkopts,
104 ] + commands.walkopts,
98 _('hg purge [OPTION]... [DIR]...'))
105 _('hg purge [OPTION]... [DIR]...'))
99 }
106 }
@@ -1,132 +1,138 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 touch untracked_file_readonly
38 python <<EOF
39 import os, stat
40 f= 'untracked_file_readonly'
41 os.chmod(f, os.stat(f).st_mode & ~stat.S_IWRITE)
42 EOF
37 hg purge -p
43 hg purge -p
38 hg purge -v
44 hg purge -v
39 ls
45 ls
40
46
41 echo % delete an untracked file in a tracked directory
47 echo % delete an untracked file in a tracked directory
42 touch directory/untracked_file
48 touch directory/untracked_file
43 hg purge -p
49 hg purge -p
44 hg purge -v
50 hg purge -v
45 ls
51 ls
46
52
47 echo % delete nested directories
53 echo % delete nested directories
48 mkdir -p untracked_directory/nested_directory
54 mkdir -p untracked_directory/nested_directory
49 hg purge -p
55 hg purge -p
50 hg purge -v
56 hg purge -v
51 ls
57 ls
52
58
53 echo % delete nested directories from a subdir
59 echo % delete nested directories from a subdir
54 mkdir -p untracked_directory/nested_directory
60 mkdir -p untracked_directory/nested_directory
55 cd directory
61 cd directory
56 hg purge -p
62 hg purge -p
57 hg purge -v
63 hg purge -v
58 cd ..
64 cd ..
59 ls
65 ls
60
66
61 echo % delete only part of the tree
67 echo % delete only part of the tree
62 mkdir -p untracked_directory/nested_directory
68 mkdir -p untracked_directory/nested_directory
63 touch directory/untracked_file
69 touch directory/untracked_file
64 cd directory
70 cd directory
65 hg purge -p ../untracked_directory
71 hg purge -p ../untracked_directory
66 hg purge -v ../untracked_directory
72 hg purge -v ../untracked_directory
67 cd ..
73 cd ..
68 ls
74 ls
69 ls directory/untracked_file
75 ls directory/untracked_file
70 rm directory/untracked_file
76 rm directory/untracked_file
71
77
72 echo % skip ignored files if --all not specified
78 echo % skip ignored files if --all not specified
73 touch ignored
79 touch ignored
74 hg purge -p
80 hg purge -p
75 hg purge -v
81 hg purge -v
76 ls
82 ls
77 hg purge -p --all
83 hg purge -p --all
78 hg purge -v --all
84 hg purge -v --all
79 ls
85 ls
80
86
81 echo % abort with missing files until we support name mangling filesystems
87 echo % abort with missing files until we support name mangling filesystems
82 touch untracked_file
88 touch untracked_file
83 rm r1
89 rm r1
84 # hide error messages to avoid changing the output when the text changes
90 # hide error messages to avoid changing the output when the text changes
85 hg purge -p 2> /dev/null
91 hg purge -p 2> /dev/null
86 hg st
92 hg st
87
93
88 hg purge -p
94 hg purge -p
89 hg purge -v 2> /dev/null
95 hg purge -v 2> /dev/null
90 hg st
96 hg st
91
97
92 hg purge -v
98 hg purge -v
93 hg revert --all --quiet
99 hg revert --all --quiet
94 hg st -a
100 hg st -a
95
101
96 echo '% tracked file in ignored directory (issue621)'
102 echo '% tracked file in ignored directory (issue621)'
97 echo directory >> .hgignore
103 echo directory >> .hgignore
98 hg ci -m 'ignore directory'
104 hg ci -m 'ignore directory'
99 touch untracked_file
105 touch untracked_file
100 hg purge -p
106 hg purge -p
101 hg purge -v
107 hg purge -v
102
108
103 echo % skip excluded files
109 echo % skip excluded files
104 touch excluded_file
110 touch excluded_file
105 hg purge -p -X excluded_file
111 hg purge -p -X excluded_file
106 hg purge -v -X excluded_file
112 hg purge -v -X excluded_file
107 ls
113 ls
108 rm excluded_file
114 rm excluded_file
109
115
110 echo % skip files in excluded dirs
116 echo % skip files in excluded dirs
111 mkdir excluded_dir
117 mkdir excluded_dir
112 touch excluded_dir/file
118 touch excluded_dir/file
113 hg purge -p -X excluded_dir
119 hg purge -p -X excluded_dir
114 hg purge -v -X excluded_dir
120 hg purge -v -X excluded_dir
115 ls
121 ls
116 ls excluded_dir
122 ls excluded_dir
117 rm -R excluded_dir
123 rm -R excluded_dir
118
124
119 echo % skip excluded empty dirs
125 echo % skip excluded empty dirs
120 mkdir excluded_dir
126 mkdir excluded_dir
121 hg purge -p -X excluded_dir
127 hg purge -p -X excluded_dir
122 hg purge -v -X excluded_dir
128 hg purge -v -X excluded_dir
123 ls
129 ls
124 rmdir excluded_dir
130 rmdir excluded_dir
125
131
126 echo % skip patterns
132 echo % skip patterns
127 mkdir .svn
133 mkdir .svn
128 touch .svn/foo
134 touch .svn/foo
129 mkdir directory/.svn
135 mkdir directory/.svn
130 touch directory/.svn/foo
136 touch directory/.svn/foo
131 hg purge -p -X .svn -X '*/.svn'
137 hg purge -p -X .svn -X '*/.svn'
132 hg purge -p -X re:.*.svn
138 hg purge -p -X re:.*.svn
@@ -1,76 +1,78 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 untracked_file_readonly
18 Removing file untracked_file
19 Removing file untracked_file
20 Removing file untracked_file_readonly
19 directory
21 directory
20 r1
22 r1
21 % delete an untracked file in a tracked directory
23 % delete an untracked file in a tracked directory
22 directory/untracked_file
24 directory/untracked_file
23 Removing file directory/untracked_file
25 Removing file directory/untracked_file
24 directory
26 directory
25 r1
27 r1
26 % delete nested directories
28 % delete nested directories
27 untracked_directory/nested_directory
29 untracked_directory/nested_directory
28 Removing directory untracked_directory/nested_directory
30 Removing directory untracked_directory/nested_directory
29 Removing directory untracked_directory
31 Removing directory untracked_directory
30 directory
32 directory
31 r1
33 r1
32 % delete nested directories from a subdir
34 % delete nested directories from a subdir
33 untracked_directory/nested_directory
35 untracked_directory/nested_directory
34 Removing directory untracked_directory/nested_directory
36 Removing directory untracked_directory/nested_directory
35 Removing directory untracked_directory
37 Removing directory untracked_directory
36 directory
38 directory
37 r1
39 r1
38 % delete only part of the tree
40 % delete only part of the tree
39 untracked_directory/nested_directory
41 untracked_directory/nested_directory
40 Removing directory untracked_directory/nested_directory
42 Removing directory untracked_directory/nested_directory
41 Removing directory untracked_directory
43 Removing directory untracked_directory
42 directory
44 directory
43 r1
45 r1
44 directory/untracked_file
46 directory/untracked_file
45 % skip ignored files if --all not specified
47 % skip ignored files if --all not specified
46 directory
48 directory
47 ignored
49 ignored
48 r1
50 r1
49 ignored
51 ignored
50 Removing file ignored
52 Removing file ignored
51 directory
53 directory
52 r1
54 r1
53 % abort with missing files until we support name mangling filesystems
55 % abort with missing files until we support name mangling filesystems
54 untracked_file
56 untracked_file
55 ! r1
57 ! r1
56 ? untracked_file
58 ? untracked_file
57 untracked_file
59 untracked_file
58 Removing file untracked_file
60 Removing file untracked_file
59 ! r1
61 ! r1
60 % tracked file in ignored directory (issue621)
62 % tracked file in ignored directory (issue621)
61 untracked_file
63 untracked_file
62 Removing file untracked_file
64 Removing file untracked_file
63 % skip excluded files
65 % skip excluded files
64 directory
66 directory
65 excluded_file
67 excluded_file
66 r1
68 r1
67 % skip files in excluded dirs
69 % skip files in excluded dirs
68 directory
70 directory
69 excluded_dir
71 excluded_dir
70 r1
72 r1
71 file
73 file
72 % skip excluded empty dirs
74 % skip excluded empty dirs
73 directory
75 directory
74 excluded_dir
76 excluded_dir
75 r1
77 r1
76 % skip patterns
78 % skip patterns
General Comments 0
You need to be logged in to leave comments. Login now