##// END OF EJS Templates
narrow: consider both local and remote matchers in narrowchangegroup...
Martin von Zweigbergk -
r36485:2d82a24d default
parent child Browse files
Show More
@@ -1,366 +1,372 b''
1 1 # narrowchangegroup.py - narrow clone changegroup creation and consumption
2 2 #
3 3 # Copyright 2017 Google, Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 from mercurial.i18n import _
11 11 from mercurial import (
12 12 changegroup,
13 13 error,
14 14 extensions,
15 15 manifest,
16 match as matchmod,
16 17 mdiff,
17 18 node,
18 19 revlog,
19 20 util,
20 21 )
21 22
22 23 def setup():
23 24
25 def _cgmatcher(cgpacker):
26 localmatcher = getattr(cgpacker._repo, 'narrowmatch', lambda: None)()
27 remotematcher = getattr(cgpacker, '_narrow_matcher', lambda: None)()
28 if localmatcher and remotematcher:
29 return matchmod.intersectmatchers(localmatcher, remotematcher)
30 else:
31 return localmatcher or remotematcher
32
24 33 def prune(orig, self, revlog, missing, commonrevs):
25 34 if isinstance(revlog, manifest.manifestrevlog):
26 matcher = getattr(self._repo, 'narrowmatch',
27 getattr(self, '_narrow_matcher', None))
28 if (matcher is not None and
29 not matcher().visitdir(revlog._dir[:-1] or '.')):
35 matcher = _cgmatcher(self)
36 if (matcher and
37 not matcher.visitdir(revlog._dir[:-1] or '.')):
30 38 return []
31 39 return orig(self, revlog, missing, commonrevs)
32 40
33 41 extensions.wrapfunction(changegroup.cg1packer, 'prune', prune)
34 42
35 43 def generatefiles(orig, self, changedfiles, linknodes, commonrevs,
36 44 source):
37 matcher = getattr(self._repo, 'narrowmatch',
38 getattr(self, '_narrow_matcher', None))
39 if matcher is not None:
40 narrowmatch = matcher()
41 changedfiles = [f for f in changedfiles if narrowmatch(f)]
45 matcher = _cgmatcher(self)
46 if matcher:
47 changedfiles = filter(matcher, changedfiles)
42 48 if getattr(self, 'is_shallow', False):
43 49 # See comment in generate() for why this sadness is a thing.
44 50 mfdicts = self._mfdicts
45 51 del self._mfdicts
46 52 # In a shallow clone, the linknodes callback needs to also include
47 53 # those file nodes that are in the manifests we sent but weren't
48 54 # introduced by those manifests.
49 55 commonctxs = [self._repo[c] for c in commonrevs]
50 56 oldlinknodes = linknodes
51 57 clrev = self._repo.changelog.rev
52 58 def linknodes(flog, fname):
53 59 for c in commonctxs:
54 60 try:
55 61 fnode = c.filenode(fname)
56 62 self.clrev_to_localrev[c.rev()] = flog.rev(fnode)
57 63 except error.ManifestLookupError:
58 64 pass
59 65 links = oldlinknodes(flog, fname)
60 66 if len(links) != len(mfdicts):
61 67 for mf, lr in mfdicts:
62 68 fnode = mf.get(fname, None)
63 69 if fnode in links:
64 70 links[fnode] = min(links[fnode], lr, key=clrev)
65 71 elif fnode:
66 72 links[fnode] = lr
67 73 return links
68 74 return orig(self, changedfiles, linknodes, commonrevs, source)
69 75 extensions.wrapfunction(
70 76 changegroup.cg1packer, 'generatefiles', generatefiles)
71 77
72 78 def ellipsisdata(packer, rev, revlog_, p1, p2, data, linknode):
73 79 n = revlog_.node(rev)
74 80 p1n, p2n = revlog_.node(p1), revlog_.node(p2)
75 81 flags = revlog_.flags(rev)
76 82 flags |= revlog.REVIDX_ELLIPSIS
77 83 meta = packer.builddeltaheader(
78 84 n, p1n, p2n, node.nullid, linknode, flags)
79 85 # TODO: try and actually send deltas for ellipsis data blocks
80 86 diffheader = mdiff.trivialdiffheader(len(data))
81 87 l = len(meta) + len(diffheader) + len(data)
82 88 return ''.join((changegroup.chunkheader(l),
83 89 meta,
84 90 diffheader,
85 91 data))
86 92
87 93 def close(orig, self):
88 94 getattr(self, 'clrev_to_localrev', {}).clear()
89 95 if getattr(self, 'next_clrev_to_localrev', {}):
90 96 self.clrev_to_localrev = self.next_clrev_to_localrev
91 97 del self.next_clrev_to_localrev
92 98 self.changelog_done = True
93 99 return orig(self)
94 100 extensions.wrapfunction(changegroup.cg1packer, 'close', close)
95 101
96 102 # In a perfect world, we'd generate better ellipsis-ified graphs
97 103 # for non-changelog revlogs. In practice, we haven't started doing
98 104 # that yet, so the resulting DAGs for the manifestlog and filelogs
99 105 # are actually full of bogus parentage on all the ellipsis
100 106 # nodes. This has the side effect that, while the contents are
101 107 # correct, the individual DAGs might be completely out of whack in
102 108 # a case like 882681bc3166 and its ancestors (back about 10
103 109 # revisions or so) in the main hg repo.
104 110 #
105 111 # The one invariant we *know* holds is that the new (potentially
106 112 # bogus) DAG shape will be valid if we order the nodes in the
107 113 # order that they're introduced in dramatis personae by the
108 114 # changelog, so what we do is we sort the non-changelog histories
109 115 # by the order in which they are used by the changelog.
110 116 def _sortgroup(orig, self, revlog, nodelist, lookup):
111 117 if not util.safehasattr(self, 'full_nodes') or not self.clnode_to_rev:
112 118 return orig(self, revlog, nodelist, lookup)
113 119 key = lambda n: self.clnode_to_rev[lookup(n)]
114 120 return [revlog.rev(n) for n in sorted(nodelist, key=key)]
115 121
116 122 extensions.wrapfunction(changegroup.cg1packer, '_sortgroup', _sortgroup)
117 123
118 124 def generate(orig, self, commonrevs, clnodes, fastpathlinkrev, source):
119 125 '''yield a sequence of changegroup chunks (strings)'''
120 126 # Note: other than delegating to orig, the only deviation in
121 127 # logic from normal hg's generate is marked with BEGIN/END
122 128 # NARROW HACK.
123 129 if not util.safehasattr(self, 'full_nodes'):
124 130 # not sending a narrow bundle
125 131 for x in orig(self, commonrevs, clnodes, fastpathlinkrev, source):
126 132 yield x
127 133 return
128 134
129 135 repo = self._repo
130 136 cl = repo.changelog
131 137 mfl = repo.manifestlog
132 138 mfrevlog = mfl._revlog
133 139
134 140 clrevorder = {}
135 141 mfs = {} # needed manifests
136 142 fnodes = {} # needed file nodes
137 143 changedfiles = set()
138 144
139 145 # Callback for the changelog, used to collect changed files and manifest
140 146 # nodes.
141 147 # Returns the linkrev node (identity in the changelog case).
142 148 def lookupcl(x):
143 149 c = cl.read(x)
144 150 clrevorder[x] = len(clrevorder)
145 151 # BEGIN NARROW HACK
146 152 #
147 153 # Only update mfs if x is going to be sent. Otherwise we
148 154 # end up with bogus linkrevs specified for manifests and
149 155 # we skip some manifest nodes that we should otherwise
150 156 # have sent.
151 157 if x in self.full_nodes or cl.rev(x) in self.precomputed_ellipsis:
152 158 n = c[0]
153 159 # record the first changeset introducing this manifest version
154 160 mfs.setdefault(n, x)
155 161 # Set this narrow-specific dict so we have the lowest manifest
156 162 # revnum to look up for this cl revnum. (Part of mapping
157 163 # changelog ellipsis parents to manifest ellipsis parents)
158 164 self.next_clrev_to_localrev.setdefault(cl.rev(x),
159 165 mfrevlog.rev(n))
160 166 # We can't trust the changed files list in the changeset if the
161 167 # client requested a shallow clone.
162 168 if self.is_shallow:
163 169 changedfiles.update(mfl[c[0]].read().keys())
164 170 else:
165 171 changedfiles.update(c[3])
166 172 # END NARROW HACK
167 173 # Record a complete list of potentially-changed files in
168 174 # this manifest.
169 175 return x
170 176
171 177 self._verbosenote(_('uncompressed size of bundle content:\n'))
172 178 size = 0
173 179 for chunk in self.group(clnodes, cl, lookupcl, units=_('changesets')):
174 180 size += len(chunk)
175 181 yield chunk
176 182 self._verbosenote(_('%8.i (changelog)\n') % size)
177 183
178 184 # We need to make sure that the linkrev in the changegroup refers to
179 185 # the first changeset that introduced the manifest or file revision.
180 186 # The fastpath is usually safer than the slowpath, because the filelogs
181 187 # are walked in revlog order.
182 188 #
183 189 # When taking the slowpath with reorder=None and the manifest revlog
184 190 # uses generaldelta, the manifest may be walked in the "wrong" order.
185 191 # Without 'clrevorder', we would get an incorrect linkrev (see fix in
186 192 # cc0ff93d0c0c).
187 193 #
188 194 # When taking the fastpath, we are only vulnerable to reordering
189 195 # of the changelog itself. The changelog never uses generaldelta, so
190 196 # it is only reordered when reorder=True. To handle this case, we
191 197 # simply take the slowpath, which already has the 'clrevorder' logic.
192 198 # This was also fixed in cc0ff93d0c0c.
193 199 fastpathlinkrev = fastpathlinkrev and not self._reorder
194 200 # Treemanifests don't work correctly with fastpathlinkrev
195 201 # either, because we don't discover which directory nodes to
196 202 # send along with files. This could probably be fixed.
197 203 fastpathlinkrev = fastpathlinkrev and (
198 204 'treemanifest' not in repo.requirements)
199 205 # Shallow clones also don't work correctly with fastpathlinkrev
200 206 # because file nodes may need to be sent for a manifest even if they
201 207 # weren't introduced by that manifest.
202 208 fastpathlinkrev = fastpathlinkrev and not self.is_shallow
203 209
204 210 for chunk in self.generatemanifests(commonrevs, clrevorder,
205 211 fastpathlinkrev, mfs, fnodes, source):
206 212 yield chunk
207 213 # BEGIN NARROW HACK
208 214 mfdicts = None
209 215 if self.is_shallow:
210 216 mfdicts = [(self._repo.manifestlog[n].read(), lr)
211 217 for (n, lr) in mfs.iteritems()]
212 218 # END NARROW HACK
213 219 mfs.clear()
214 220 clrevs = set(cl.rev(x) for x in clnodes)
215 221
216 222 if not fastpathlinkrev:
217 223 def linknodes(unused, fname):
218 224 return fnodes.get(fname, {})
219 225 else:
220 226 cln = cl.node
221 227 def linknodes(filerevlog, fname):
222 228 llr = filerevlog.linkrev
223 229 fln = filerevlog.node
224 230 revs = ((r, llr(r)) for r in filerevlog)
225 231 return dict((fln(r), cln(lr)) for r, lr in revs if lr in clrevs)
226 232
227 233 # BEGIN NARROW HACK
228 234 #
229 235 # We need to pass the mfdicts variable down into
230 236 # generatefiles(), but more than one command might have
231 237 # wrapped generatefiles so we can't modify the function
232 238 # signature. Instead, we pass the data to ourselves using an
233 239 # instance attribute. I'm sorry.
234 240 self._mfdicts = mfdicts
235 241 # END NARROW HACK
236 242 for chunk in self.generatefiles(changedfiles, linknodes, commonrevs,
237 243 source):
238 244 yield chunk
239 245
240 246 yield self.close()
241 247
242 248 if clnodes:
243 249 repo.hook('outgoing', node=node.hex(clnodes[0]), source=source)
244 250 extensions.wrapfunction(changegroup.cg1packer, 'generate', generate)
245 251
246 252 def revchunk(orig, self, revlog, rev, prev, linknode):
247 253 if not util.safehasattr(self, 'full_nodes'):
248 254 # not sending a narrow changegroup
249 255 for x in orig(self, revlog, rev, prev, linknode):
250 256 yield x
251 257 return
252 258 # build up some mapping information that's useful later. See
253 259 # the local() nested function below.
254 260 if not self.changelog_done:
255 261 self.clnode_to_rev[linknode] = rev
256 262 linkrev = rev
257 263 self.clrev_to_localrev[linkrev] = rev
258 264 else:
259 265 linkrev = self.clnode_to_rev[linknode]
260 266 self.clrev_to_localrev[linkrev] = rev
261 267 # This is a node to send in full, because the changeset it
262 268 # corresponds to was a full changeset.
263 269 if linknode in self.full_nodes:
264 270 for x in orig(self, revlog, rev, prev, linknode):
265 271 yield x
266 272 return
267 273 # At this point, a node can either be one we should skip or an
268 274 # ellipsis. If it's not an ellipsis, bail immediately.
269 275 if linkrev not in self.precomputed_ellipsis:
270 276 return
271 277 linkparents = self.precomputed_ellipsis[linkrev]
272 278 def local(clrev):
273 279 """Turn a changelog revnum into a local revnum.
274 280
275 281 The ellipsis dag is stored as revnums on the changelog,
276 282 but when we're producing ellipsis entries for
277 283 non-changelog revlogs, we need to turn those numbers into
278 284 something local. This does that for us, and during the
279 285 changelog sending phase will also expand the stored
280 286 mappings as needed.
281 287 """
282 288 if clrev == node.nullrev:
283 289 return node.nullrev
284 290 if not self.changelog_done:
285 291 # If we're doing the changelog, it's possible that we
286 292 # have a parent that is already on the client, and we
287 293 # need to store some extra mapping information so that
288 294 # our contained ellipsis nodes will be able to resolve
289 295 # their parents.
290 296 if clrev not in self.clrev_to_localrev:
291 297 clnode = revlog.node(clrev)
292 298 self.clnode_to_rev[clnode] = clrev
293 299 return clrev
294 300 # Walk the ellipsis-ized changelog breadth-first looking for a
295 301 # change that has been linked from the current revlog.
296 302 #
297 303 # For a flat manifest revlog only a single step should be necessary
298 304 # as all relevant changelog entries are relevant to the flat
299 305 # manifest.
300 306 #
301 307 # For a filelog or tree manifest dirlog however not every changelog
302 308 # entry will have been relevant, so we need to skip some changelog
303 309 # nodes even after ellipsis-izing.
304 310 walk = [clrev]
305 311 while walk:
306 312 p = walk[0]
307 313 walk = walk[1:]
308 314 if p in self.clrev_to_localrev:
309 315 return self.clrev_to_localrev[p]
310 316 elif p in self.full_nodes:
311 317 walk.extend([pp for pp in self._repo.changelog.parentrevs(p)
312 318 if pp != node.nullrev])
313 319 elif p in self.precomputed_ellipsis:
314 320 walk.extend([pp for pp in self.precomputed_ellipsis[p]
315 321 if pp != node.nullrev])
316 322 else:
317 323 # In this case, we've got an ellipsis with parents
318 324 # outside the current bundle (likely an
319 325 # incremental pull). We "know" that we can use the
320 326 # value of this same revlog at whatever revision
321 327 # is pointed to by linknode. "Know" is in scare
322 328 # quotes because I haven't done enough examination
323 329 # of edge cases to convince myself this is really
324 330 # a fact - it works for all the (admittedly
325 331 # thorough) cases in our testsuite, but I would be
326 332 # somewhat unsurprised to find a case in the wild
327 333 # where this breaks down a bit. That said, I don't
328 334 # know if it would hurt anything.
329 335 for i in xrange(rev, 0, -1):
330 336 if revlog.linkrev(i) == clrev:
331 337 return i
332 338 # We failed to resolve a parent for this node, so
333 339 # we crash the changegroup construction.
334 340 raise error.Abort(
335 341 'unable to resolve parent while packing %r %r'
336 342 ' for changeset %r' % (revlog.indexfile, rev, clrev))
337 343 return node.nullrev
338 344
339 345 if not linkparents or (
340 346 revlog.parentrevs(rev) == (node.nullrev, node.nullrev)):
341 347 p1, p2 = node.nullrev, node.nullrev
342 348 elif len(linkparents) == 1:
343 349 p1, = sorted(local(p) for p in linkparents)
344 350 p2 = node.nullrev
345 351 else:
346 352 p1, p2 = sorted(local(p) for p in linkparents)
347 353 yield ellipsisdata(
348 354 self, rev, revlog, p1, p2, revlog.revision(rev), linknode)
349 355 extensions.wrapfunction(changegroup.cg1packer, 'revchunk', revchunk)
350 356
351 357 def deltaparent(orig, self, revlog, rev, p1, p2, prev):
352 358 if util.safehasattr(self, 'full_nodes'):
353 359 # TODO: send better deltas when in narrow mode.
354 360 #
355 361 # changegroup.group() loops over revisions to send,
356 362 # including revisions we'll skip. What this means is that
357 363 # `prev` will be a potentially useless delta base for all
358 364 # ellipsis nodes, as the client likely won't have it. In
359 365 # the future we should do bookkeeping about which nodes
360 366 # have been sent to the client, and try to be
361 367 # significantly smarter about delta bases. This is
362 368 # slightly tricky because this same code has to work for
363 369 # all revlogs, and we don't have the linkrev/linknode here.
364 370 return p1
365 371 return orig(self, revlog, rev, p1, p2, prev)
366 372 extensions.wrapfunction(changegroup.cg2packer, 'deltaparent', deltaparent)
@@ -1,210 +1,209 b''
1 1
2 2 $ . "$TESTDIR/narrow-library.sh"
3 3
4 4 create full repo
5 5
6 6 $ hg init master
7 7 $ cd master
8 8 $ cat >> .hg/hgrc <<EOF
9 9 > [narrow]
10 10 > serveellipses=True
11 11 > EOF
12 12
13 13 $ mkdir inside
14 14 $ echo 1 > inside/f
15 15 $ mkdir inside2
16 16 $ echo 1 > inside2/f
17 17 $ mkdir outside
18 18 $ echo 1 > outside/f
19 19 $ hg ci -Aqm 'initial'
20 20
21 21 $ echo 2 > inside/f
22 22 $ hg ci -qm 'inside 2'
23 23
24 24 $ echo 2 > inside2/f
25 25 $ hg ci -qm 'inside2 2'
26 26
27 27 $ echo 2 > outside/f
28 28 $ hg ci -qm 'outside 2'
29 29
30 30 $ cd ..
31 31
32 32 $ hg clone --narrow ssh://user@dummy/master narrow --include inside
33 33 requesting all changes
34 34 adding changesets
35 35 adding manifests
36 36 adding file changes
37 37 added 3 changesets with 2 changes to 1 files
38 38 new changesets *:* (glob)
39 39 updating to branch default
40 40 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
41 41
42 42 $ hg clone --narrow ssh://user@dummy/master narrow2 --include inside --include inside2
43 43 requesting all changes
44 44 adding changesets
45 45 adding manifests
46 46 adding file changes
47 47 added 4 changesets with 4 changes to 2 files
48 48 new changesets *:* (glob)
49 49 updating to branch default
50 50 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
51 51
52 52 Can push to wider repo if change does not affect paths in wider repo that are
53 53 not also in narrower repo
54 54
55 55 $ cd narrow
56 56 $ echo 3 > inside/f
57 57 $ hg ci -m 'inside 3'
58 58 $ hg push ssh://user@dummy/narrow2
59 59 pushing to ssh://user@dummy/narrow2
60 60 searching for changes
61 61 remote: adding changesets
62 62 remote: adding manifests
63 63 remote: adding file changes
64 64 remote: added 1 changesets with 1 changes to 1 files
65 65
66 66 Can push to narrower repo if change affects only paths within remote's
67 67 narrow spec
68 68
69 69 $ cd ../narrow2
70 70 $ cat >> .hg/hgrc <<EOF
71 71 > [narrow]
72 72 > serveellipses=True
73 73 > EOF
74 74 $ hg co -r 'desc("inside 3")'
75 75 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
76 76 $ echo 4 > inside/f
77 77 $ hg ci -m 'inside 4'
78 78 $ hg push ssh://user@dummy/narrow
79 79 pushing to ssh://user@dummy/narrow
80 80 searching for changes
81 81 remote: adding changesets
82 82 remote: adding manifests
83 83 remote: adding file changes
84 84 remote: added 1 changesets with 1 changes to 1 files
85 85
86 86 Can push to narrow repo if change affects only paths outside remote's
87 87 narrow spec
88 88
89 89 $ echo 3 > inside2/f
90 90 $ hg ci -m 'inside2 3'
91 91 TODO: this should be successful
92 92 $ hg push ssh://user@dummy/narrow
93 93 pushing to ssh://user@dummy/narrow
94 94 searching for changes
95 95 remote: adding changesets
96 96 remote: adding manifests
97 97 remote: adding file changes
98 98 remote: transaction abort!
99 99 remote: rollback completed
100 100 remote: abort: data/inside2/f.i@4a1aa07735e6: unknown parent!
101 101 abort: stream ended unexpectedly (got 0 bytes, expected 4)
102 102 [255]
103 103
104 104 Can pull from wider repo if change affects only paths outside remote's
105 105 narrow spec
106 106 $ echo 4 > inside2/f
107 107 $ hg ci -m 'inside2 4'
108 108 $ hg log -G -T '{rev} {node|short} {files}\n'
109 109 @ 7 d78a96df731d inside2/f
110 110 |
111 111 o 6 8c26f5218962 inside2/f
112 112 |
113 113 o 5 ba3480e2f9de inside/f
114 114 |
115 115 o 4 4e5edd526618 inside/f
116 116 |
117 117 o 3 81e7e07b7ab0 outside/f
118 118 |
119 119 o 2 f3993b8c0c2b inside2/f
120 120 |
121 121 o 1 8cd66ca966b4 inside/f
122 122 |
123 123 o 0 c8057d6f53ab inside/f inside2/f outside/f
124 124
125 125 $ cd ../narrow
126 126 $ hg log -G -T '{rev} {node|short} {files}\n'
127 127 o 4 ba3480e2f9de inside/f
128 128 |
129 129 @ 3 4e5edd526618 inside/f
130 130 |
131 131 o 2 81e7e07b7ab0 outside/f
132 132 |
133 133 o 1 8cd66ca966b4 inside/f
134 134 |
135 135 o 0 c8057d6f53ab inside/f inside2/f outside/f
136 136
137 137 $ hg pull ssh://user@dummy/narrow2
138 138 pulling from ssh://user@dummy/narrow2
139 139 searching for changes
140 remote: abort: unable to resolve parent while packing 'data/inside2/f.i' 3 for changeset 5 (?)
141 140 adding changesets
142 remote: abort: unexpected error: unable to resolve parent while packing 'data/inside2/f.i' 3 for changeset 5
143 transaction abort!
144 rollback completed
145 abort: pull failed on remote
146 [255]
141 adding manifests
142 adding file changes
143 added 1 changesets with 0 changes to 0 files
144 new changesets d78a96df731d
145 (run 'hg update' to get a working copy)
147 146
148 147 Check that the resulting history is valid in the full repo
149 148
150 149 $ cd ../narrow2
151 150 $ hg push ssh://user@dummy/master
152 151 pushing to ssh://user@dummy/master
153 152 searching for changes
154 153 remote: adding changesets
155 154 remote: adding manifests
156 155 remote: adding file changes
157 156 remote: added 4 changesets with 4 changes to 2 files
158 157 $ cd ../master
159 158 $ hg verify
160 159 checking changesets
161 160 checking manifests
162 161 crosschecking files in changesets and manifests
163 162 checking files
164 163 3 files, 8 changesets, 10 total revisions
165 164
166 165 Can not push to wider repo if change affects paths in wider repo that are
167 166 not also in narrower repo
168 167 $ cd ../master
169 168 $ hg co -r 'desc("inside2 4")'
170 169 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
171 170 $ echo 5 > inside2/f
172 171 $ hg ci -m 'inside2 5'
173 172 $ hg log -G -T '{rev} {node|short} {files}\n'
174 173 @ 8 5970befb64ba inside2/f
175 174 |
176 175 o 7 d78a96df731d inside2/f
177 176 |
178 177 o 6 8c26f5218962 inside2/f
179 178 |
180 179 o 5 ba3480e2f9de inside/f
181 180 |
182 181 o 4 4e5edd526618 inside/f
183 182 |
184 183 o 3 81e7e07b7ab0 outside/f
185 184 |
186 185 o 2 f3993b8c0c2b inside2/f
187 186 |
188 187 o 1 8cd66ca966b4 inside/f
189 188 |
190 189 o 0 c8057d6f53ab inside/f inside2/f outside/f
191 190
192 191 $ cd ../narrow
193 192 $ hg pull
194 193 pulling from ssh://user@dummy/master
195 194 searching for changes
196 195 adding changesets
197 196 adding manifests
198 197 adding file changes
199 198 added 1 changesets with 0 changes to 0 files
200 199 new changesets * (glob)
201 200 (run 'hg update' to get a working copy)
202 201 TODO: this should tell the user that their narrow clone does not have the
203 202 necessary content to be able to push to the target
204 203 $ hg push ssh://user@dummy/narrow2
205 204 pushing to ssh://user@dummy/narrow2
206 205 searching for changes
207 remote has heads on branch 'default' that are not known locally: d78a96df731d
208 abort: push creates new remote head 5970befb64ba!
209 (pull and merge or see 'hg help push' for details about pushing new heads)
210 [255]
206 remote: adding changesets
207 remote: adding manifests
208 remote: adding file changes
209 remote: added 1 changesets with 0 changes to 0 files
General Comments 0
You need to be logged in to leave comments. Login now