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