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]...'))