##// END OF EJS Templates
purge: fix b777dd8f7836 (remove read-only files)...
Patrick Mezard -
r8044:c1e2b740 default
parent child Browse files
Show More
@@ -1,106 +1,110 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, stat
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):
75 def removefile(path):
76 # read-only files cannot be unlinked under Windows
76 try:
77 s = os.stat(path)
77 os.remove(path)
78 if (s.st_dev & stat.S_IWRITE) == 0:
78 except OSError:
79 os.chmod(path, s.st_mode | stat.S_IWRITE)
79 # read-only files cannot be unlinked under Windows
80 os.remove(path)
80 s = os.stat(path)
81 if (s.st_mode & stat.S_IWRITE) != 0:
82 raise
83 os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
84 os.remove(path)
81
85
82 directories = []
86 directories = []
83 match = cmdutil.match(repo, dirs, opts)
87 match = cmdutil.match(repo, dirs, opts)
84 match.dir = directories.append
88 match.dir = directories.append
85 status = repo.status(match=match, ignored=opts['all'], unknown=True)
89 status = repo.status(match=match, ignored=opts['all'], unknown=True)
86
90
87 for f in util.sort(status[4] + status[5]):
91 for f in util.sort(status[4] + status[5]):
88 ui.note(_('Removing file %s\n') % f)
92 ui.note(_('Removing file %s\n') % f)
89 remove(removefile, f)
93 remove(removefile, f)
90
94
91 for f in util.sort(directories)[::-1]:
95 for f in util.sort(directories)[::-1]:
92 if match(f) and not os.listdir(repo.wjoin(f)):
96 if match(f) and not os.listdir(repo.wjoin(f)):
93 ui.note(_('Removing directory %s\n') % f)
97 ui.note(_('Removing directory %s\n') % f)
94 remove(os.rmdir, f)
98 remove(os.rmdir, f)
95
99
96 cmdtable = {
100 cmdtable = {
97 'purge|clean':
101 'purge|clean':
98 (purge,
102 (purge,
99 [('a', 'abort-on-err', None, _('abort if an error occurs')),
103 [('a', 'abort-on-err', None, _('abort if an error occurs')),
100 ('', 'all', None, _('purge ignored files too')),
104 ('', 'all', None, _('purge ignored files too')),
101 ('p', 'print', None, _('print the file names instead of deleting them')),
105 ('p', 'print', None, _('print the file names instead of deleting them')),
102 ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
106 ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
103 ' (implies -p)')),
107 ' (implies -p)')),
104 ] + commands.walkopts,
108 ] + commands.walkopts,
105 _('hg purge [OPTION]... [DIR]...'))
109 _('hg purge [OPTION]... [DIR]...'))
106 }
110 }
@@ -1,138 +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
37 touch untracked_file_readonly
38 python <<EOF
38 python <<EOF
39 import os, stat
39 import os, stat
40 f= 'untracked_file_readonly'
40 f= 'untracked_file_readonly'
41 os.chmod(f, os.stat(f).st_mode & ~stat.S_IWRITE)
41 os.chmod(f, stat.S_IMODE(os.stat(f).st_mode) & ~stat.S_IWRITE)
42 EOF
42 EOF
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 an untracked file in a tracked directory
47 echo % delete an untracked file in a tracked directory
48 touch directory/untracked_file
48 touch directory/untracked_file
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
53 echo % delete nested directories
54 mkdir -p untracked_directory/nested_directory
54 mkdir -p untracked_directory/nested_directory
55 hg purge -p
55 hg purge -p
56 hg purge -v
56 hg purge -v
57 ls
57 ls
58
58
59 echo % delete nested directories from a subdir
59 echo % delete nested directories from a subdir
60 mkdir -p untracked_directory/nested_directory
60 mkdir -p untracked_directory/nested_directory
61 cd directory
61 cd directory
62 hg purge -p
62 hg purge -p
63 hg purge -v
63 hg purge -v
64 cd ..
64 cd ..
65 ls
65 ls
66
66
67 echo % delete only part of the tree
67 echo % delete only part of the tree
68 mkdir -p untracked_directory/nested_directory
68 mkdir -p untracked_directory/nested_directory
69 touch directory/untracked_file
69 touch directory/untracked_file
70 cd directory
70 cd directory
71 hg purge -p ../untracked_directory
71 hg purge -p ../untracked_directory
72 hg purge -v ../untracked_directory
72 hg purge -v ../untracked_directory
73 cd ..
73 cd ..
74 ls
74 ls
75 ls directory/untracked_file
75 ls directory/untracked_file
76 rm directory/untracked_file
76 rm directory/untracked_file
77
77
78 echo % skip ignored files if --all not specified
78 echo % skip ignored files if --all not specified
79 touch ignored
79 touch ignored
80 hg purge -p
80 hg purge -p
81 hg purge -v
81 hg purge -v
82 ls
82 ls
83 hg purge -p --all
83 hg purge -p --all
84 hg purge -v --all
84 hg purge -v --all
85 ls
85 ls
86
86
87 echo % abort with missing files until we support name mangling filesystems
87 echo % abort with missing files until we support name mangling filesystems
88 touch untracked_file
88 touch untracked_file
89 rm r1
89 rm r1
90 # 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
91 hg purge -p 2> /dev/null
91 hg purge -p 2> /dev/null
92 hg st
92 hg st
93
93
94 hg purge -p
94 hg purge -p
95 hg purge -v 2> /dev/null
95 hg purge -v 2> /dev/null
96 hg st
96 hg st
97
97
98 hg purge -v
98 hg purge -v
99 hg revert --all --quiet
99 hg revert --all --quiet
100 hg st -a
100 hg st -a
101
101
102 echo '% tracked file in ignored directory (issue621)'
102 echo '% tracked file in ignored directory (issue621)'
103 echo directory >> .hgignore
103 echo directory >> .hgignore
104 hg ci -m 'ignore directory'
104 hg ci -m 'ignore directory'
105 touch untracked_file
105 touch untracked_file
106 hg purge -p
106 hg purge -p
107 hg purge -v
107 hg purge -v
108
108
109 echo % skip excluded files
109 echo % skip excluded files
110 touch excluded_file
110 touch excluded_file
111 hg purge -p -X excluded_file
111 hg purge -p -X excluded_file
112 hg purge -v -X excluded_file
112 hg purge -v -X excluded_file
113 ls
113 ls
114 rm excluded_file
114 rm excluded_file
115
115
116 echo % skip files in excluded dirs
116 echo % skip files in excluded dirs
117 mkdir excluded_dir
117 mkdir excluded_dir
118 touch excluded_dir/file
118 touch excluded_dir/file
119 hg purge -p -X excluded_dir
119 hg purge -p -X excluded_dir
120 hg purge -v -X excluded_dir
120 hg purge -v -X excluded_dir
121 ls
121 ls
122 ls excluded_dir
122 ls excluded_dir
123 rm -R excluded_dir
123 rm -R excluded_dir
124
124
125 echo % skip excluded empty dirs
125 echo % skip excluded empty dirs
126 mkdir excluded_dir
126 mkdir excluded_dir
127 hg purge -p -X excluded_dir
127 hg purge -p -X excluded_dir
128 hg purge -v -X excluded_dir
128 hg purge -v -X excluded_dir
129 ls
129 ls
130 rmdir excluded_dir
130 rmdir excluded_dir
131
131
132 echo % skip patterns
132 echo % skip patterns
133 mkdir .svn
133 mkdir .svn
134 touch .svn/foo
134 touch .svn/foo
135 mkdir directory/.svn
135 mkdir directory/.svn
136 touch directory/.svn/foo
136 touch directory/.svn/foo
137 hg purge -p -X .svn -X '*/.svn'
137 hg purge -p -X .svn -X '*/.svn'
138 hg purge -p -X re:.*.svn
138 hg purge -p -X re:.*.svn
General Comments 0
You need to be logged in to leave comments. Login now