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