##// END OF EJS Templates
Make the purge extension use the statwalk walker from the dirstate object
Emanuele Aina -
r4147:691f9168 default
parent child Browse files
Show More
@@ -1,176 +1,178 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 is free software; you can redistribute it and/or modify
7 7 # it under the terms of the GNU General Public License as published by
8 8 # the Free Software Foundation; either version 2 of the License, or
9 9 # (at your option) any later version.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 19
20 20 from mercurial import hg, util
21 21 from mercurial.i18n import _
22 22 import os
23 23
24 24 class Purge(object):
25 25 def __init__(self, act=True, abort_on_err=False, eol='\n'):
26 26 self._repo = None
27 27 self._ui = None
28 28 self._hg_root = None
29 29 self._act = act
30 30 self._abort_on_err = abort_on_err
31 31 self._eol = eol
32 32
33 33 def purge(self, ui, repo, dirs=None):
34 34 self._repo = repo
35 35 self._ui = ui
36 36 self._hg_root = self._split_path(repo.root)
37
38 if not dirs:
39 dirs = [repo.root]
37
38 directories = []
39 files = []
40 for src, f, st in repo.dirstate.statwalk(files=dirs, ignored=True,
41 directories=True):
42 if src == 'd':
43 directories.append(f)
44 elif src == 'f' and f not in repo.dirstate:
45 files.append(f)
40 46
41 for path in dirs:
42 path = os.path.abspath(path)
43 for root, dirs, files in os.walk(path, topdown=False):
44 if '.hg' in self._split_path(root):
45 # Skip files in the .hg directory.
46 # Note that if the repository is in a directory
47 # called .hg this command does not work.
48 continue
49 for name in files:
50 self._remove_file(os.path.join(root, name))
51 if not os.listdir(root):
52 # Remove this directory if it is empty.
53 self._remove_dir(root)
47 directories.sort()
48
49 for f in files:
50 self._remove_file(os.path.join(repo.root, f))
51
52 for f in directories[::-1]:
53 f = os.path.join(repo.root, f)
54 if not os.listdir(f):
55 self._remove_dir(f)
54 56
55 57 self._repo = None
56 58 self._ui = None
57 59 self._hg_root = None
58 60
59 61 def _error(self, msg):
60 62 if self._abort_on_err:
61 63 raise util.Abort(msg)
62 64 else:
63 65 self._ui.warn(_('warning: %s\n') % msg)
64 66
65 67 def _remove_file(self, name):
66 68 relative_name = self._relative_name(name)
67 69 # dirstate.state() requires a path relative to the root
68 70 # directory.
69 71 if self._repo.dirstate.state(relative_name) != '?':
70 72 return
71 73 self._ui.note(_('Removing file %s\n') % relative_name)
72 74 if self._act:
73 75 try:
74 76 os.remove(name)
75 77 except OSError, e:
76 78 self._error(_('%s cannot be removed') % relative_name)
77 79 else:
78 80 self._ui.write('%s%s' % (relative_name, self._eol))
79 81
80 82 def _remove_dir(self, name):
81 83 relative_name = self._relative_name(name)
82 84 self._ui.note(_('Removing directory %s\n') % relative_name)
83 85 if self._act:
84 86 try:
85 87 os.rmdir(name)
86 88 except OSError, e:
87 89 self._error(_('%s cannot be removed') % relative_name)
88 90 else:
89 91 self._ui.write('%s%s' % (relative_name, self._eol))
90 92
91 93 def _relative_name(self, path):
92 94 '''
93 95 Returns "path" but relative to the root directory of the
94 96 repository and with '\\' replaced with '/'.
95 97 This is needed because this is the format required by
96 98 self._repo.dirstate.state().
97 99 '''
98 100 splitted_path = self._split_path(path)[len(self._hg_root):]
99 101 # Even on Windows self._repo.dirstate.state() wants '/'.
100 102 return self._join_path(splitted_path).replace('\\', '/')
101 103
102 104 def _split_path(self, path):
103 105 '''
104 106 Returns a list of the single files/directories in "path".
105 107 For instance:
106 108 '/home/user/test' -> ['/', 'home', 'user', 'test']
107 109 'C:\\Mercurial' -> ['C:\\', 'Mercurial']
108 110 '''
109 111 ret = []
110 112 while True:
111 113 head, tail = os.path.split(path)
112 114 if tail:
113 115 ret.append(tail)
114 116 if head == path:
115 117 ret.append(head)
116 118 break
117 119 path = head
118 120 ret.reverse()
119 121 return ret
120 122
121 123 def _join_path(self, splitted_path):
122 124 '''
123 125 Joins a list returned by _split_path().
124 126 '''
125 127 ret = ''
126 128 for part in splitted_path:
127 129 if ret:
128 130 ret = os.path.join(ret, part)
129 131 else:
130 132 ret = part
131 133 return ret
132 134
133 135
134 136 def purge(ui, repo, *dirs, **opts):
135 137 '''removes files not tracked by mercurial
136 138
137 139 Delete files not known to mercurial, this is useful to test local and
138 140 uncommitted changes in the otherwise clean source tree.
139 141
140 142 This means that purge will delete:
141 143 - Unknown files: files marked with "?" by "hg status"
142 144 - Ignored files: files usually ignored by Mercurial because they match
143 145 a pattern in a ".hgignore" file
144 146 - Empty directories: in fact Mercurial ignores directories unless they
145 147 contain files under source control managment
146 148 But it will leave untouched:
147 149 - Unmodified tracked files
148 150 - Modified tracked files
149 151 - New files added to the repository (with "hg add")
150 152
151 153 If directories are given on the command line, only files in these
152 154 directories are considered.
153 155
154 156 Be careful with purge, you could irreversibly delete some files you
155 157 forgot to add to the repository. If you only want to print the list of
156 158 files that this program would delete use the --print option.
157 159 '''
158 160 act = not opts['print']
159 161 abort_on_err = bool(opts['abort_on_err'])
160 162 eol = opts['print0'] and '\0' or '\n'
161 163 if eol == '\0':
162 164 # --print0 implies --print
163 165 act = False
164 166 p = Purge(act, abort_on_err, eol)
165 167 p.purge(ui, repo, dirs)
166 168
167 169
168 170 cmdtable = {
169 171 'purge':
170 172 (purge,
171 173 [('a', 'abort-on-err', None, _('abort if an error occurs')),
172 174 ('p', 'print', None, _('print the file names instead of deleting them')),
173 175 ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
174 176 ' (implies -p)'))],
175 177 _('hg purge [OPTION]... [DIR]...'))
176 178 }
General Comments 0
You need to be logged in to leave comments. Login now