##// END OF EJS Templates
narrow: replace filtering in list comprehension by set operations...
Martin von Zweigbergk -
r40465:5d8f2914 default
parent child Browse files
Show More
@@ -1,469 +1,469 b''
1 # narrowcommands.py - command modifications for narrowhg extension
1 # narrowcommands.py - command modifications for narrowhg extension
2 #
2 #
3 # Copyright 2017 Google, Inc.
3 # Copyright 2017 Google, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 from __future__ import absolute_import
7 from __future__ import absolute_import
8
8
9 import itertools
9 import itertools
10 import os
10 import os
11
11
12 from mercurial.i18n import _
12 from mercurial.i18n import _
13 from mercurial import (
13 from mercurial import (
14 bundle2,
14 bundle2,
15 cmdutil,
15 cmdutil,
16 commands,
16 commands,
17 discovery,
17 discovery,
18 encoding,
18 encoding,
19 error,
19 error,
20 exchange,
20 exchange,
21 extensions,
21 extensions,
22 hg,
22 hg,
23 merge,
23 merge,
24 narrowspec,
24 narrowspec,
25 node,
25 node,
26 pycompat,
26 pycompat,
27 registrar,
27 registrar,
28 repair,
28 repair,
29 repository,
29 repository,
30 repoview,
30 repoview,
31 sparse,
31 sparse,
32 util,
32 util,
33 wireprototypes,
33 wireprototypes,
34 )
34 )
35
35
36 table = {}
36 table = {}
37 command = registrar.command(table)
37 command = registrar.command(table)
38
38
39 def setup():
39 def setup():
40 """Wraps user-facing mercurial commands with narrow-aware versions."""
40 """Wraps user-facing mercurial commands with narrow-aware versions."""
41
41
42 entry = extensions.wrapcommand(commands.table, 'clone', clonenarrowcmd)
42 entry = extensions.wrapcommand(commands.table, 'clone', clonenarrowcmd)
43 entry[1].append(('', 'narrow', None,
43 entry[1].append(('', 'narrow', None,
44 _("create a narrow clone of select files")))
44 _("create a narrow clone of select files")))
45 entry[1].append(('', 'depth', '',
45 entry[1].append(('', 'depth', '',
46 _("limit the history fetched by distance from heads")))
46 _("limit the history fetched by distance from heads")))
47 entry[1].append(('', 'narrowspec', '',
47 entry[1].append(('', 'narrowspec', '',
48 _("read narrowspecs from file")))
48 _("read narrowspecs from file")))
49 # TODO(durin42): unify sparse/narrow --include/--exclude logic a bit
49 # TODO(durin42): unify sparse/narrow --include/--exclude logic a bit
50 if 'sparse' not in extensions.enabled():
50 if 'sparse' not in extensions.enabled():
51 entry[1].append(('', 'include', [],
51 entry[1].append(('', 'include', [],
52 _("specifically fetch this file/directory")))
52 _("specifically fetch this file/directory")))
53 entry[1].append(
53 entry[1].append(
54 ('', 'exclude', [],
54 ('', 'exclude', [],
55 _("do not fetch this file/directory, even if included")))
55 _("do not fetch this file/directory, even if included")))
56
56
57 entry = extensions.wrapcommand(commands.table, 'pull', pullnarrowcmd)
57 entry = extensions.wrapcommand(commands.table, 'pull', pullnarrowcmd)
58 entry[1].append(('', 'depth', '',
58 entry[1].append(('', 'depth', '',
59 _("limit the history fetched by distance from heads")))
59 _("limit the history fetched by distance from heads")))
60
60
61 extensions.wrapcommand(commands.table, 'archive', archivenarrowcmd)
61 extensions.wrapcommand(commands.table, 'archive', archivenarrowcmd)
62
62
63 def clonenarrowcmd(orig, ui, repo, *args, **opts):
63 def clonenarrowcmd(orig, ui, repo, *args, **opts):
64 """Wraps clone command, so 'hg clone' first wraps localrepo.clone()."""
64 """Wraps clone command, so 'hg clone' first wraps localrepo.clone()."""
65 opts = pycompat.byteskwargs(opts)
65 opts = pycompat.byteskwargs(opts)
66 wrappedextraprepare = util.nullcontextmanager()
66 wrappedextraprepare = util.nullcontextmanager()
67 narrowspecfile = opts['narrowspec']
67 narrowspecfile = opts['narrowspec']
68
68
69 if narrowspecfile:
69 if narrowspecfile:
70 filepath = os.path.join(encoding.getcwd(), narrowspecfile)
70 filepath = os.path.join(encoding.getcwd(), narrowspecfile)
71 ui.status(_("reading narrowspec from '%s'\n") % filepath)
71 ui.status(_("reading narrowspec from '%s'\n") % filepath)
72 try:
72 try:
73 fdata = util.readfile(filepath)
73 fdata = util.readfile(filepath)
74 except IOError as inst:
74 except IOError as inst:
75 raise error.Abort(_("cannot read narrowspecs from '%s': %s") %
75 raise error.Abort(_("cannot read narrowspecs from '%s': %s") %
76 (filepath, encoding.strtolocal(inst.strerror)))
76 (filepath, encoding.strtolocal(inst.strerror)))
77
77
78 includes, excludes, profiles = sparse.parseconfig(ui, fdata, 'narrow')
78 includes, excludes, profiles = sparse.parseconfig(ui, fdata, 'narrow')
79 if profiles:
79 if profiles:
80 raise error.Abort(_("cannot specify other files using '%include' in"
80 raise error.Abort(_("cannot specify other files using '%include' in"
81 " narrowspec"))
81 " narrowspec"))
82
82
83 narrowspec.validatepatterns(includes)
83 narrowspec.validatepatterns(includes)
84 narrowspec.validatepatterns(excludes)
84 narrowspec.validatepatterns(excludes)
85
85
86 # narrowspec is passed so we should assume that user wants narrow clone
86 # narrowspec is passed so we should assume that user wants narrow clone
87 opts['narrow'] = True
87 opts['narrow'] = True
88 opts['include'].extend(includes)
88 opts['include'].extend(includes)
89 opts['exclude'].extend(excludes)
89 opts['exclude'].extend(excludes)
90
90
91 if opts['narrow']:
91 if opts['narrow']:
92 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
92 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
93 orig(pullop, kwargs)
93 orig(pullop, kwargs)
94
94
95 if opts.get('depth'):
95 if opts.get('depth'):
96 kwargs['depth'] = opts['depth']
96 kwargs['depth'] = opts['depth']
97 wrappedextraprepare = extensions.wrappedfunction(exchange,
97 wrappedextraprepare = extensions.wrappedfunction(exchange,
98 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
98 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
99
99
100 with wrappedextraprepare:
100 with wrappedextraprepare:
101 return orig(ui, repo, *args, **pycompat.strkwargs(opts))
101 return orig(ui, repo, *args, **pycompat.strkwargs(opts))
102
102
103 def pullnarrowcmd(orig, ui, repo, *args, **opts):
103 def pullnarrowcmd(orig, ui, repo, *args, **opts):
104 """Wraps pull command to allow modifying narrow spec."""
104 """Wraps pull command to allow modifying narrow spec."""
105 wrappedextraprepare = util.nullcontextmanager()
105 wrappedextraprepare = util.nullcontextmanager()
106 if repository.NARROW_REQUIREMENT in repo.requirements:
106 if repository.NARROW_REQUIREMENT in repo.requirements:
107
107
108 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
108 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
109 orig(pullop, kwargs)
109 orig(pullop, kwargs)
110 if opts.get(r'depth'):
110 if opts.get(r'depth'):
111 kwargs['depth'] = opts[r'depth']
111 kwargs['depth'] = opts[r'depth']
112 wrappedextraprepare = extensions.wrappedfunction(exchange,
112 wrappedextraprepare = extensions.wrappedfunction(exchange,
113 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
113 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
114
114
115 with wrappedextraprepare:
115 with wrappedextraprepare:
116 return orig(ui, repo, *args, **opts)
116 return orig(ui, repo, *args, **opts)
117
117
118 def archivenarrowcmd(orig, ui, repo, *args, **opts):
118 def archivenarrowcmd(orig, ui, repo, *args, **opts):
119 """Wraps archive command to narrow the default includes."""
119 """Wraps archive command to narrow the default includes."""
120 if repository.NARROW_REQUIREMENT in repo.requirements:
120 if repository.NARROW_REQUIREMENT in repo.requirements:
121 repo_includes, repo_excludes = repo.narrowpats
121 repo_includes, repo_excludes = repo.narrowpats
122 includes = set(opts.get(r'include', []))
122 includes = set(opts.get(r'include', []))
123 excludes = set(opts.get(r'exclude', []))
123 excludes = set(opts.get(r'exclude', []))
124 includes, excludes, unused_invalid = narrowspec.restrictpatterns(
124 includes, excludes, unused_invalid = narrowspec.restrictpatterns(
125 includes, excludes, repo_includes, repo_excludes)
125 includes, excludes, repo_includes, repo_excludes)
126 if includes:
126 if includes:
127 opts[r'include'] = includes
127 opts[r'include'] = includes
128 if excludes:
128 if excludes:
129 opts[r'exclude'] = excludes
129 opts[r'exclude'] = excludes
130 return orig(ui, repo, *args, **opts)
130 return orig(ui, repo, *args, **opts)
131
131
132 def pullbundle2extraprepare(orig, pullop, kwargs):
132 def pullbundle2extraprepare(orig, pullop, kwargs):
133 repo = pullop.repo
133 repo = pullop.repo
134 if repository.NARROW_REQUIREMENT not in repo.requirements:
134 if repository.NARROW_REQUIREMENT not in repo.requirements:
135 return orig(pullop, kwargs)
135 return orig(pullop, kwargs)
136
136
137 if wireprototypes.NARROWCAP not in pullop.remote.capabilities():
137 if wireprototypes.NARROWCAP not in pullop.remote.capabilities():
138 raise error.Abort(_("server does not support narrow clones"))
138 raise error.Abort(_("server does not support narrow clones"))
139 orig(pullop, kwargs)
139 orig(pullop, kwargs)
140 kwargs['narrow'] = True
140 kwargs['narrow'] = True
141 include, exclude = repo.narrowpats
141 include, exclude = repo.narrowpats
142 kwargs['oldincludepats'] = include
142 kwargs['oldincludepats'] = include
143 kwargs['oldexcludepats'] = exclude
143 kwargs['oldexcludepats'] = exclude
144 kwargs['includepats'] = include
144 kwargs['includepats'] = include
145 kwargs['excludepats'] = exclude
145 kwargs['excludepats'] = exclude
146 # calculate known nodes only in ellipses cases because in non-ellipses cases
146 # calculate known nodes only in ellipses cases because in non-ellipses cases
147 # we have all the nodes
147 # we have all the nodes
148 if wireprototypes.ELLIPSESCAP in pullop.remote.capabilities():
148 if wireprototypes.ELLIPSESCAP in pullop.remote.capabilities():
149 kwargs['known'] = [node.hex(ctx.node()) for ctx in
149 kwargs['known'] = [node.hex(ctx.node()) for ctx in
150 repo.set('::%ln', pullop.common)
150 repo.set('::%ln', pullop.common)
151 if ctx.node() != node.nullid]
151 if ctx.node() != node.nullid]
152 if not kwargs['known']:
152 if not kwargs['known']:
153 # Mercurial serializes an empty list as '' and deserializes it as
153 # Mercurial serializes an empty list as '' and deserializes it as
154 # [''], so delete it instead to avoid handling the empty string on
154 # [''], so delete it instead to avoid handling the empty string on
155 # the server.
155 # the server.
156 del kwargs['known']
156 del kwargs['known']
157
157
158 extensions.wrapfunction(exchange,'_pullbundle2extraprepare',
158 extensions.wrapfunction(exchange,'_pullbundle2extraprepare',
159 pullbundle2extraprepare)
159 pullbundle2extraprepare)
160
160
161 # This is an extension point for filesystems that need to do something other
161 # This is an extension point for filesystems that need to do something other
162 # than just blindly unlink the files. It's not clear what arguments would be
162 # than just blindly unlink the files. It's not clear what arguments would be
163 # useful, so we're passing in a fair number of them, some of them redundant.
163 # useful, so we're passing in a fair number of them, some of them redundant.
164 def _narrowcleanupwdir(repo, oldincludes, oldexcludes, newincludes, newexcludes,
164 def _narrowcleanupwdir(repo, oldincludes, oldexcludes, newincludes, newexcludes,
165 oldmatch, newmatch):
165 oldmatch, newmatch):
166 for f in repo.dirstate:
166 for f in repo.dirstate:
167 if not newmatch(f):
167 if not newmatch(f):
168 repo.dirstate.drop(f)
168 repo.dirstate.drop(f)
169 repo.wvfs.unlinkpath(f)
169 repo.wvfs.unlinkpath(f)
170
170
171 def _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
171 def _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
172 newincludes, newexcludes, force):
172 newincludes, newexcludes, force):
173 oldmatch = narrowspec.match(repo.root, oldincludes, oldexcludes)
173 oldmatch = narrowspec.match(repo.root, oldincludes, oldexcludes)
174 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
174 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
175
175
176 # This is essentially doing "hg outgoing" to find all local-only
176 # This is essentially doing "hg outgoing" to find all local-only
177 # commits. We will then check that the local-only commits don't
177 # commits. We will then check that the local-only commits don't
178 # have any changes to files that will be untracked.
178 # have any changes to files that will be untracked.
179 unfi = repo.unfiltered()
179 unfi = repo.unfiltered()
180 outgoing = discovery.findcommonoutgoing(unfi, remote,
180 outgoing = discovery.findcommonoutgoing(unfi, remote,
181 commoninc=commoninc)
181 commoninc=commoninc)
182 ui.status(_('looking for local changes to affected paths\n'))
182 ui.status(_('looking for local changes to affected paths\n'))
183 localnodes = []
183 localnodes = []
184 for n in itertools.chain(outgoing.missing, outgoing.excluded):
184 for n in itertools.chain(outgoing.missing, outgoing.excluded):
185 if any(oldmatch(f) and not newmatch(f) for f in unfi[n].files()):
185 if any(oldmatch(f) and not newmatch(f) for f in unfi[n].files()):
186 localnodes.append(n)
186 localnodes.append(n)
187 revstostrip = unfi.revs('descendants(%ln)', localnodes)
187 revstostrip = unfi.revs('descendants(%ln)', localnodes)
188 hiddenrevs = repoview.filterrevs(repo, 'visible')
188 hiddenrevs = repoview.filterrevs(repo, 'visible')
189 visibletostrip = list(repo.changelog.node(r)
189 visibletostrip = list(repo.changelog.node(r)
190 for r in (revstostrip - hiddenrevs))
190 for r in (revstostrip - hiddenrevs))
191 if visibletostrip:
191 if visibletostrip:
192 ui.status(_('The following changeset(s) or their ancestors have '
192 ui.status(_('The following changeset(s) or their ancestors have '
193 'local changes not on the remote:\n'))
193 'local changes not on the remote:\n'))
194 maxnodes = 10
194 maxnodes = 10
195 if ui.verbose or len(visibletostrip) <= maxnodes:
195 if ui.verbose or len(visibletostrip) <= maxnodes:
196 for n in visibletostrip:
196 for n in visibletostrip:
197 ui.status('%s\n' % node.short(n))
197 ui.status('%s\n' % node.short(n))
198 else:
198 else:
199 for n in visibletostrip[:maxnodes]:
199 for n in visibletostrip[:maxnodes]:
200 ui.status('%s\n' % node.short(n))
200 ui.status('%s\n' % node.short(n))
201 ui.status(_('...and %d more, use --verbose to list all\n') %
201 ui.status(_('...and %d more, use --verbose to list all\n') %
202 (len(visibletostrip) - maxnodes))
202 (len(visibletostrip) - maxnodes))
203 if not force:
203 if not force:
204 raise error.Abort(_('local changes found'),
204 raise error.Abort(_('local changes found'),
205 hint=_('use --force-delete-local-changes to '
205 hint=_('use --force-delete-local-changes to '
206 'ignore'))
206 'ignore'))
207
207
208 with ui.uninterruptable():
208 with ui.uninterruptable():
209 if revstostrip:
209 if revstostrip:
210 tostrip = [unfi.changelog.node(r) for r in revstostrip]
210 tostrip = [unfi.changelog.node(r) for r in revstostrip]
211 if repo['.'].node() in tostrip:
211 if repo['.'].node() in tostrip:
212 # stripping working copy, so move to a different commit first
212 # stripping working copy, so move to a different commit first
213 urev = max(repo.revs('(::%n) - %ln + null',
213 urev = max(repo.revs('(::%n) - %ln + null',
214 repo['.'].node(), visibletostrip))
214 repo['.'].node(), visibletostrip))
215 hg.clean(repo, urev)
215 hg.clean(repo, urev)
216 repair.strip(ui, unfi, tostrip, topic='narrow')
216 repair.strip(ui, unfi, tostrip, topic='narrow')
217
217
218 todelete = []
218 todelete = []
219 for f, f2, size in repo.store.datafiles():
219 for f, f2, size in repo.store.datafiles():
220 if f.startswith('data/'):
220 if f.startswith('data/'):
221 file = f[5:-2]
221 file = f[5:-2]
222 if not newmatch(file):
222 if not newmatch(file):
223 todelete.append(f)
223 todelete.append(f)
224 elif f.startswith('meta/'):
224 elif f.startswith('meta/'):
225 dir = f[5:-13]
225 dir = f[5:-13]
226 dirs = ['.'] + sorted(util.dirs({dir})) + [dir]
226 dirs = ['.'] + sorted(util.dirs({dir})) + [dir]
227 include = True
227 include = True
228 for d in dirs:
228 for d in dirs:
229 visit = newmatch.visitdir(d)
229 visit = newmatch.visitdir(d)
230 if not visit:
230 if not visit:
231 include = False
231 include = False
232 break
232 break
233 if visit == 'all':
233 if visit == 'all':
234 break
234 break
235 if not include:
235 if not include:
236 todelete.append(f)
236 todelete.append(f)
237
237
238 repo.destroying()
238 repo.destroying()
239
239
240 with repo.transaction("narrowing"):
240 with repo.transaction("narrowing"):
241 for f in todelete:
241 for f in todelete:
242 ui.status(_('deleting %s\n') % f)
242 ui.status(_('deleting %s\n') % f)
243 util.unlinkpath(repo.svfs.join(f))
243 util.unlinkpath(repo.svfs.join(f))
244 repo.store.markremoved(f)
244 repo.store.markremoved(f)
245
245
246 _narrowcleanupwdir(repo, oldincludes, oldexcludes, newincludes,
246 _narrowcleanupwdir(repo, oldincludes, oldexcludes, newincludes,
247 newexcludes, oldmatch, newmatch)
247 newexcludes, oldmatch, newmatch)
248 repo.setnarrowpats(newincludes, newexcludes)
248 repo.setnarrowpats(newincludes, newexcludes)
249
249
250 repo.destroyed()
250 repo.destroyed()
251
251
252 def _widen(ui, repo, remote, commoninc, oldincludes, oldexcludes,
252 def _widen(ui, repo, remote, commoninc, oldincludes, oldexcludes,
253 newincludes, newexcludes):
253 newincludes, newexcludes):
254 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
254 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
255
255
256 # for now we assume that if a server has ellipses enabled, we will be
256 # for now we assume that if a server has ellipses enabled, we will be
257 # exchanging ellipses nodes. In future we should add ellipses as a client
257 # exchanging ellipses nodes. In future we should add ellipses as a client
258 # side requirement (maybe) to distinguish a client is shallow or not and
258 # side requirement (maybe) to distinguish a client is shallow or not and
259 # then send that information to server whether we want ellipses or not.
259 # then send that information to server whether we want ellipses or not.
260 # Theoretically a non-ellipses repo should be able to use narrow
260 # Theoretically a non-ellipses repo should be able to use narrow
261 # functionality from an ellipses enabled server
261 # functionality from an ellipses enabled server
262 ellipsesremote = wireprototypes.ELLIPSESCAP in remote.capabilities()
262 ellipsesremote = wireprototypes.ELLIPSESCAP in remote.capabilities()
263
263
264 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
264 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
265 orig(pullop, kwargs)
265 orig(pullop, kwargs)
266 # The old{in,ex}cludepats have already been set by orig()
266 # The old{in,ex}cludepats have already been set by orig()
267 kwargs['includepats'] = newincludes
267 kwargs['includepats'] = newincludes
268 kwargs['excludepats'] = newexcludes
268 kwargs['excludepats'] = newexcludes
269 wrappedextraprepare = extensions.wrappedfunction(exchange,
269 wrappedextraprepare = extensions.wrappedfunction(exchange,
270 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
270 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
271
271
272 # define a function that narrowbundle2 can call after creating the
272 # define a function that narrowbundle2 can call after creating the
273 # backup bundle, but before applying the bundle from the server
273 # backup bundle, but before applying the bundle from the server
274 def setnewnarrowpats():
274 def setnewnarrowpats():
275 repo.setnarrowpats(newincludes, newexcludes)
275 repo.setnarrowpats(newincludes, newexcludes)
276 repo.setnewnarrowpats = setnewnarrowpats
276 repo.setnewnarrowpats = setnewnarrowpats
277 # silence the devel-warning of applying an empty changegroup
277 # silence the devel-warning of applying an empty changegroup
278 overrides = {('devel', 'all-warnings'): False}
278 overrides = {('devel', 'all-warnings'): False}
279
279
280 with ui.uninterruptable():
280 with ui.uninterruptable():
281 common = commoninc[0]
281 common = commoninc[0]
282 if ellipsesremote:
282 if ellipsesremote:
283 ds = repo.dirstate
283 ds = repo.dirstate
284 p1, p2 = ds.p1(), ds.p2()
284 p1, p2 = ds.p1(), ds.p2()
285 with ds.parentchange():
285 with ds.parentchange():
286 ds.setparents(node.nullid, node.nullid)
286 ds.setparents(node.nullid, node.nullid)
287 with wrappedextraprepare,\
287 with wrappedextraprepare,\
288 repo.ui.configoverride(overrides, 'widen'):
288 repo.ui.configoverride(overrides, 'widen'):
289 exchange.pull(repo, remote, heads=common)
289 exchange.pull(repo, remote, heads=common)
290 with ds.parentchange():
290 with ds.parentchange():
291 ds.setparents(p1, p2)
291 ds.setparents(p1, p2)
292 else:
292 else:
293 with remote.commandexecutor() as e:
293 with remote.commandexecutor() as e:
294 bundle = e.callcommand('narrow_widen', {
294 bundle = e.callcommand('narrow_widen', {
295 'oldincludes': oldincludes,
295 'oldincludes': oldincludes,
296 'oldexcludes': oldexcludes,
296 'oldexcludes': oldexcludes,
297 'newincludes': newincludes,
297 'newincludes': newincludes,
298 'newexcludes': newexcludes,
298 'newexcludes': newexcludes,
299 'cgversion': '03',
299 'cgversion': '03',
300 'commonheads': common,
300 'commonheads': common,
301 'known': [],
301 'known': [],
302 'ellipses': False,
302 'ellipses': False,
303 }).result()
303 }).result()
304
304
305 with repo.transaction('widening') as tr,\
305 with repo.transaction('widening') as tr,\
306 repo.ui.configoverride(overrides, 'widen'):
306 repo.ui.configoverride(overrides, 'widen'):
307 tgetter = lambda: tr
307 tgetter = lambda: tr
308 bundle2.processbundle(repo, bundle,
308 bundle2.processbundle(repo, bundle,
309 transactiongetter=tgetter)
309 transactiongetter=tgetter)
310
310
311 repo.setnewnarrowpats()
311 repo.setnewnarrowpats()
312 actions = {k: [] for k in 'a am f g cd dc r dm dg m e k p pr'.split()}
312 actions = {k: [] for k in 'a am f g cd dc r dm dg m e k p pr'.split()}
313 addgaction = actions['g'].append
313 addgaction = actions['g'].append
314
314
315 mf = repo['.'].manifest().matches(newmatch)
315 mf = repo['.'].manifest().matches(newmatch)
316 for f, fn in mf.iteritems():
316 for f, fn in mf.iteritems():
317 if f not in repo.dirstate:
317 if f not in repo.dirstate:
318 addgaction((f, (mf.flags(f), False),
318 addgaction((f, (mf.flags(f), False),
319 "add from widened narrow clone"))
319 "add from widened narrow clone"))
320
320
321 merge.applyupdates(repo, actions, wctx=repo[None],
321 merge.applyupdates(repo, actions, wctx=repo[None],
322 mctx=repo['.'], overwrite=False)
322 mctx=repo['.'], overwrite=False)
323 merge.recordupdates(repo, actions, branchmerge=False)
323 merge.recordupdates(repo, actions, branchmerge=False)
324
324
325 # TODO(rdamazio): Make new matcher format and update description
325 # TODO(rdamazio): Make new matcher format and update description
326 @command('tracked',
326 @command('tracked',
327 [('', 'addinclude', [], _('new paths to include')),
327 [('', 'addinclude', [], _('new paths to include')),
328 ('', 'removeinclude', [], _('old paths to no longer include')),
328 ('', 'removeinclude', [], _('old paths to no longer include')),
329 ('', 'addexclude', [], _('new paths to exclude')),
329 ('', 'addexclude', [], _('new paths to exclude')),
330 ('', 'import-rules', '', _('import narrowspecs from a file')),
330 ('', 'import-rules', '', _('import narrowspecs from a file')),
331 ('', 'removeexclude', [], _('old paths to no longer exclude')),
331 ('', 'removeexclude', [], _('old paths to no longer exclude')),
332 ('', 'clear', False, _('whether to replace the existing narrowspec')),
332 ('', 'clear', False, _('whether to replace the existing narrowspec')),
333 ('', 'force-delete-local-changes', False,
333 ('', 'force-delete-local-changes', False,
334 _('forces deletion of local changes when narrowing')),
334 _('forces deletion of local changes when narrowing')),
335 ] + commands.remoteopts,
335 ] + commands.remoteopts,
336 _('[OPTIONS]... [REMOTE]'),
336 _('[OPTIONS]... [REMOTE]'),
337 inferrepo=True)
337 inferrepo=True)
338 def trackedcmd(ui, repo, remotepath=None, *pats, **opts):
338 def trackedcmd(ui, repo, remotepath=None, *pats, **opts):
339 """show or change the current narrowspec
339 """show or change the current narrowspec
340
340
341 With no argument, shows the current narrowspec entries, one per line. Each
341 With no argument, shows the current narrowspec entries, one per line. Each
342 line will be prefixed with 'I' or 'X' for included or excluded patterns,
342 line will be prefixed with 'I' or 'X' for included or excluded patterns,
343 respectively.
343 respectively.
344
344
345 The narrowspec is comprised of expressions to match remote files and/or
345 The narrowspec is comprised of expressions to match remote files and/or
346 directories that should be pulled into your client.
346 directories that should be pulled into your client.
347 The narrowspec has *include* and *exclude* expressions, with excludes always
347 The narrowspec has *include* and *exclude* expressions, with excludes always
348 trumping includes: that is, if a file matches an exclude expression, it will
348 trumping includes: that is, if a file matches an exclude expression, it will
349 be excluded even if it also matches an include expression.
349 be excluded even if it also matches an include expression.
350 Excluding files that were never included has no effect.
350 Excluding files that were never included has no effect.
351
351
352 Each included or excluded entry is in the format described by
352 Each included or excluded entry is in the format described by
353 'hg help patterns'.
353 'hg help patterns'.
354
354
355 The options allow you to add or remove included and excluded expressions.
355 The options allow you to add or remove included and excluded expressions.
356
356
357 If --clear is specified, then all previous includes and excludes are DROPPED
357 If --clear is specified, then all previous includes and excludes are DROPPED
358 and replaced by the new ones specified to --addinclude and --addexclude.
358 and replaced by the new ones specified to --addinclude and --addexclude.
359 If --clear is specified without any further options, the narrowspec will be
359 If --clear is specified without any further options, the narrowspec will be
360 empty and will not match any files.
360 empty and will not match any files.
361 """
361 """
362 opts = pycompat.byteskwargs(opts)
362 opts = pycompat.byteskwargs(opts)
363 if repository.NARROW_REQUIREMENT not in repo.requirements:
363 if repository.NARROW_REQUIREMENT not in repo.requirements:
364 ui.warn(_('The narrow command is only supported on respositories cloned'
364 ui.warn(_('The narrow command is only supported on respositories cloned'
365 ' with --narrow.\n'))
365 ' with --narrow.\n'))
366 return 1
366 return 1
367
367
368 # Before supporting, decide whether it "hg tracked --clear" should mean
368 # Before supporting, decide whether it "hg tracked --clear" should mean
369 # tracking no paths or all paths.
369 # tracking no paths or all paths.
370 if opts['clear']:
370 if opts['clear']:
371 ui.warn(_('The --clear option is not yet supported.\n'))
371 ui.warn(_('The --clear option is not yet supported.\n'))
372 return 1
372 return 1
373
373
374 # import rules from a file
374 # import rules from a file
375 newrules = opts.get('import_rules')
375 newrules = opts.get('import_rules')
376 if newrules:
376 if newrules:
377 try:
377 try:
378 filepath = os.path.join(encoding.getcwd(), newrules)
378 filepath = os.path.join(encoding.getcwd(), newrules)
379 fdata = util.readfile(filepath)
379 fdata = util.readfile(filepath)
380 except IOError as inst:
380 except IOError as inst:
381 raise error.Abort(_("cannot read narrowspecs from '%s': %s") %
381 raise error.Abort(_("cannot read narrowspecs from '%s': %s") %
382 (filepath, encoding.strtolocal(inst.strerror)))
382 (filepath, encoding.strtolocal(inst.strerror)))
383 includepats, excludepats, profiles = sparse.parseconfig(ui, fdata,
383 includepats, excludepats, profiles = sparse.parseconfig(ui, fdata,
384 'narrow')
384 'narrow')
385 if profiles:
385 if profiles:
386 raise error.Abort(_("including other spec files using '%include' "
386 raise error.Abort(_("including other spec files using '%include' "
387 "is not supported in narrowspec"))
387 "is not supported in narrowspec"))
388 opts['addinclude'].extend(includepats)
388 opts['addinclude'].extend(includepats)
389 opts['addexclude'].extend(excludepats)
389 opts['addexclude'].extend(excludepats)
390
390
391 addedincludes = narrowspec.parsepatterns(opts['addinclude'])
391 addedincludes = narrowspec.parsepatterns(opts['addinclude'])
392 removedincludes = narrowspec.parsepatterns(opts['removeinclude'])
392 removedincludes = narrowspec.parsepatterns(opts['removeinclude'])
393 addedexcludes = narrowspec.parsepatterns(opts['addexclude'])
393 addedexcludes = narrowspec.parsepatterns(opts['addexclude'])
394 removedexcludes = narrowspec.parsepatterns(opts['removeexclude'])
394 removedexcludes = narrowspec.parsepatterns(opts['removeexclude'])
395
395
396 only_show = not (addedincludes or removedincludes or addedexcludes or
396 only_show = not (addedincludes or removedincludes or addedexcludes or
397 removedexcludes or newrules)
397 removedexcludes or newrules)
398
398
399 oldincludes, oldexcludes = repo.narrowpats
399 oldincludes, oldexcludes = repo.narrowpats
400
400
401 # filter the user passed additions and deletions into actual additions and
401 # filter the user passed additions and deletions into actual additions and
402 # deletions of excludes and includes
402 # deletions of excludes and includes
403 addedincludes = set([i for i in addedincludes if i not in oldincludes])
403 addedincludes -= oldincludes
404 removedincludes = set([i for i in removedincludes if i in oldincludes])
404 removedincludes &= oldincludes
405 addedexcludes = set([i for i in addedexcludes if i not in oldexcludes])
405 addedexcludes -= oldexcludes
406 removedexcludes = set([i for i in removedexcludes if i in oldexcludes])
406 removedexcludes &= oldexcludes
407
407
408 widening = addedincludes or removedexcludes
408 widening = addedincludes or removedexcludes
409 narrowing = removedincludes or addedexcludes
409 narrowing = removedincludes or addedexcludes
410
410
411 # Only print the current narrowspec.
411 # Only print the current narrowspec.
412 if only_show:
412 if only_show:
413 include, exclude = repo.narrowpats
413 include, exclude = repo.narrowpats
414
414
415 ui.pager('tracked')
415 ui.pager('tracked')
416 fm = ui.formatter('narrow', opts)
416 fm = ui.formatter('narrow', opts)
417 for i in sorted(include):
417 for i in sorted(include):
418 fm.startitem()
418 fm.startitem()
419 fm.write('status', '%s ', 'I', label='narrow.included')
419 fm.write('status', '%s ', 'I', label='narrow.included')
420 fm.write('pat', '%s\n', i, label='narrow.included')
420 fm.write('pat', '%s\n', i, label='narrow.included')
421 for i in sorted(exclude):
421 for i in sorted(exclude):
422 fm.startitem()
422 fm.startitem()
423 fm.write('status', '%s ', 'X', label='narrow.excluded')
423 fm.write('status', '%s ', 'X', label='narrow.excluded')
424 fm.write('pat', '%s\n', i, label='narrow.excluded')
424 fm.write('pat', '%s\n', i, label='narrow.excluded')
425 fm.end()
425 fm.end()
426 return 0
426 return 0
427
427
428 if not widening and not narrowing:
428 if not widening and not narrowing:
429 ui.status(_("nothing to widen or narrow\n"))
429 ui.status(_("nothing to widen or narrow\n"))
430 return 0
430 return 0
431
431
432 with repo.wlock(), repo.lock():
432 with repo.wlock(), repo.lock():
433 cmdutil.bailifchanged(repo)
433 cmdutil.bailifchanged(repo)
434
434
435 # Find the revisions we have in common with the remote. These will
435 # Find the revisions we have in common with the remote. These will
436 # be used for finding local-only changes for narrowing. They will
436 # be used for finding local-only changes for narrowing. They will
437 # also define the set of revisions to update for widening.
437 # also define the set of revisions to update for widening.
438 remotepath = ui.expandpath(remotepath or 'default')
438 remotepath = ui.expandpath(remotepath or 'default')
439 url, branches = hg.parseurl(remotepath)
439 url, branches = hg.parseurl(remotepath)
440 ui.status(_('comparing with %s\n') % util.hidepassword(url))
440 ui.status(_('comparing with %s\n') % util.hidepassword(url))
441 remote = hg.peer(repo, opts, url)
441 remote = hg.peer(repo, opts, url)
442
442
443 # check narrow support before doing anything if widening needs to be
443 # check narrow support before doing anything if widening needs to be
444 # performed. In future we should also abort if client is ellipses and
444 # performed. In future we should also abort if client is ellipses and
445 # server does not support ellipses
445 # server does not support ellipses
446 if widening and wireprototypes.NARROWCAP not in remote.capabilities():
446 if widening and wireprototypes.NARROWCAP not in remote.capabilities():
447 raise error.Abort(_("server does not support narrow clones"))
447 raise error.Abort(_("server does not support narrow clones"))
448
448
449 commoninc = discovery.findcommonincoming(repo, remote)
449 commoninc = discovery.findcommonincoming(repo, remote)
450
450
451 if narrowing:
451 if narrowing:
452 newincludes = oldincludes - removedincludes
452 newincludes = oldincludes - removedincludes
453 newexcludes = oldexcludes | addedexcludes
453 newexcludes = oldexcludes | addedexcludes
454 _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
454 _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
455 newincludes, newexcludes,
455 newincludes, newexcludes,
456 opts['force_delete_local_changes'])
456 opts['force_delete_local_changes'])
457 # _narrow() updated the narrowspec and _widen() below needs to
457 # _narrow() updated the narrowspec and _widen() below needs to
458 # use the updated values as its base (otherwise removed includes
458 # use the updated values as its base (otherwise removed includes
459 # and addedexcludes will be lost in the resulting narrowspec)
459 # and addedexcludes will be lost in the resulting narrowspec)
460 oldincludes = newincludes
460 oldincludes = newincludes
461 oldexcludes = newexcludes
461 oldexcludes = newexcludes
462
462
463 if widening:
463 if widening:
464 newincludes = oldincludes | addedincludes
464 newincludes = oldincludes | addedincludes
465 newexcludes = oldexcludes - removedexcludes
465 newexcludes = oldexcludes - removedexcludes
466 _widen(ui, repo, remote, commoninc, oldincludes, oldexcludes,
466 _widen(ui, repo, remote, commoninc, oldincludes, oldexcludes,
467 newincludes, newexcludes)
467 newincludes, newexcludes)
468
468
469 return 0
469 return 0
General Comments 0
You need to be logged in to leave comments. Login now