##// END OF EJS Templates
narrow: only send includepats and excludepats if they are not empty...
Pulkit Goyal -
r40528:a2c4502e default
parent child Browse files
Show More
@@ -1,469 +1,471 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 if include:
145 kwargs['excludepats'] = exclude
145 kwargs['includepats'] = include
146 if exclude:
147 kwargs['excludepats'] = exclude
146 # calculate known nodes only in ellipses cases because in non-ellipses cases
148 # calculate known nodes only in ellipses cases because in non-ellipses cases
147 # we have all the nodes
149 # we have all the nodes
148 if wireprototypes.ELLIPSESCAP in pullop.remote.capabilities():
150 if wireprototypes.ELLIPSESCAP in pullop.remote.capabilities():
149 kwargs['known'] = [node.hex(ctx.node()) for ctx in
151 kwargs['known'] = [node.hex(ctx.node()) for ctx in
150 repo.set('::%ln', pullop.common)
152 repo.set('::%ln', pullop.common)
151 if ctx.node() != node.nullid]
153 if ctx.node() != node.nullid]
152 if not kwargs['known']:
154 if not kwargs['known']:
153 # Mercurial serializes an empty list as '' and deserializes it as
155 # Mercurial serializes an empty list as '' and deserializes it as
154 # [''], so delete it instead to avoid handling the empty string on
156 # [''], so delete it instead to avoid handling the empty string on
155 # the server.
157 # the server.
156 del kwargs['known']
158 del kwargs['known']
157
159
158 extensions.wrapfunction(exchange,'_pullbundle2extraprepare',
160 extensions.wrapfunction(exchange,'_pullbundle2extraprepare',
159 pullbundle2extraprepare)
161 pullbundle2extraprepare)
160
162
161 # This is an extension point for filesystems that need to do something other
163 # 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
164 # 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.
165 # useful, so we're passing in a fair number of them, some of them redundant.
164 def _narrowcleanupwdir(repo, oldincludes, oldexcludes, newincludes, newexcludes,
166 def _narrowcleanupwdir(repo, oldincludes, oldexcludes, newincludes, newexcludes,
165 oldmatch, newmatch):
167 oldmatch, newmatch):
166 for f in repo.dirstate:
168 for f in repo.dirstate:
167 if not newmatch(f):
169 if not newmatch(f):
168 repo.dirstate.drop(f)
170 repo.dirstate.drop(f)
169 repo.wvfs.unlinkpath(f)
171 repo.wvfs.unlinkpath(f)
170
172
171 def _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
173 def _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
172 newincludes, newexcludes, force):
174 newincludes, newexcludes, force):
173 oldmatch = narrowspec.match(repo.root, oldincludes, oldexcludes)
175 oldmatch = narrowspec.match(repo.root, oldincludes, oldexcludes)
174 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
176 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
175
177
176 # This is essentially doing "hg outgoing" to find all local-only
178 # This is essentially doing "hg outgoing" to find all local-only
177 # commits. We will then check that the local-only commits don't
179 # commits. We will then check that the local-only commits don't
178 # have any changes to files that will be untracked.
180 # have any changes to files that will be untracked.
179 unfi = repo.unfiltered()
181 unfi = repo.unfiltered()
180 outgoing = discovery.findcommonoutgoing(unfi, remote,
182 outgoing = discovery.findcommonoutgoing(unfi, remote,
181 commoninc=commoninc)
183 commoninc=commoninc)
182 ui.status(_('looking for local changes to affected paths\n'))
184 ui.status(_('looking for local changes to affected paths\n'))
183 localnodes = []
185 localnodes = []
184 for n in itertools.chain(outgoing.missing, outgoing.excluded):
186 for n in itertools.chain(outgoing.missing, outgoing.excluded):
185 if any(oldmatch(f) and not newmatch(f) for f in unfi[n].files()):
187 if any(oldmatch(f) and not newmatch(f) for f in unfi[n].files()):
186 localnodes.append(n)
188 localnodes.append(n)
187 revstostrip = unfi.revs('descendants(%ln)', localnodes)
189 revstostrip = unfi.revs('descendants(%ln)', localnodes)
188 hiddenrevs = repoview.filterrevs(repo, 'visible')
190 hiddenrevs = repoview.filterrevs(repo, 'visible')
189 visibletostrip = list(repo.changelog.node(r)
191 visibletostrip = list(repo.changelog.node(r)
190 for r in (revstostrip - hiddenrevs))
192 for r in (revstostrip - hiddenrevs))
191 if visibletostrip:
193 if visibletostrip:
192 ui.status(_('The following changeset(s) or their ancestors have '
194 ui.status(_('The following changeset(s) or their ancestors have '
193 'local changes not on the remote:\n'))
195 'local changes not on the remote:\n'))
194 maxnodes = 10
196 maxnodes = 10
195 if ui.verbose or len(visibletostrip) <= maxnodes:
197 if ui.verbose or len(visibletostrip) <= maxnodes:
196 for n in visibletostrip:
198 for n in visibletostrip:
197 ui.status('%s\n' % node.short(n))
199 ui.status('%s\n' % node.short(n))
198 else:
200 else:
199 for n in visibletostrip[:maxnodes]:
201 for n in visibletostrip[:maxnodes]:
200 ui.status('%s\n' % node.short(n))
202 ui.status('%s\n' % node.short(n))
201 ui.status(_('...and %d more, use --verbose to list all\n') %
203 ui.status(_('...and %d more, use --verbose to list all\n') %
202 (len(visibletostrip) - maxnodes))
204 (len(visibletostrip) - maxnodes))
203 if not force:
205 if not force:
204 raise error.Abort(_('local changes found'),
206 raise error.Abort(_('local changes found'),
205 hint=_('use --force-delete-local-changes to '
207 hint=_('use --force-delete-local-changes to '
206 'ignore'))
208 'ignore'))
207
209
208 with ui.uninterruptable():
210 with ui.uninterruptable():
209 if revstostrip:
211 if revstostrip:
210 tostrip = [unfi.changelog.node(r) for r in revstostrip]
212 tostrip = [unfi.changelog.node(r) for r in revstostrip]
211 if repo['.'].node() in tostrip:
213 if repo['.'].node() in tostrip:
212 # stripping working copy, so move to a different commit first
214 # stripping working copy, so move to a different commit first
213 urev = max(repo.revs('(::%n) - %ln + null',
215 urev = max(repo.revs('(::%n) - %ln + null',
214 repo['.'].node(), visibletostrip))
216 repo['.'].node(), visibletostrip))
215 hg.clean(repo, urev)
217 hg.clean(repo, urev)
216 repair.strip(ui, unfi, tostrip, topic='narrow')
218 repair.strip(ui, unfi, tostrip, topic='narrow')
217
219
218 todelete = []
220 todelete = []
219 for f, f2, size in repo.store.datafiles():
221 for f, f2, size in repo.store.datafiles():
220 if f.startswith('data/'):
222 if f.startswith('data/'):
221 file = f[5:-2]
223 file = f[5:-2]
222 if not newmatch(file):
224 if not newmatch(file):
223 todelete.append(f)
225 todelete.append(f)
224 elif f.startswith('meta/'):
226 elif f.startswith('meta/'):
225 dir = f[5:-13]
227 dir = f[5:-13]
226 dirs = ['.'] + sorted(util.dirs({dir})) + [dir]
228 dirs = ['.'] + sorted(util.dirs({dir})) + [dir]
227 include = True
229 include = True
228 for d in dirs:
230 for d in dirs:
229 visit = newmatch.visitdir(d)
231 visit = newmatch.visitdir(d)
230 if not visit:
232 if not visit:
231 include = False
233 include = False
232 break
234 break
233 if visit == 'all':
235 if visit == 'all':
234 break
236 break
235 if not include:
237 if not include:
236 todelete.append(f)
238 todelete.append(f)
237
239
238 repo.destroying()
240 repo.destroying()
239
241
240 with repo.transaction("narrowing"):
242 with repo.transaction("narrowing"):
241 for f in todelete:
243 for f in todelete:
242 ui.status(_('deleting %s\n') % f)
244 ui.status(_('deleting %s\n') % f)
243 util.unlinkpath(repo.svfs.join(f))
245 util.unlinkpath(repo.svfs.join(f))
244 repo.store.markremoved(f)
246 repo.store.markremoved(f)
245
247
246 _narrowcleanupwdir(repo, oldincludes, oldexcludes, newincludes,
248 _narrowcleanupwdir(repo, oldincludes, oldexcludes, newincludes,
247 newexcludes, oldmatch, newmatch)
249 newexcludes, oldmatch, newmatch)
248 repo.setnarrowpats(newincludes, newexcludes)
250 repo.setnarrowpats(newincludes, newexcludes)
249
251
250 repo.destroyed()
252 repo.destroyed()
251
253
252 def _widen(ui, repo, remote, commoninc, oldincludes, oldexcludes,
254 def _widen(ui, repo, remote, commoninc, oldincludes, oldexcludes,
253 newincludes, newexcludes):
255 newincludes, newexcludes):
254 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
256 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
255
257
256 # for now we assume that if a server has ellipses enabled, we will be
258 # 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
259 # 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
260 # 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.
261 # then send that information to server whether we want ellipses or not.
260 # Theoretically a non-ellipses repo should be able to use narrow
262 # Theoretically a non-ellipses repo should be able to use narrow
261 # functionality from an ellipses enabled server
263 # functionality from an ellipses enabled server
262 ellipsesremote = wireprototypes.ELLIPSESCAP in remote.capabilities()
264 ellipsesremote = wireprototypes.ELLIPSESCAP in remote.capabilities()
263
265
264 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
266 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
265 orig(pullop, kwargs)
267 orig(pullop, kwargs)
266 # The old{in,ex}cludepats have already been set by orig()
268 # The old{in,ex}cludepats have already been set by orig()
267 kwargs['includepats'] = newincludes
269 kwargs['includepats'] = newincludes
268 kwargs['excludepats'] = newexcludes
270 kwargs['excludepats'] = newexcludes
269 wrappedextraprepare = extensions.wrappedfunction(exchange,
271 wrappedextraprepare = extensions.wrappedfunction(exchange,
270 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
272 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
271
273
272 # define a function that narrowbundle2 can call after creating the
274 # define a function that narrowbundle2 can call after creating the
273 # backup bundle, but before applying the bundle from the server
275 # backup bundle, but before applying the bundle from the server
274 def setnewnarrowpats():
276 def setnewnarrowpats():
275 repo.setnarrowpats(newincludes, newexcludes)
277 repo.setnarrowpats(newincludes, newexcludes)
276 repo.setnewnarrowpats = setnewnarrowpats
278 repo.setnewnarrowpats = setnewnarrowpats
277 # silence the devel-warning of applying an empty changegroup
279 # silence the devel-warning of applying an empty changegroup
278 overrides = {('devel', 'all-warnings'): False}
280 overrides = {('devel', 'all-warnings'): False}
279
281
280 with ui.uninterruptable():
282 with ui.uninterruptable():
281 common = commoninc[0]
283 common = commoninc[0]
282 if ellipsesremote:
284 if ellipsesremote:
283 ds = repo.dirstate
285 ds = repo.dirstate
284 p1, p2 = ds.p1(), ds.p2()
286 p1, p2 = ds.p1(), ds.p2()
285 with ds.parentchange():
287 with ds.parentchange():
286 ds.setparents(node.nullid, node.nullid)
288 ds.setparents(node.nullid, node.nullid)
287 with wrappedextraprepare,\
289 with wrappedextraprepare,\
288 repo.ui.configoverride(overrides, 'widen'):
290 repo.ui.configoverride(overrides, 'widen'):
289 exchange.pull(repo, remote, heads=common)
291 exchange.pull(repo, remote, heads=common)
290 with ds.parentchange():
292 with ds.parentchange():
291 ds.setparents(p1, p2)
293 ds.setparents(p1, p2)
292 else:
294 else:
293 with remote.commandexecutor() as e:
295 with remote.commandexecutor() as e:
294 bundle = e.callcommand('narrow_widen', {
296 bundle = e.callcommand('narrow_widen', {
295 'oldincludes': oldincludes,
297 'oldincludes': oldincludes,
296 'oldexcludes': oldexcludes,
298 'oldexcludes': oldexcludes,
297 'newincludes': newincludes,
299 'newincludes': newincludes,
298 'newexcludes': newexcludes,
300 'newexcludes': newexcludes,
299 'cgversion': '03',
301 'cgversion': '03',
300 'commonheads': common,
302 'commonheads': common,
301 'known': [],
303 'known': [],
302 'ellipses': False,
304 'ellipses': False,
303 }).result()
305 }).result()
304
306
305 with repo.transaction('widening') as tr,\
307 with repo.transaction('widening') as tr,\
306 repo.ui.configoverride(overrides, 'widen'):
308 repo.ui.configoverride(overrides, 'widen'):
307 tgetter = lambda: tr
309 tgetter = lambda: tr
308 bundle2.processbundle(repo, bundle,
310 bundle2.processbundle(repo, bundle,
309 transactiongetter=tgetter)
311 transactiongetter=tgetter)
310
312
311 repo.setnewnarrowpats()
313 repo.setnewnarrowpats()
312 actions = {k: [] for k in 'a am f g cd dc r dm dg m e k p pr'.split()}
314 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
315 addgaction = actions['g'].append
314
316
315 mf = repo['.'].manifest().matches(newmatch)
317 mf = repo['.'].manifest().matches(newmatch)
316 for f, fn in mf.iteritems():
318 for f, fn in mf.iteritems():
317 if f not in repo.dirstate:
319 if f not in repo.dirstate:
318 addgaction((f, (mf.flags(f), False),
320 addgaction((f, (mf.flags(f), False),
319 "add from widened narrow clone"))
321 "add from widened narrow clone"))
320
322
321 merge.applyupdates(repo, actions, wctx=repo[None],
323 merge.applyupdates(repo, actions, wctx=repo[None],
322 mctx=repo['.'], overwrite=False)
324 mctx=repo['.'], overwrite=False)
323 merge.recordupdates(repo, actions, branchmerge=False)
325 merge.recordupdates(repo, actions, branchmerge=False)
324
326
325 # TODO(rdamazio): Make new matcher format and update description
327 # TODO(rdamazio): Make new matcher format and update description
326 @command('tracked',
328 @command('tracked',
327 [('', 'addinclude', [], _('new paths to include')),
329 [('', 'addinclude', [], _('new paths to include')),
328 ('', 'removeinclude', [], _('old paths to no longer include')),
330 ('', 'removeinclude', [], _('old paths to no longer include')),
329 ('', 'addexclude', [], _('new paths to exclude')),
331 ('', 'addexclude', [], _('new paths to exclude')),
330 ('', 'import-rules', '', _('import narrowspecs from a file')),
332 ('', 'import-rules', '', _('import narrowspecs from a file')),
331 ('', 'removeexclude', [], _('old paths to no longer exclude')),
333 ('', 'removeexclude', [], _('old paths to no longer exclude')),
332 ('', 'clear', False, _('whether to replace the existing narrowspec')),
334 ('', 'clear', False, _('whether to replace the existing narrowspec')),
333 ('', 'force-delete-local-changes', False,
335 ('', 'force-delete-local-changes', False,
334 _('forces deletion of local changes when narrowing')),
336 _('forces deletion of local changes when narrowing')),
335 ] + commands.remoteopts,
337 ] + commands.remoteopts,
336 _('[OPTIONS]... [REMOTE]'),
338 _('[OPTIONS]... [REMOTE]'),
337 inferrepo=True)
339 inferrepo=True)
338 def trackedcmd(ui, repo, remotepath=None, *pats, **opts):
340 def trackedcmd(ui, repo, remotepath=None, *pats, **opts):
339 """show or change the current narrowspec
341 """show or change the current narrowspec
340
342
341 With no argument, shows the current narrowspec entries, one per line. Each
343 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,
344 line will be prefixed with 'I' or 'X' for included or excluded patterns,
343 respectively.
345 respectively.
344
346
345 The narrowspec is comprised of expressions to match remote files and/or
347 The narrowspec is comprised of expressions to match remote files and/or
346 directories that should be pulled into your client.
348 directories that should be pulled into your client.
347 The narrowspec has *include* and *exclude* expressions, with excludes always
349 The narrowspec has *include* and *exclude* expressions, with excludes always
348 trumping includes: that is, if a file matches an exclude expression, it will
350 trumping includes: that is, if a file matches an exclude expression, it will
349 be excluded even if it also matches an include expression.
351 be excluded even if it also matches an include expression.
350 Excluding files that were never included has no effect.
352 Excluding files that were never included has no effect.
351
353
352 Each included or excluded entry is in the format described by
354 Each included or excluded entry is in the format described by
353 'hg help patterns'.
355 'hg help patterns'.
354
356
355 The options allow you to add or remove included and excluded expressions.
357 The options allow you to add or remove included and excluded expressions.
356
358
357 If --clear is specified, then all previous includes and excludes are DROPPED
359 If --clear is specified, then all previous includes and excludes are DROPPED
358 and replaced by the new ones specified to --addinclude and --addexclude.
360 and replaced by the new ones specified to --addinclude and --addexclude.
359 If --clear is specified without any further options, the narrowspec will be
361 If --clear is specified without any further options, the narrowspec will be
360 empty and will not match any files.
362 empty and will not match any files.
361 """
363 """
362 opts = pycompat.byteskwargs(opts)
364 opts = pycompat.byteskwargs(opts)
363 if repository.NARROW_REQUIREMENT not in repo.requirements:
365 if repository.NARROW_REQUIREMENT not in repo.requirements:
364 ui.warn(_('The narrow command is only supported on respositories cloned'
366 ui.warn(_('The narrow command is only supported on respositories cloned'
365 ' with --narrow.\n'))
367 ' with --narrow.\n'))
366 return 1
368 return 1
367
369
368 # Before supporting, decide whether it "hg tracked --clear" should mean
370 # Before supporting, decide whether it "hg tracked --clear" should mean
369 # tracking no paths or all paths.
371 # tracking no paths or all paths.
370 if opts['clear']:
372 if opts['clear']:
371 ui.warn(_('The --clear option is not yet supported.\n'))
373 ui.warn(_('The --clear option is not yet supported.\n'))
372 return 1
374 return 1
373
375
374 # import rules from a file
376 # import rules from a file
375 newrules = opts.get('import_rules')
377 newrules = opts.get('import_rules')
376 if newrules:
378 if newrules:
377 try:
379 try:
378 filepath = os.path.join(encoding.getcwd(), newrules)
380 filepath = os.path.join(encoding.getcwd(), newrules)
379 fdata = util.readfile(filepath)
381 fdata = util.readfile(filepath)
380 except IOError as inst:
382 except IOError as inst:
381 raise error.Abort(_("cannot read narrowspecs from '%s': %s") %
383 raise error.Abort(_("cannot read narrowspecs from '%s': %s") %
382 (filepath, encoding.strtolocal(inst.strerror)))
384 (filepath, encoding.strtolocal(inst.strerror)))
383 includepats, excludepats, profiles = sparse.parseconfig(ui, fdata,
385 includepats, excludepats, profiles = sparse.parseconfig(ui, fdata,
384 'narrow')
386 'narrow')
385 if profiles:
387 if profiles:
386 raise error.Abort(_("including other spec files using '%include' "
388 raise error.Abort(_("including other spec files using '%include' "
387 "is not supported in narrowspec"))
389 "is not supported in narrowspec"))
388 opts['addinclude'].extend(includepats)
390 opts['addinclude'].extend(includepats)
389 opts['addexclude'].extend(excludepats)
391 opts['addexclude'].extend(excludepats)
390
392
391 addedincludes = narrowspec.parsepatterns(opts['addinclude'])
393 addedincludes = narrowspec.parsepatterns(opts['addinclude'])
392 removedincludes = narrowspec.parsepatterns(opts['removeinclude'])
394 removedincludes = narrowspec.parsepatterns(opts['removeinclude'])
393 addedexcludes = narrowspec.parsepatterns(opts['addexclude'])
395 addedexcludes = narrowspec.parsepatterns(opts['addexclude'])
394 removedexcludes = narrowspec.parsepatterns(opts['removeexclude'])
396 removedexcludes = narrowspec.parsepatterns(opts['removeexclude'])
395
397
396 only_show = not (addedincludes or removedincludes or addedexcludes or
398 only_show = not (addedincludes or removedincludes or addedexcludes or
397 removedexcludes or newrules)
399 removedexcludes or newrules)
398
400
399 oldincludes, oldexcludes = repo.narrowpats
401 oldincludes, oldexcludes = repo.narrowpats
400
402
401 # filter the user passed additions and deletions into actual additions and
403 # filter the user passed additions and deletions into actual additions and
402 # deletions of excludes and includes
404 # deletions of excludes and includes
403 addedincludes -= oldincludes
405 addedincludes -= oldincludes
404 removedincludes &= oldincludes
406 removedincludes &= oldincludes
405 addedexcludes -= oldexcludes
407 addedexcludes -= oldexcludes
406 removedexcludes &= oldexcludes
408 removedexcludes &= oldexcludes
407
409
408 widening = addedincludes or removedexcludes
410 widening = addedincludes or removedexcludes
409 narrowing = removedincludes or addedexcludes
411 narrowing = removedincludes or addedexcludes
410
412
411 # Only print the current narrowspec.
413 # Only print the current narrowspec.
412 if only_show:
414 if only_show:
413 include, exclude = repo.narrowpats
415 include, exclude = repo.narrowpats
414
416
415 ui.pager('tracked')
417 ui.pager('tracked')
416 fm = ui.formatter('narrow', opts)
418 fm = ui.formatter('narrow', opts)
417 for i in sorted(include):
419 for i in sorted(include):
418 fm.startitem()
420 fm.startitem()
419 fm.write('status', '%s ', 'I', label='narrow.included')
421 fm.write('status', '%s ', 'I', label='narrow.included')
420 fm.write('pat', '%s\n', i, label='narrow.included')
422 fm.write('pat', '%s\n', i, label='narrow.included')
421 for i in sorted(exclude):
423 for i in sorted(exclude):
422 fm.startitem()
424 fm.startitem()
423 fm.write('status', '%s ', 'X', label='narrow.excluded')
425 fm.write('status', '%s ', 'X', label='narrow.excluded')
424 fm.write('pat', '%s\n', i, label='narrow.excluded')
426 fm.write('pat', '%s\n', i, label='narrow.excluded')
425 fm.end()
427 fm.end()
426 return 0
428 return 0
427
429
428 if not widening and not narrowing:
430 if not widening and not narrowing:
429 ui.status(_("nothing to widen or narrow\n"))
431 ui.status(_("nothing to widen or narrow\n"))
430 return 0
432 return 0
431
433
432 with repo.wlock(), repo.lock():
434 with repo.wlock(), repo.lock():
433 cmdutil.bailifchanged(repo)
435 cmdutil.bailifchanged(repo)
434
436
435 # Find the revisions we have in common with the remote. These will
437 # Find the revisions we have in common with the remote. These will
436 # be used for finding local-only changes for narrowing. They will
438 # be used for finding local-only changes for narrowing. They will
437 # also define the set of revisions to update for widening.
439 # also define the set of revisions to update for widening.
438 remotepath = ui.expandpath(remotepath or 'default')
440 remotepath = ui.expandpath(remotepath or 'default')
439 url, branches = hg.parseurl(remotepath)
441 url, branches = hg.parseurl(remotepath)
440 ui.status(_('comparing with %s\n') % util.hidepassword(url))
442 ui.status(_('comparing with %s\n') % util.hidepassword(url))
441 remote = hg.peer(repo, opts, url)
443 remote = hg.peer(repo, opts, url)
442
444
443 # check narrow support before doing anything if widening needs to be
445 # check narrow support before doing anything if widening needs to be
444 # performed. In future we should also abort if client is ellipses and
446 # performed. In future we should also abort if client is ellipses and
445 # server does not support ellipses
447 # server does not support ellipses
446 if widening and wireprototypes.NARROWCAP not in remote.capabilities():
448 if widening and wireprototypes.NARROWCAP not in remote.capabilities():
447 raise error.Abort(_("server does not support narrow clones"))
449 raise error.Abort(_("server does not support narrow clones"))
448
450
449 commoninc = discovery.findcommonincoming(repo, remote)
451 commoninc = discovery.findcommonincoming(repo, remote)
450
452
451 if narrowing:
453 if narrowing:
452 newincludes = oldincludes - removedincludes
454 newincludes = oldincludes - removedincludes
453 newexcludes = oldexcludes | addedexcludes
455 newexcludes = oldexcludes | addedexcludes
454 _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
456 _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
455 newincludes, newexcludes,
457 newincludes, newexcludes,
456 opts['force_delete_local_changes'])
458 opts['force_delete_local_changes'])
457 # _narrow() updated the narrowspec and _widen() below needs to
459 # _narrow() updated the narrowspec and _widen() below needs to
458 # use the updated values as its base (otherwise removed includes
460 # use the updated values as its base (otherwise removed includes
459 # and addedexcludes will be lost in the resulting narrowspec)
461 # and addedexcludes will be lost in the resulting narrowspec)
460 oldincludes = newincludes
462 oldincludes = newincludes
461 oldexcludes = newexcludes
463 oldexcludes = newexcludes
462
464
463 if widening:
465 if widening:
464 newincludes = oldincludes | addedincludes
466 newincludes = oldincludes | addedincludes
465 newexcludes = oldexcludes - removedexcludes
467 newexcludes = oldexcludes - removedexcludes
466 _widen(ui, repo, remote, commoninc, oldincludes, oldexcludes,
468 _widen(ui, repo, remote, commoninc, oldincludes, oldexcludes,
467 newincludes, newexcludes)
469 newincludes, newexcludes)
468
470
469 return 0
471 return 0
General Comments 0
You need to be logged in to leave comments. Login now