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): |
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: |
69 if self._act: |
75 try: |
70 try: |
76 os.remove(name) |
71 os.remove(self._repo.wjoin(name)) |
77 except OSError, e: |
72 except OSError, e: |
78 self._error(_('%s cannot be removed') % relative_name) |
73 self._error(_('%s cannot be removed') % name) |
79 else: |
74 else: |
80 self._ui.write('%s%s' % (relative_name, self._eol)) |
75 self._ui.write('%s%s' % (name, self._eol)) |
81 |
76 |
82 def _remove_dir(self, name): |
77 def _remove_dir(self, name): |
83 relative_name = self._relative_name(name) |
78 self._ui.note(_('Removing directory %s\n') % name) |
84 self._ui.note(_('Removing directory %s\n') % relative_name) |
|
85 if self._act: |
79 if self._act: |
86 try: |
80 try: |
87 os.rmdir(name) |
81 os.rmdir(self._repo.wjoin(name)) |
88 except OSError, e: |
82 except OSError, e: |
89 self._error(_('%s cannot be removed') % relative_name) |
83 self._error(_('%s cannot be removed') % name) |
90 else: |
84 else: |
91 self._ui.write('%s%s' % (relative_name, self._eol)) |
85 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 |
|
104 def _split_path(self, path): |
|
105 ''' |
|
106 Returns a list of the single files/directories in "path". |
|
107 For instance: |
|
108 '/home/user/test' -> ['/', 'home', 'user', 'test'] |
|
109 'C:\\Mercurial' -> ['C:\\', 'Mercurial'] |
|
110 ''' |
|
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: |
|
132 ret = part |
|
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 |