comparison hgext/purge/__init__.py @ 2379:e90cff87f871

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