##// END OF EJS Templates
narrow: add '--import-rules' flag to tracked command...
Pulkit Goyal -
r39471:ab20ee07 default
parent child Browse files
Show More
@@ -0,0 +1,218 b''
1 #testcases flat tree
2 $ . "$TESTDIR/narrow-library.sh"
3
4 #if tree
5 $ cat << EOF >> $HGRCPATH
6 > [experimental]
7 > treemanifest = 1
8 > EOF
9 #endif
10
11 $ hg init master
12 $ cd master
13 $ cat >> .hg/hgrc <<EOF
14 > [narrow]
15 > serveellipses=True
16 > EOF
17
18 $ mkdir inside
19 $ echo 'inside' > inside/f
20 $ hg add inside/f
21 $ hg commit -m 'add inside'
22
23 $ mkdir widest
24 $ echo 'widest' > widest/f
25 $ hg add widest/f
26 $ hg commit -m 'add widest'
27
28 $ mkdir outside
29 $ echo 'outside' > outside/f
30 $ hg add outside/f
31 $ hg commit -m 'add outside'
32
33 $ cd ..
34
35 narrow clone the inside file
36
37 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
38 requesting all changes
39 adding changesets
40 adding manifests
41 adding file changes
42 added 2 changesets with 1 changes to 1 files
43 new changesets *:* (glob)
44 updating to branch default
45 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 $ cd narrow
47 $ hg tracked
48 I path:inside
49 $ ls
50 inside
51 $ cat inside/f
52 inside
53 $ cd ..
54
55 add more upstream files which we will include in a wider narrow spec
56
57 $ cd master
58
59 $ mkdir wider
60 $ echo 'wider' > wider/f
61 $ hg add wider/f
62 $ echo 'widest v2' > widest/f
63 $ hg commit -m 'add wider, update widest'
64
65 $ echo 'widest v3' > widest/f
66 $ hg commit -m 'update widest v3'
67
68 $ echo 'inside v2' > inside/f
69 $ hg commit -m 'update inside'
70
71 $ mkdir outside2
72 $ echo 'outside2' > outside2/f
73 $ hg add outside2/f
74 $ hg commit -m 'add outside2'
75
76 $ echo 'widest v4' > widest/f
77 $ hg commit -m 'update widest v4'
78
79 $ hg log -T "{if(ellipsis, '...')}{rev}: {desc}\n"
80 7: update widest v4
81 6: add outside2
82 5: update inside
83 4: update widest v3
84 3: add wider, update widest
85 2: add outside
86 1: add widest
87 0: add inside
88
89 $ cd ..
90
91 Testing the --import-rules flag of `hg tracked` command
92
93 $ cd narrow
94 $ hg tracked --import-rules
95 hg tracked: option --import-rules requires argument
96 hg tracked [OPTIONS]... [REMOTE]
97
98 show or change the current narrowspec
99
100 options ([+] can be repeated):
101
102 --addinclude VALUE [+] new paths to include
103 --removeinclude VALUE [+] old paths to no longer include
104 --addexclude VALUE [+] new paths to exclude
105 --import-rules VALUE import narrowspecs from a file
106 --removeexclude VALUE [+] old paths to no longer exclude
107 --clear whether to replace the existing narrowspec
108 --force-delete-local-changes forces deletion of local changes when
109 narrowing
110 -e --ssh CMD specify ssh command to use
111 --remotecmd CMD specify hg command to run on the remote side
112 --insecure do not verify server certificate (ignoring
113 web.cacerts config)
114
115 (use 'hg tracked -h' to show more help)
116 [255]
117 $ hg tracked --import-rules doesnotexist
118 abort: cannot read narrowspecs from '$TESTTMP/narrow/doesnotexist': $ENOENT$
119 [255]
120
121 $ cat > specs <<EOF
122 > %include foo
123 > [include]
124 > path:widest/
125 > [exclude]
126 > path:inside/
127 > EOF
128
129 $ hg tracked --import-rules specs
130 abort: including other spec files using '%include' is not supported in narrowspec
131 [255]
132
133 $ cat > specs <<EOF
134 > [include]
135 > outisde
136 > [exclude]
137 > inside
138 > EOF
139
140 $ hg tracked --import-rules specs
141 comparing with ssh://user@dummy/master
142 searching for changes
143 looking for local changes to affected paths
144 deleting data/inside/f.i
145 deleting meta/inside/00manifest.i (tree !)
146 no changes found
147 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-widen.hg (glob)
148 adding changesets
149 adding manifests
150 adding file changes
151 added 2 changesets with 0 changes to 0 files
152 new changesets *:* (glob)
153 $ hg tracked
154 I path:outisde
155 X path:inside
156
157 Testing the --import-rules flag with --addinclude and --addexclude
158
159 $ cat > specs <<EOF
160 > [include]
161 > widest
162 > EOF
163
164 $ hg tracked --import-rules specs --addinclude 'wider/'
165 comparing with ssh://user@dummy/master
166 searching for changes
167 no changes found
168 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-widen.hg (glob)
169 adding changesets
170 adding manifests
171 adding file changes
172 added 3 changesets with 1 changes to 1 files
173 new changesets *:* (glob)
174 $ hg tracked
175 I path:outisde
176 I path:wider
177 I path:widest
178 X path:inside
179
180 $ cat > specs <<EOF
181 > [exclude]
182 > outside2
183 > EOF
184
185 $ hg tracked --import-rules specs --addexclude 'widest'
186 comparing with ssh://user@dummy/master
187 searching for changes
188 looking for local changes to affected paths
189 deleting data/widest/f.i
190 deleting meta/widest/00manifest.i (tree !)
191 $ hg tracked
192 I path:outisde
193 I path:wider
194 X path:inside
195 X path:outside2
196 X path:widest
197
198 $ hg tracked --import-rules specs --clear
199 The --clear option is not yet supported.
200 [1]
201
202 Testing with passing a out of wdir file
203
204 $ cat > ../nspecs <<EOF
205 > [include]
206 > widest
207 > EOF
208
209 $ hg tracked --import-rules ../nspecs
210 comparing with ssh://user@dummy/master
211 searching for changes
212 no changes found
213 saved backup bundle to $TESTTMP/narrow/.hg/strip-backup/*-widen.hg (glob)
214 adding changesets
215 adding manifests
216 adding file changes
217 added 3 changesets with 0 changes to 0 files
218 new changesets *:* (glob)
@@ -1,430 +1,449 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 cmdutil,
14 cmdutil,
15 commands,
15 commands,
16 discovery,
16 discovery,
17 encoding,
17 error,
18 error,
18 exchange,
19 exchange,
19 extensions,
20 extensions,
20 hg,
21 hg,
21 merge,
22 merge,
22 narrowspec,
23 narrowspec,
23 node,
24 node,
24 pycompat,
25 pycompat,
25 registrar,
26 registrar,
26 repair,
27 repair,
27 repository,
28 repository,
28 repoview,
29 repoview,
29 sparse,
30 sparse,
30 util,
31 util,
31 )
32 )
32
33
33 from . import (
34 from . import (
34 narrowbundle2,
35 narrowbundle2,
35 )
36 )
36
37
37 table = {}
38 table = {}
38 command = registrar.command(table)
39 command = registrar.command(table)
39
40
40 def setup():
41 def setup():
41 """Wraps user-facing mercurial commands with narrow-aware versions."""
42 """Wraps user-facing mercurial commands with narrow-aware versions."""
42
43
43 entry = extensions.wrapcommand(commands.table, 'clone', clonenarrowcmd)
44 entry = extensions.wrapcommand(commands.table, 'clone', clonenarrowcmd)
44 entry[1].append(('', 'narrow', None,
45 entry[1].append(('', 'narrow', None,
45 _("create a narrow clone of select files")))
46 _("create a narrow clone of select files")))
46 entry[1].append(('', 'depth', '',
47 entry[1].append(('', 'depth', '',
47 _("limit the history fetched by distance from heads")))
48 _("limit the history fetched by distance from heads")))
48 entry[1].append(('', 'narrowspec', '',
49 entry[1].append(('', 'narrowspec', '',
49 _("read narrowspecs from file")))
50 _("read narrowspecs from file")))
50 # TODO(durin42): unify sparse/narrow --include/--exclude logic a bit
51 # TODO(durin42): unify sparse/narrow --include/--exclude logic a bit
51 if 'sparse' not in extensions.enabled():
52 if 'sparse' not in extensions.enabled():
52 entry[1].append(('', 'include', [],
53 entry[1].append(('', 'include', [],
53 _("specifically fetch this file/directory")))
54 _("specifically fetch this file/directory")))
54 entry[1].append(
55 entry[1].append(
55 ('', 'exclude', [],
56 ('', 'exclude', [],
56 _("do not fetch this file/directory, even if included")))
57 _("do not fetch this file/directory, even if included")))
57
58
58 entry = extensions.wrapcommand(commands.table, 'pull', pullnarrowcmd)
59 entry = extensions.wrapcommand(commands.table, 'pull', pullnarrowcmd)
59 entry[1].append(('', 'depth', '',
60 entry[1].append(('', 'depth', '',
60 _("limit the history fetched by distance from heads")))
61 _("limit the history fetched by distance from heads")))
61
62
62 extensions.wrapcommand(commands.table, 'archive', archivenarrowcmd)
63 extensions.wrapcommand(commands.table, 'archive', archivenarrowcmd)
63
64
64 def expandpull(pullop, includepats, excludepats):
65 def expandpull(pullop, includepats, excludepats):
65 if not narrowspec.needsexpansion(includepats):
66 if not narrowspec.needsexpansion(includepats):
66 return includepats, excludepats
67 return includepats, excludepats
67
68
68 heads = pullop.heads or pullop.rheads
69 heads = pullop.heads or pullop.rheads
69 includepats, excludepats = pullop.remote.expandnarrow(
70 includepats, excludepats = pullop.remote.expandnarrow(
70 includepats, excludepats, heads)
71 includepats, excludepats, heads)
71 pullop.repo.ui.debug('Expanded narrowspec to inc=%s, exc=%s\n' % (
72 pullop.repo.ui.debug('Expanded narrowspec to inc=%s, exc=%s\n' % (
72 includepats, excludepats))
73 includepats, excludepats))
73 return set(includepats), set(excludepats)
74 return set(includepats), set(excludepats)
74
75
75 def clonenarrowcmd(orig, ui, repo, *args, **opts):
76 def clonenarrowcmd(orig, ui, repo, *args, **opts):
76 """Wraps clone command, so 'hg clone' first wraps localrepo.clone()."""
77 """Wraps clone command, so 'hg clone' first wraps localrepo.clone()."""
77 opts = pycompat.byteskwargs(opts)
78 opts = pycompat.byteskwargs(opts)
78 wrappedextraprepare = util.nullcontextmanager()
79 wrappedextraprepare = util.nullcontextmanager()
79 opts_narrow = opts['narrow']
80 opts_narrow = opts['narrow']
80 narrowspecfile = opts['narrowspec']
81 narrowspecfile = opts['narrowspec']
81
82
82 if narrowspecfile:
83 if narrowspecfile:
83 filepath = os.path.join(pycompat.getcwd(), narrowspecfile)
84 filepath = os.path.join(pycompat.getcwd(), narrowspecfile)
84 ui.status(_("reading narrowspec from '%s'\n") % filepath)
85 ui.status(_("reading narrowspec from '%s'\n") % filepath)
85 try:
86 try:
86 fp = open(filepath, 'rb')
87 fp = open(filepath, 'rb')
87 except IOError:
88 except IOError:
88 raise error.Abort(_("file '%s' not found") % filepath)
89 raise error.Abort(_("file '%s' not found") % filepath)
89
90
90 includes, excludes, profiles = sparse.parseconfig(ui, fp.read(),
91 includes, excludes, profiles = sparse.parseconfig(ui, fp.read(),
91 'narrow')
92 'narrow')
92 if profiles:
93 if profiles:
93 raise error.Abort(_("cannot specify other files using '%include' in"
94 raise error.Abort(_("cannot specify other files using '%include' in"
94 " narrowspec"))
95 " narrowspec"))
95
96
96 # narrowspec is passed so we should assume that user wants narrow clone
97 # narrowspec is passed so we should assume that user wants narrow clone
97 opts_narrow = True
98 opts_narrow = True
98 opts['include'].extend(includes)
99 opts['include'].extend(includes)
99 opts['exclude'].extend(excludes)
100 opts['exclude'].extend(excludes)
100
101
101 if opts_narrow:
102 if opts_narrow:
102 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
103 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
103 # Create narrow spec patterns from clone flags
104 # Create narrow spec patterns from clone flags
104 includepats = narrowspec.parsepatterns(opts['include'])
105 includepats = narrowspec.parsepatterns(opts['include'])
105 excludepats = narrowspec.parsepatterns(opts['exclude'])
106 excludepats = narrowspec.parsepatterns(opts['exclude'])
106
107
107 # If necessary, ask the server to expand the narrowspec.
108 # If necessary, ask the server to expand the narrowspec.
108 includepats, excludepats = expandpull(
109 includepats, excludepats = expandpull(
109 pullop, includepats, excludepats)
110 pullop, includepats, excludepats)
110
111
111 if not includepats and excludepats:
112 if not includepats and excludepats:
112 # If nothing was included, we assume the user meant to include
113 # If nothing was included, we assume the user meant to include
113 # everything, except what they asked to exclude.
114 # everything, except what they asked to exclude.
114 includepats = {'path:.'}
115 includepats = {'path:.'}
115
116
116 pullop.repo.setnarrowpats(includepats, excludepats)
117 pullop.repo.setnarrowpats(includepats, excludepats)
117
118
118 # This will populate 'includepats' etc with the values from the
119 # This will populate 'includepats' etc with the values from the
119 # narrowspec we just saved.
120 # narrowspec we just saved.
120 orig(pullop, kwargs)
121 orig(pullop, kwargs)
121
122
122 if opts.get('depth'):
123 if opts.get('depth'):
123 kwargs['depth'] = opts['depth']
124 kwargs['depth'] = opts['depth']
124 wrappedextraprepare = extensions.wrappedfunction(exchange,
125 wrappedextraprepare = extensions.wrappedfunction(exchange,
125 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
126 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
126
127
127 def pullnarrow(orig, repo, *args, **kwargs):
128 def pullnarrow(orig, repo, *args, **kwargs):
128 if opts_narrow:
129 if opts_narrow:
129 repo.requirements.add(repository.NARROW_REQUIREMENT)
130 repo.requirements.add(repository.NARROW_REQUIREMENT)
130 repo._writerequirements()
131 repo._writerequirements()
131
132
132 return orig(repo, *args, **kwargs)
133 return orig(repo, *args, **kwargs)
133
134
134 wrappedpull = extensions.wrappedfunction(exchange, 'pull', pullnarrow)
135 wrappedpull = extensions.wrappedfunction(exchange, 'pull', pullnarrow)
135
136
136 with wrappedextraprepare, wrappedpull:
137 with wrappedextraprepare, wrappedpull:
137 return orig(ui, repo, *args, **pycompat.strkwargs(opts))
138 return orig(ui, repo, *args, **pycompat.strkwargs(opts))
138
139
139 def pullnarrowcmd(orig, ui, repo, *args, **opts):
140 def pullnarrowcmd(orig, ui, repo, *args, **opts):
140 """Wraps pull command to allow modifying narrow spec."""
141 """Wraps pull command to allow modifying narrow spec."""
141 wrappedextraprepare = util.nullcontextmanager()
142 wrappedextraprepare = util.nullcontextmanager()
142 if repository.NARROW_REQUIREMENT in repo.requirements:
143 if repository.NARROW_REQUIREMENT in repo.requirements:
143
144
144 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
145 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
145 orig(pullop, kwargs)
146 orig(pullop, kwargs)
146 if opts.get(r'depth'):
147 if opts.get(r'depth'):
147 kwargs['depth'] = opts[r'depth']
148 kwargs['depth'] = opts[r'depth']
148 wrappedextraprepare = extensions.wrappedfunction(exchange,
149 wrappedextraprepare = extensions.wrappedfunction(exchange,
149 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
150 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
150
151
151 with wrappedextraprepare:
152 with wrappedextraprepare:
152 return orig(ui, repo, *args, **opts)
153 return orig(ui, repo, *args, **opts)
153
154
154 def archivenarrowcmd(orig, ui, repo, *args, **opts):
155 def archivenarrowcmd(orig, ui, repo, *args, **opts):
155 """Wraps archive command to narrow the default includes."""
156 """Wraps archive command to narrow the default includes."""
156 if repository.NARROW_REQUIREMENT in repo.requirements:
157 if repository.NARROW_REQUIREMENT in repo.requirements:
157 repo_includes, repo_excludes = repo.narrowpats
158 repo_includes, repo_excludes = repo.narrowpats
158 includes = set(opts.get(r'include', []))
159 includes = set(opts.get(r'include', []))
159 excludes = set(opts.get(r'exclude', []))
160 excludes = set(opts.get(r'exclude', []))
160 includes, excludes, unused_invalid = narrowspec.restrictpatterns(
161 includes, excludes, unused_invalid = narrowspec.restrictpatterns(
161 includes, excludes, repo_includes, repo_excludes)
162 includes, excludes, repo_includes, repo_excludes)
162 if includes:
163 if includes:
163 opts[r'include'] = includes
164 opts[r'include'] = includes
164 if excludes:
165 if excludes:
165 opts[r'exclude'] = excludes
166 opts[r'exclude'] = excludes
166 return orig(ui, repo, *args, **opts)
167 return orig(ui, repo, *args, **opts)
167
168
168 def pullbundle2extraprepare(orig, pullop, kwargs):
169 def pullbundle2extraprepare(orig, pullop, kwargs):
169 repo = pullop.repo
170 repo = pullop.repo
170 if repository.NARROW_REQUIREMENT not in repo.requirements:
171 if repository.NARROW_REQUIREMENT not in repo.requirements:
171 return orig(pullop, kwargs)
172 return orig(pullop, kwargs)
172
173
173 if narrowbundle2.NARROWCAP not in pullop.remotebundle2caps:
174 if narrowbundle2.NARROWCAP not in pullop.remotebundle2caps:
174 raise error.Abort(_("server doesn't support narrow clones"))
175 raise error.Abort(_("server doesn't support narrow clones"))
175 orig(pullop, kwargs)
176 orig(pullop, kwargs)
176 kwargs['narrow'] = True
177 kwargs['narrow'] = True
177 include, exclude = repo.narrowpats
178 include, exclude = repo.narrowpats
178 kwargs['oldincludepats'] = include
179 kwargs['oldincludepats'] = include
179 kwargs['oldexcludepats'] = exclude
180 kwargs['oldexcludepats'] = exclude
180 kwargs['includepats'] = include
181 kwargs['includepats'] = include
181 kwargs['excludepats'] = exclude
182 kwargs['excludepats'] = exclude
182 kwargs['known'] = [node.hex(ctx.node()) for ctx in
183 kwargs['known'] = [node.hex(ctx.node()) for ctx in
183 repo.set('::%ln', pullop.common)
184 repo.set('::%ln', pullop.common)
184 if ctx.node() != node.nullid]
185 if ctx.node() != node.nullid]
185 if not kwargs['known']:
186 if not kwargs['known']:
186 # Mercurial serialized an empty list as '' and deserializes it as
187 # Mercurial serialized an empty list as '' and deserializes it as
187 # [''], so delete it instead to avoid handling the empty string on the
188 # [''], so delete it instead to avoid handling the empty string on the
188 # server.
189 # server.
189 del kwargs['known']
190 del kwargs['known']
190
191
191 extensions.wrapfunction(exchange,'_pullbundle2extraprepare',
192 extensions.wrapfunction(exchange,'_pullbundle2extraprepare',
192 pullbundle2extraprepare)
193 pullbundle2extraprepare)
193
194
194 def _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
195 def _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
195 newincludes, newexcludes, force):
196 newincludes, newexcludes, force):
196 oldmatch = narrowspec.match(repo.root, oldincludes, oldexcludes)
197 oldmatch = narrowspec.match(repo.root, oldincludes, oldexcludes)
197 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
198 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
198
199
199 # This is essentially doing "hg outgoing" to find all local-only
200 # This is essentially doing "hg outgoing" to find all local-only
200 # commits. We will then check that the local-only commits don't
201 # commits. We will then check that the local-only commits don't
201 # have any changes to files that will be untracked.
202 # have any changes to files that will be untracked.
202 unfi = repo.unfiltered()
203 unfi = repo.unfiltered()
203 outgoing = discovery.findcommonoutgoing(unfi, remote,
204 outgoing = discovery.findcommonoutgoing(unfi, remote,
204 commoninc=commoninc)
205 commoninc=commoninc)
205 ui.status(_('looking for local changes to affected paths\n'))
206 ui.status(_('looking for local changes to affected paths\n'))
206 localnodes = []
207 localnodes = []
207 for n in itertools.chain(outgoing.missing, outgoing.excluded):
208 for n in itertools.chain(outgoing.missing, outgoing.excluded):
208 if any(oldmatch(f) and not newmatch(f) for f in unfi[n].files()):
209 if any(oldmatch(f) and not newmatch(f) for f in unfi[n].files()):
209 localnodes.append(n)
210 localnodes.append(n)
210 revstostrip = unfi.revs('descendants(%ln)', localnodes)
211 revstostrip = unfi.revs('descendants(%ln)', localnodes)
211 hiddenrevs = repoview.filterrevs(repo, 'visible')
212 hiddenrevs = repoview.filterrevs(repo, 'visible')
212 visibletostrip = list(repo.changelog.node(r)
213 visibletostrip = list(repo.changelog.node(r)
213 for r in (revstostrip - hiddenrevs))
214 for r in (revstostrip - hiddenrevs))
214 if visibletostrip:
215 if visibletostrip:
215 ui.status(_('The following changeset(s) or their ancestors have '
216 ui.status(_('The following changeset(s) or their ancestors have '
216 'local changes not on the remote:\n'))
217 'local changes not on the remote:\n'))
217 maxnodes = 10
218 maxnodes = 10
218 if ui.verbose or len(visibletostrip) <= maxnodes:
219 if ui.verbose or len(visibletostrip) <= maxnodes:
219 for n in visibletostrip:
220 for n in visibletostrip:
220 ui.status('%s\n' % node.short(n))
221 ui.status('%s\n' % node.short(n))
221 else:
222 else:
222 for n in visibletostrip[:maxnodes]:
223 for n in visibletostrip[:maxnodes]:
223 ui.status('%s\n' % node.short(n))
224 ui.status('%s\n' % node.short(n))
224 ui.status(_('...and %d more, use --verbose to list all\n') %
225 ui.status(_('...and %d more, use --verbose to list all\n') %
225 (len(visibletostrip) - maxnodes))
226 (len(visibletostrip) - maxnodes))
226 if not force:
227 if not force:
227 raise error.Abort(_('local changes found'),
228 raise error.Abort(_('local changes found'),
228 hint=_('use --force-delete-local-changes to '
229 hint=_('use --force-delete-local-changes to '
229 'ignore'))
230 'ignore'))
230
231
231 with ui.uninterruptable():
232 with ui.uninterruptable():
232 if revstostrip:
233 if revstostrip:
233 tostrip = [unfi.changelog.node(r) for r in revstostrip]
234 tostrip = [unfi.changelog.node(r) for r in revstostrip]
234 if repo['.'].node() in tostrip:
235 if repo['.'].node() in tostrip:
235 # stripping working copy, so move to a different commit first
236 # stripping working copy, so move to a different commit first
236 urev = max(repo.revs('(::%n) - %ln + null',
237 urev = max(repo.revs('(::%n) - %ln + null',
237 repo['.'].node(), visibletostrip))
238 repo['.'].node(), visibletostrip))
238 hg.clean(repo, urev)
239 hg.clean(repo, urev)
239 repair.strip(ui, unfi, tostrip, topic='narrow')
240 repair.strip(ui, unfi, tostrip, topic='narrow')
240
241
241 todelete = []
242 todelete = []
242 for f, f2, size in repo.store.datafiles():
243 for f, f2, size in repo.store.datafiles():
243 if f.startswith('data/'):
244 if f.startswith('data/'):
244 file = f[5:-2]
245 file = f[5:-2]
245 if not newmatch(file):
246 if not newmatch(file):
246 todelete.append(f)
247 todelete.append(f)
247 elif f.startswith('meta/'):
248 elif f.startswith('meta/'):
248 dir = f[5:-13]
249 dir = f[5:-13]
249 dirs = ['.'] + sorted(util.dirs({dir})) + [dir]
250 dirs = ['.'] + sorted(util.dirs({dir})) + [dir]
250 include = True
251 include = True
251 for d in dirs:
252 for d in dirs:
252 visit = newmatch.visitdir(d)
253 visit = newmatch.visitdir(d)
253 if not visit:
254 if not visit:
254 include = False
255 include = False
255 break
256 break
256 if visit == 'all':
257 if visit == 'all':
257 break
258 break
258 if not include:
259 if not include:
259 todelete.append(f)
260 todelete.append(f)
260
261
261 repo.destroying()
262 repo.destroying()
262
263
263 with repo.transaction("narrowing"):
264 with repo.transaction("narrowing"):
264 for f in todelete:
265 for f in todelete:
265 ui.status(_('deleting %s\n') % f)
266 ui.status(_('deleting %s\n') % f)
266 util.unlinkpath(repo.svfs.join(f))
267 util.unlinkpath(repo.svfs.join(f))
267 repo.store.markremoved(f)
268 repo.store.markremoved(f)
268
269
269 for f in repo.dirstate:
270 for f in repo.dirstate:
270 if not newmatch(f):
271 if not newmatch(f):
271 repo.dirstate.drop(f)
272 repo.dirstate.drop(f)
272 repo.wvfs.unlinkpath(f)
273 repo.wvfs.unlinkpath(f)
273 repo.setnarrowpats(newincludes, newexcludes)
274 repo.setnarrowpats(newincludes, newexcludes)
274
275
275 repo.destroyed()
276 repo.destroyed()
276
277
277 def _widen(ui, repo, remote, commoninc, newincludes, newexcludes):
278 def _widen(ui, repo, remote, commoninc, newincludes, newexcludes):
278 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
279 newmatch = narrowspec.match(repo.root, newincludes, newexcludes)
279
280
280 # TODO(martinvonz): Get expansion working with widening/narrowing.
281 # TODO(martinvonz): Get expansion working with widening/narrowing.
281 if narrowspec.needsexpansion(newincludes):
282 if narrowspec.needsexpansion(newincludes):
282 raise error.Abort('Expansion not yet supported on pull')
283 raise error.Abort('Expansion not yet supported on pull')
283
284
284 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
285 def pullbundle2extraprepare_widen(orig, pullop, kwargs):
285 orig(pullop, kwargs)
286 orig(pullop, kwargs)
286 # The old{in,ex}cludepats have already been set by orig()
287 # The old{in,ex}cludepats have already been set by orig()
287 kwargs['includepats'] = newincludes
288 kwargs['includepats'] = newincludes
288 kwargs['excludepats'] = newexcludes
289 kwargs['excludepats'] = newexcludes
289 kwargs['widen'] = True
290 kwargs['widen'] = True
290 wrappedextraprepare = extensions.wrappedfunction(exchange,
291 wrappedextraprepare = extensions.wrappedfunction(exchange,
291 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
292 '_pullbundle2extraprepare', pullbundle2extraprepare_widen)
292
293
293 # define a function that narrowbundle2 can call after creating the
294 # define a function that narrowbundle2 can call after creating the
294 # backup bundle, but before applying the bundle from the server
295 # backup bundle, but before applying the bundle from the server
295 def setnewnarrowpats():
296 def setnewnarrowpats():
296 repo.setnarrowpats(newincludes, newexcludes)
297 repo.setnarrowpats(newincludes, newexcludes)
297 repo.setnewnarrowpats = setnewnarrowpats
298 repo.setnewnarrowpats = setnewnarrowpats
298
299
299 with ui.uninterruptable():
300 with ui.uninterruptable():
300 ds = repo.dirstate
301 ds = repo.dirstate
301 p1, p2 = ds.p1(), ds.p2()
302 p1, p2 = ds.p1(), ds.p2()
302 with ds.parentchange():
303 with ds.parentchange():
303 ds.setparents(node.nullid, node.nullid)
304 ds.setparents(node.nullid, node.nullid)
304 common = commoninc[0]
305 common = commoninc[0]
305 with wrappedextraprepare:
306 with wrappedextraprepare:
306 exchange.pull(repo, remote, heads=common)
307 exchange.pull(repo, remote, heads=common)
307 with ds.parentchange():
308 with ds.parentchange():
308 ds.setparents(p1, p2)
309 ds.setparents(p1, p2)
309
310
310 repo.setnewnarrowpats()
311 repo.setnewnarrowpats()
311 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()}
312 addgaction = actions['g'].append
313 addgaction = actions['g'].append
313
314
314 mf = repo['.'].manifest().matches(newmatch)
315 mf = repo['.'].manifest().matches(newmatch)
315 for f, fn in mf.iteritems():
316 for f, fn in mf.iteritems():
316 if f not in repo.dirstate:
317 if f not in repo.dirstate:
317 addgaction((f, (mf.flags(f), False),
318 addgaction((f, (mf.flags(f), False),
318 "add from widened narrow clone"))
319 "add from widened narrow clone"))
319
320
320 merge.applyupdates(repo, actions, wctx=repo[None],
321 merge.applyupdates(repo, actions, wctx=repo[None],
321 mctx=repo['.'], overwrite=False)
322 mctx=repo['.'], overwrite=False)
322 merge.recordupdates(repo, actions, branchmerge=False)
323 merge.recordupdates(repo, actions, branchmerge=False)
323
324
324 # TODO(rdamazio): Make new matcher format and update description
325 # TODO(rdamazio): Make new matcher format and update description
325 @command('tracked',
326 @command('tracked',
326 [('', 'addinclude', [], _('new paths to include')),
327 [('', 'addinclude', [], _('new paths to include')),
327 ('', 'removeinclude', [], _('old paths to no longer include')),
328 ('', 'removeinclude', [], _('old paths to no longer include')),
328 ('', 'addexclude', [], _('new paths to exclude')),
329 ('', 'addexclude', [], _('new paths to exclude')),
330 ('', 'import-rules', '', _('import narrowspecs from a file')),
329 ('', 'removeexclude', [], _('old paths to no longer exclude')),
331 ('', 'removeexclude', [], _('old paths to no longer exclude')),
330 ('', 'clear', False, _('whether to replace the existing narrowspec')),
332 ('', 'clear', False, _('whether to replace the existing narrowspec')),
331 ('', 'force-delete-local-changes', False,
333 ('', 'force-delete-local-changes', False,
332 _('forces deletion of local changes when narrowing')),
334 _('forces deletion of local changes when narrowing')),
333 ] + commands.remoteopts,
335 ] + commands.remoteopts,
334 _('[OPTIONS]... [REMOTE]'),
336 _('[OPTIONS]... [REMOTE]'),
335 inferrepo=True)
337 inferrepo=True)
336 def trackedcmd(ui, repo, remotepath=None, *pats, **opts):
338 def trackedcmd(ui, repo, remotepath=None, *pats, **opts):
337 """show or change the current narrowspec
339 """show or change the current narrowspec
338
340
339 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
340 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,
341 respectively.
343 respectively.
342
344
343 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
344 directories that should be pulled into your client.
346 directories that should be pulled into your client.
345 The narrowspec has *include* and *exclude* expressions, with excludes always
347 The narrowspec has *include* and *exclude* expressions, with excludes always
346 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
347 be excluded even if it also matches an include expression.
349 be excluded even if it also matches an include expression.
348 Excluding files that were never included has no effect.
350 Excluding files that were never included has no effect.
349
351
350 Each included or excluded entry is in the format described by
352 Each included or excluded entry is in the format described by
351 'hg help patterns'.
353 'hg help patterns'.
352
354
353 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.
354
356
355 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
356 and replaced by the new ones specified to --addinclude and --addexclude.
358 and replaced by the new ones specified to --addinclude and --addexclude.
357 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
358 empty and will not match any files.
360 empty and will not match any files.
359 """
361 """
360 opts = pycompat.byteskwargs(opts)
362 opts = pycompat.byteskwargs(opts)
361 if repository.NARROW_REQUIREMENT not in repo.requirements:
363 if repository.NARROW_REQUIREMENT not in repo.requirements:
362 ui.warn(_('The narrow command is only supported on respositories cloned'
364 ui.warn(_('The narrow command is only supported on respositories cloned'
363 ' with --narrow.\n'))
365 ' with --narrow.\n'))
364 return 1
366 return 1
365
367
366 # Before supporting, decide whether it "hg tracked --clear" should mean
368 # Before supporting, decide whether it "hg tracked --clear" should mean
367 # tracking no paths or all paths.
369 # tracking no paths or all paths.
368 if opts['clear']:
370 if opts['clear']:
369 ui.warn(_('The --clear option is not yet supported.\n'))
371 ui.warn(_('The --clear option is not yet supported.\n'))
370 return 1
372 return 1
371
373
374 # import rules from a file
375 newrules = opts.get('import_rules')
376 if newrules:
377 try:
378 filepath = os.path.join(pycompat.getcwd(), newrules)
379 fdata = util.readfile(filepath)
380 except IOError as inst:
381 raise error.Abort(_("cannot read narrowspecs from '%s': %s") %
382 (filepath, encoding.strtolocal(inst.strerror)))
383 includepats, excludepats, profiles = sparse.parseconfig(ui, fdata,
384 'narrow')
385 if profiles:
386 raise error.Abort(_("including other spec files using '%include' "
387 "is not supported in narrowspec"))
388 opts['addinclude'].extend(includepats)
389 opts['addexclude'].extend(excludepats)
390
372 if narrowspec.needsexpansion(opts['addinclude'] + opts['addexclude']):
391 if narrowspec.needsexpansion(opts['addinclude'] + opts['addexclude']):
373 raise error.Abort('Expansion not yet supported on widen/narrow')
392 raise error.Abort('Expansion not yet supported on widen/narrow')
374
393
375 addedincludes = narrowspec.parsepatterns(opts['addinclude'])
394 addedincludes = narrowspec.parsepatterns(opts['addinclude'])
376 removedincludes = narrowspec.parsepatterns(opts['removeinclude'])
395 removedincludes = narrowspec.parsepatterns(opts['removeinclude'])
377 addedexcludes = narrowspec.parsepatterns(opts['addexclude'])
396 addedexcludes = narrowspec.parsepatterns(opts['addexclude'])
378 removedexcludes = narrowspec.parsepatterns(opts['removeexclude'])
397 removedexcludes = narrowspec.parsepatterns(opts['removeexclude'])
379 widening = addedincludes or removedexcludes
398 widening = addedincludes or removedexcludes
380 narrowing = removedincludes or addedexcludes
399 narrowing = removedincludes or addedexcludes
381 only_show = not widening and not narrowing
400 only_show = not widening and not narrowing
382
401
383 # Only print the current narrowspec.
402 # Only print the current narrowspec.
384 if only_show:
403 if only_show:
385 include, exclude = repo.narrowpats
404 include, exclude = repo.narrowpats
386
405
387 ui.pager('tracked')
406 ui.pager('tracked')
388 fm = ui.formatter('narrow', opts)
407 fm = ui.formatter('narrow', opts)
389 for i in sorted(include):
408 for i in sorted(include):
390 fm.startitem()
409 fm.startitem()
391 fm.write('status', '%s ', 'I', label='narrow.included')
410 fm.write('status', '%s ', 'I', label='narrow.included')
392 fm.write('pat', '%s\n', i, label='narrow.included')
411 fm.write('pat', '%s\n', i, label='narrow.included')
393 for i in sorted(exclude):
412 for i in sorted(exclude):
394 fm.startitem()
413 fm.startitem()
395 fm.write('status', '%s ', 'X', label='narrow.excluded')
414 fm.write('status', '%s ', 'X', label='narrow.excluded')
396 fm.write('pat', '%s\n', i, label='narrow.excluded')
415 fm.write('pat', '%s\n', i, label='narrow.excluded')
397 fm.end()
416 fm.end()
398 return 0
417 return 0
399
418
400 with repo.wlock(), repo.lock():
419 with repo.wlock(), repo.lock():
401 cmdutil.bailifchanged(repo)
420 cmdutil.bailifchanged(repo)
402
421
403 # Find the revisions we have in common with the remote. These will
422 # Find the revisions we have in common with the remote. These will
404 # be used for finding local-only changes for narrowing. They will
423 # be used for finding local-only changes for narrowing. They will
405 # also define the set of revisions to update for widening.
424 # also define the set of revisions to update for widening.
406 remotepath = ui.expandpath(remotepath or 'default')
425 remotepath = ui.expandpath(remotepath or 'default')
407 url, branches = hg.parseurl(remotepath)
426 url, branches = hg.parseurl(remotepath)
408 ui.status(_('comparing with %s\n') % util.hidepassword(url))
427 ui.status(_('comparing with %s\n') % util.hidepassword(url))
409 remote = hg.peer(repo, opts, url)
428 remote = hg.peer(repo, opts, url)
410 commoninc = discovery.findcommonincoming(repo, remote)
429 commoninc = discovery.findcommonincoming(repo, remote)
411
430
412 oldincludes, oldexcludes = repo.narrowpats
431 oldincludes, oldexcludes = repo.narrowpats
413 if narrowing:
432 if narrowing:
414 newincludes = oldincludes - removedincludes
433 newincludes = oldincludes - removedincludes
415 newexcludes = oldexcludes | addedexcludes
434 newexcludes = oldexcludes | addedexcludes
416 _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
435 _narrow(ui, repo, remote, commoninc, oldincludes, oldexcludes,
417 newincludes, newexcludes,
436 newincludes, newexcludes,
418 opts['force_delete_local_changes'])
437 opts['force_delete_local_changes'])
419 # _narrow() updated the narrowspec and _widen() below needs to
438 # _narrow() updated the narrowspec and _widen() below needs to
420 # use the updated values as its base (otherwise removed includes
439 # use the updated values as its base (otherwise removed includes
421 # and addedexcludes will be lost in the resulting narrowspec)
440 # and addedexcludes will be lost in the resulting narrowspec)
422 oldincludes = newincludes
441 oldincludes = newincludes
423 oldexcludes = newexcludes
442 oldexcludes = newexcludes
424
443
425 if widening:
444 if widening:
426 newincludes = oldincludes | addedincludes
445 newincludes = oldincludes | addedincludes
427 newexcludes = oldexcludes - removedexcludes
446 newexcludes = oldexcludes - removedexcludes
428 _widen(ui, repo, remote, commoninc, newincludes, newexcludes)
447 _widen(ui, repo, remote, commoninc, newincludes, newexcludes)
429
448
430 return 0
449 return 0
General Comments 0
You need to be logged in to leave comments. Login now