comparison purge.py @ 2364:f368a1c302d5

Initial commit
author demian@gaudron.lan
date Fri, 12 May 2006 20:26:19 +0200
parents
children 9da3dd62c827
comparison
equal deleted inserted replaced
-1:000000000000 2364:f368a1c302d5
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 class Purge(object):
26 '''removes files not tracked by mercurial
27
28 Delete files not known to mercurial, this is useful to test local and
29 uncommitted changes in the otherwise clean source tree.
30
31 This means that purge will delete:
32 - Unknown files: files marked with "?" by "hg status"
33 - Ignored files: files usually ignored by Mercurial because they match a
34 pattern in a ".hgignore" file
35 - Empty directories: infact Mercurial ignores directories unless they
36 contain files under source control managment
37 But it will leave untouched:
38 - Unmodified tracked files
39 - Modified tracked files
40 - New files added to the repository (with "hg add")
41
42 If names are given, only files matching the names are considered, else
43 all files in the repository directory are considered.
44
45 Be careful with purge, you could irreversibly delete some files you
46 forgot to add to the repository. If you only want to print the list of
47 files that this program would delete use the -vn options.
48 '''
49
50 def __init__(self, act=True, abort_on_err=False):
51 self._repo = None
52 self._ui = None
53 self._hg_root = None
54 self._act = act
55 self._abort_on_err = abort_on_err
56
57 def purge(self, ui, repo, paths=None):
58 self._repo = repo
59 self._ui = ui
60 self._hg_root = self._split_path(repo.root)
61
62 if not paths:
63 paths = [repo.root]
64
65 for path in paths:
66 path = os.path.abspath(path)
67 for root, dirs, files in os.walk(path, topdown=False):
68 if '.hg' in self._split_path(root):
69 # Skip files in the .hg directory.
70 # Note that if the repository is in a directory
71 # called .hg this command does not work.
72 continue
73 for name in files:
74 self._remove_file(os.path.join(root, name))
75 if not os.listdir(root):
76 # Remove this directory if it is empty.
77 self._remove_dir(root)
78
79 self._repo = None
80 self._ui = None
81 self._hg_root = None
82
83 def _error(self, msg):
84 if self._abort_on_err:
85 raise util.Abort(msg)
86 else:
87 ui.warn('warning: ' + msg + '\n')
88
89 def _remove_file(self, name):
90 relative_name = self._relative_name(name)
91 # dirstate.state() requires a path relative to the root
92 # directory.
93 if self._repo.dirstate.state(relative_name) != '?':
94 return
95 if self._ui.verbose:
96 self._ui.status(name + '\n')
97 if self._act:
98 try:
99 os.remove(name)
100 except OSError, e:
101 error('"%s" cannot be removed' % name)
102
103 def _remove_dir(self, name):
104 if self._ui.verbose:
105 self._ui.status(name + '\n')
106 if self._act:
107 try:
108 os.rmdir(name)
109 except OSError, e:
110 error('"%s" cannot be removed' % name)
111
112 def _relative_name(self, name):
113 splitted_path = self._split_path(name)[len(self._hg_root):]
114 return self._join_path(splitted_path)
115
116 def _split_path(self, path):
117 ret = []
118 while True:
119 head, tail = os.path.split(path)
120 if tail:
121 ret.append(tail)
122 if head == path:
123 ret.append(head)
124 break
125 path = head
126 ret.reverse()
127 return ret
128
129 def _join_path(self, splitted_path):
130 ret = ''
131 for part in splitted_path:
132 if ret:
133 ret = os.path.join(ret, part)
134 else:
135 ret = part
136 return ret
137
138 def from_command(ui, repo, *paths, **opts):
139 act = True
140 if opts['nothing']:
141 act = False
142
143 abort_on_err = True
144 if not opts['abort_on_err']:
145 abort_on_err = False
146
147 p = Purge(act, abort_on_err)
148 p.purge(ui, repo, paths)
149
150 # The docstring of from_command() is used by hg to print the help
151 # of the command.
152 from_command.__doc__ = __doc__
153 from_command = staticmethod(from_command)
154
155
156 cmdtable = {
157 'purge': (Purge.from_command,
158 [('a', 'abort-on-err', None, 'abort if an error occurs'),
159 ('n', 'nothing', None, 'do nothing on files, useful with --verbose'),
160 ],
161 'hg purge [OPTIONS] [NAME]')
162 }