Mercurial > hg
comparison hgext/purge.py @ 5517:98d5f9b95699
purge: simplify safety net for case mangling filesystems
Relying on the exact return of statwalk would cause us to abort
when there was at least one tracked file inside an ignored directory.
This patch forces an extra walk of the whole working directory even
on sane filesystems, where it wouldn't be needed.
Fixes issue621.
author | Alexis S. L. Carvalho <alexis@cecm.usp.br> |
---|---|
date | Fri, 09 Nov 2007 20:21:35 -0200 |
parents | c80af96943aa |
children | e75aab656f46 |
comparison
equal
deleted
inserted
replaced
5516:f252ba975925 | 5517:98d5f9b95699 |
---|---|
47 except OSError, e: | 47 except OSError, e: |
48 error(_('%s cannot be removed') % name) | 48 error(_('%s cannot be removed') % name) |
49 else: | 49 else: |
50 ui.write('%s%s' % (name, eol)) | 50 ui.write('%s%s' % (name, eol)) |
51 | 51 |
52 if not force: | |
53 _check_fs(ui, repo) | |
54 | |
52 directories = [] | 55 directories = [] |
53 files = [] | 56 files = [] |
54 missing = [] | 57 missing = [] |
55 roots, match, anypats = util.cmdmatcher(repo.root, repo.getcwd(), dirs, | 58 roots, match, anypats = util.cmdmatcher(repo.root, repo.getcwd(), dirs, |
56 include, exclude) | 59 include, exclude) |
61 elif src == 'm': | 64 elif src == 'm': |
62 missing.append(f) | 65 missing.append(f) |
63 elif src == 'f' and f not in repo.dirstate: | 66 elif src == 'f' and f not in repo.dirstate: |
64 files.append(f) | 67 files.append(f) |
65 | 68 |
66 _check_missing(ui, repo, missing, force) | |
67 | |
68 directories.sort() | 69 directories.sort() |
69 | 70 |
70 for f in files: | 71 for f in files: |
71 if f not in repo.dirstate: | 72 if f not in repo.dirstate: |
72 ui.note(_('Removing file %s\n') % f) | 73 ui.note(_('Removing file %s\n') % f) |
75 for f in directories[::-1]: | 76 for f in directories[::-1]: |
76 if match(f) and not os.listdir(repo.wjoin(f)): | 77 if match(f) and not os.listdir(repo.wjoin(f)): |
77 ui.note(_('Removing directory %s\n') % f) | 78 ui.note(_('Removing directory %s\n') % f) |
78 remove(os.rmdir, f) | 79 remove(os.rmdir, f) |
79 | 80 |
80 def _check_missing(ui, repo, missing, force=False): | 81 def _check_fs(ui, repo): |
81 """Abort if there is the chance of having problems with name-mangling fs | 82 """Abort if there is the chance of having problems with name-mangling fs |
82 | 83 |
83 In a name mangling filesystem (e.g. a case insensitive one) | 84 In a name mangling filesystem (e.g. a case insensitive one) |
84 dirstate.walk() can yield filenames different from the ones | 85 dirstate.walk() can yield filenames different from the ones |
85 stored in the dirstate. This already confuses the status and | 86 stored in the dirstate. This already confuses the status and |
86 add commands, but with purge this may cause data loss. | 87 add commands, but with purge this may cause data loss. |
87 | 88 |
88 To prevent this, _check_missing will abort if there are missing | 89 To prevent this, this function will abort if there are uncommitted |
89 files. The force option will let the user skip the check if he | 90 changes. |
90 knows it is safe. | 91 """ |
91 | 92 |
92 Even with the force option this function will check if any of the | 93 # We can't use (files, match) to do a partial walk here - we wouldn't |
93 missing files is still available in the working dir: if so there | 94 # notice a modified README file if the user ran "hg purge readme" |
94 may be some problem with the underlying filesystem, so it | 95 modified, added, removed, deleted = repo.status()[:4] |
95 aborts unconditionally.""" | 96 if modified or added or removed or deleted: |
96 | 97 if not util.checkfolding(repo.path) and not ui.quiet: |
97 found = [f for f in missing if util.lexists(repo.wjoin(f))] | 98 ui.warn(_("Purging on name mangling filesystems is not " |
98 | 99 "fully supported.\n")) |
99 if found: | 100 raise util.Abort(_("outstanding uncommitted changes")) |
100 if not ui.quiet: | |
101 ui.warn(_("The following tracked files weren't listed by the " | |
102 "filesystem, but could still be found:\n")) | |
103 for f in found: | |
104 ui.warn("%s\n" % f) | |
105 if util.checkfolding(repo.path): | |
106 ui.warn(_("This is probably due to a case-insensitive " | |
107 "filesystem\n")) | |
108 raise util.Abort(_("purging on name mangling filesystems is not " | |
109 "yet fully supported")) | |
110 | |
111 if missing and not force: | |
112 raise util.Abort(_("there are missing files in the working dir and " | |
113 "purge still has problems with them due to name " | |
114 "mangling filesystems. " | |
115 "Use --force if you know what you are doing")) | |
116 | 101 |
117 | 102 |
118 def purge(ui, repo, *dirs, **opts): | 103 def purge(ui, repo, *dirs, **opts): |
119 '''removes files not tracked by mercurial | 104 '''removes files not tracked by mercurial |
120 | 105 |
156 cmdtable = { | 141 cmdtable = { |
157 'purge|clean': | 142 'purge|clean': |
158 (purge, | 143 (purge, |
159 [('a', 'abort-on-err', None, _('abort if an error occurs')), | 144 [('a', 'abort-on-err', None, _('abort if an error occurs')), |
160 ('', 'all', None, _('purge ignored files too')), | 145 ('', 'all', None, _('purge ignored files too')), |
161 ('f', 'force', None, _('purge even when missing files are detected')), | 146 ('f', 'force', None, _('purge even when there are uncommitted changes')), |
162 ('p', 'print', None, _('print the file names instead of deleting them')), | 147 ('p', 'print', None, _('print the file names instead of deleting them')), |
163 ('0', 'print0', None, _('end filenames with NUL, for use with xargs' | 148 ('0', 'print0', None, _('end filenames with NUL, for use with xargs' |
164 ' (implies -p)')), | 149 ' (implies -p)')), |
165 ] + commands.walkopts, | 150 ] + commands.walkopts, |
166 _('hg purge [OPTION]... [DIR]...')) | 151 _('hg purge [OPTION]... [DIR]...')) |