##// END OF EJS Templates
discovery: add new set-based discovery...
Peter Arrenbrecht -
r14164:cb98fed5 default
parent child Browse files
Show More
@@ -0,0 +1,242 b''
1 # dagutil.py - dag utilities for mercurial
2 #
3 # Copyright 2010 Benoit Boissinot <bboissin@gmail.com>
4 # and Peter Arrenbrecht <peter@arrenbrecht.ch>
5 #
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
8
9 from node import nullrev
10
11
12 class basedag(object):
13 '''generic interface for DAGs
14
15 terms:
16 "ix" (short for index) identifies a nodes internally,
17 "id" identifies one externally.
18
19 All params are ixs unless explicitly suffixed otherwise.
20 Pluralized params are lists or sets.
21 '''
22
23 def __init__(self):
24 self._inverse = None
25
26 def nodeset(self):
27 '''set of all node idxs'''
28 raise NotImplementedError()
29
30 def heads(self):
31 '''list of head ixs'''
32 raise NotImplementedError()
33
34 def parents(self, ix):
35 '''list of parents ixs of ix'''
36 raise NotImplementedError()
37
38 def inverse(self):
39 '''inverse DAG, where parents becomes children, etc.'''
40 raise NotImplementedError()
41
42 def ancestorset(self, starts, stops=None):
43 '''set of all ancestors of starts (incl), but stop walk at stops (excl)'''
44 raise NotImplementedError()
45
46 def descendantset(self, starts, stops=None):
47 '''set of all descendants of starts (incl), but stop walk at stops (excl)'''
48 return self.inverse().ancestorset(starts, stops)
49
50 def headsetofconnecteds(self, ixs):
51 '''subset of connected list of ixs so that no node has a descendant in it
52
53 By "connected list" we mean that if an ancestor and a descendant are in
54 the list, then so is at least one path connecting them.'''
55 raise NotImplementedError()
56
57 def externalize(self, ix):
58 '''return a list of (or set if given a set) of node ids'''
59 return self._externalize(ix)
60
61 def externalizeall(self, ixs):
62 '''return a list of (or set if given a set) of node ids'''
63 ids = self._externalizeall(ixs)
64 if isinstance(ixs, set):
65 return set(ids)
66 return list(ids)
67
68 def internalize(self, id):
69 '''return a list of (or set if given a set) of node ixs'''
70 return self._internalize(id)
71
72 def internalizeall(self, ids, filterunknown=False):
73 '''return a list of (or set if given a set) of node ids'''
74 ixs = self._internalizeall(ids, filterunknown)
75 if isinstance(ids, set):
76 return set(ixs)
77 return list(ixs)
78
79
80 class genericdag(basedag):
81 '''generic implementations for DAGs'''
82
83 def ancestorset(self, starts, stops=None):
84 stops = stops and set(stops) or set()
85 seen = set()
86 pending = list(starts)
87 while pending:
88 n = pending.pop()
89 if n not in seen and n not in stops:
90 seen.add(n)
91 pending.extend(self.parents(n))
92 return seen
93
94 def headsetofconnecteds(self, ixs):
95 hds = set(ixs)
96 if not hds:
97 return hds
98 for n in ixs:
99 for p in self.parents(n):
100 hds.discard(p)
101 assert hds
102 return hds
103
104
105 class revlogbaseddag(basedag):
106 '''generic dag interface to a revlog'''
107
108 def __init__(self, revlog, nodeset):
109 basedag.__init__(self)
110 self._revlog = revlog
111 self._heads = None
112 self._nodeset = nodeset
113
114 def nodeset(self):
115 return self._nodeset
116
117 def heads(self):
118 if self._heads is None:
119 self._heads = self._getheads()
120 return self._heads
121
122 def _externalize(self, ix):
123 return self._revlog.index[ix][7]
124 def _externalizeall(self, ixs):
125 idx = self._revlog.index
126 return [idx[i][7] for i in ixs]
127
128 def _internalize(self, id):
129 ix = self._revlog.rev(id)
130 if ix == nullrev:
131 raise LookupError(id, self._revlog.indexfile, _('nullid'))
132 return ix
133 def _internalizeall(self, ids, filterunknown):
134 rl = self._revlog
135 if filterunknown:
136 return [r for r in map(rl.nodemap.get, ids)
137 if r is not None and r != nullrev]
138 return map(self._internalize, ids)
139
140
141 class revlogdag(revlogbaseddag):
142 '''dag interface to a revlog'''
143
144 def __init__(self, revlog):
145 revlogbaseddag.__init__(self, revlog, set(xrange(len(revlog))))
146
147 def _getheads(self):
148 return [r for r in self._revlog.headrevs() if r != nullrev]
149
150 def parents(self, ix):
151 rlog = self._revlog
152 idx = rlog.index
153 revdata = idx[ix]
154 prev = revdata[5]
155 if prev != nullrev:
156 prev2 = revdata[6]
157 if prev2 == nullrev:
158 return [prev]
159 return [prev, prev2]
160 prev2 = revdata[6]
161 if prev2 != nullrev:
162 return [prev2]
163 return []
164
165 def inverse(self):
166 if self._inverse is None:
167 self._inverse = inverserevlogdag(self)
168 return self._inverse
169
170 def ancestorset(self, starts, stops=None):
171 rlog = self._revlog
172 idx = rlog.index
173 stops = stops and set(stops) or set()
174 seen = set()
175 pending = list(starts)
176 while pending:
177 rev = pending.pop()
178 if rev not in seen and rev not in stops:
179 seen.add(rev)
180 revdata = idx[rev]
181 for i in [5, 6]:
182 prev = revdata[i]
183 if prev != nullrev:
184 pending.append(prev)
185 return seen
186
187 def headsetofconnecteds(self, ixs):
188 if not ixs:
189 return set()
190 rlog = self._revlog
191 idx = rlog.index
192 headrevs = set(ixs)
193 for rev in ixs:
194 revdata = idx[rev]
195 for i in [5, 6]:
196 prev = revdata[i]
197 if prev != nullrev:
198 headrevs.discard(prev)
199 assert headrevs
200 return headrevs
201
202
203 class inverserevlogdag(revlogbaseddag, genericdag):
204 '''inverse of an existing revlog dag; see revlogdag.inverse()'''
205
206 def __init__(self, orig):
207 revlogbaseddag.__init__(self, orig._revlog, orig._nodeset)
208 self._orig = orig
209 self._children = {}
210 self._roots = []
211 self._walkfrom = len(self._revlog) - 1
212
213 def _walkto(self, walkto):
214 rev = self._walkfrom
215 cs = self._children
216 roots = self._roots
217 idx = self._revlog.index
218 while rev >= walkto:
219 data = idx[rev]
220 isroot = True
221 for prev in [data[5], data[6]]: # parent revs
222 if prev != nullrev:
223 cs.setdefault(prev, []).append(rev)
224 isroot = False
225 if isroot:
226 roots.append(rev)
227 rev -= 1
228 self._walkfrom = rev - 1
229
230 def _getheads(self):
231 self._walkto(nullrev)
232 return self._roots
233
234 def parents(self, ix):
235 if ix is None:
236 return []
237 if ix <= self._walkfrom:
238 self._walkto(ix)
239 return self._children.get(ix, [])
240
241 def inverse(self):
242 return self._orig
@@ -0,0 +1,178 b''
1 # setdiscovery.py - improved discovery of common nodeset for mercurial
2 #
3 # Copyright 2010 Benoit Boissinot <bboissin@gmail.com>
4 # and Peter Arrenbrecht <peter@arrenbrecht.ch>
5 #
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
8
9 from node import nullid
10 from i18n import _
11 import random, collections, util, dagutil
12
13 def _updatesample(dag, nodes, sample, always, quicksamplesize=0):
14 # if nodes is empty we scan the entire graph
15 if nodes:
16 heads = dag.headsetofconnecteds(nodes)
17 else:
18 heads = dag.heads()
19 dist = {}
20 visit = collections.deque(heads)
21 seen = set()
22 factor = 1
23 while visit:
24 curr = visit.popleft()
25 if curr in seen:
26 continue
27 d = dist.setdefault(curr, 1)
28 if d > factor:
29 factor *= 2
30 if d == factor:
31 if curr not in always: # need this check for the early exit below
32 sample.add(curr)
33 if quicksamplesize and (len(sample) >= quicksamplesize):
34 return
35 seen.add(curr)
36 for p in dag.parents(curr):
37 if not nodes or p in nodes:
38 dist.setdefault(p, d + 1)
39 visit.append(p)
40
41 def _setupsample(dag, nodes, size):
42 if len(nodes) <= size:
43 return set(nodes), None, 0
44 always = set(dag.heads())
45 desiredlen = size - len(always)
46 if desiredlen <= 0:
47 # This could be bad if there are very many heads, all unknown to the
48 # server. We're counting on long request support here.
49 return always, None, desiredlen
50 return always, set(), desiredlen
51
52 def _takequicksample(dag, nodes, size, initial):
53 always, sample, desiredlen = _setupsample(dag, nodes, size)
54 if sample is None:
55 return always
56 if initial:
57 fromset = None
58 else:
59 fromset = nodes
60 _updatesample(dag, fromset, sample, always, quicksamplesize=desiredlen)
61 sample.update(always)
62 return sample
63
64 def _takefullsample(dag, nodes, size):
65 always, sample, desiredlen = _setupsample(dag, nodes, size)
66 if sample is None:
67 return always
68 # update from heads
69 _updatesample(dag, nodes, sample, always)
70 # update from roots
71 _updatesample(dag.inverse(), nodes, sample, always)
72 assert sample
73 if len(sample) > desiredlen:
74 sample = set(random.sample(sample, desiredlen))
75 elif len(sample) < desiredlen:
76 more = desiredlen - len(sample)
77 sample.update(random.sample(list(nodes - sample - always), more))
78 sample.update(always)
79 return sample
80
81 def findcommonheads(ui, local, remote,
82 initialsamplesize=100,
83 fullsamplesize=200,
84 abortwhenunrelated=True):
85 '''Return a tuple (common, anyincoming, remoteheads) used to identify missing
86 nodes from or in remote.
87
88 shortcutlocal determines whether we try use direct access to localrepo if
89 remote is actually local.
90 '''
91 roundtrips = 0
92 cl = local.changelog
93 dag = dagutil.revlogdag(cl)
94 nodes = dag.nodeset()
95
96 # early exit if we know all the specified server heads already
97 ui.debug("query 1; heads\n")
98 roundtrips += 1
99 srvheadhashes = remote.heads()
100
101 ## TODO We might want to request an additional random sample of the server's
102 ## nodes batched with the heads query here.
103
104 if cl.tip() == nullid:
105 if srvheadhashes != [nullid]:
106 return [nullid], True, srvheadhashes
107 return [nullid], False, []
108
109 # start actual discovery (we note this before the next "if" for compatibility
110 # reasons)
111 ui.status(_("searching for changes\n"))
112
113 srvheads = dag.internalizeall(srvheadhashes, filterunknown=True)
114 if len(srvheads) == len(srvheadhashes):
115 ui.note("all remote heads known locally\n")
116 return (srvheadhashes, False, srvheadhashes,)
117
118 # full blown discovery
119 undecided = nodes # own nodes where I don't know if the server knows them
120 common = set() # own nodes I know we both know
121 missing = set() # own nodes I know the server lacks
122
123 # treat remote heads as a first implicit sample response
124 common.update(dag.ancestorset(srvheads))
125 undecided.difference_update(common)
126 # use cheapish initial sample
127 if common:
128 ui.debug("taking initial sample\n")
129 sample = _takefullsample(dag, undecided, size=fullsamplesize)
130 else:
131 ui.debug("taking quick initial sample\n")
132 sample = _takequicksample(dag, nodes, size=initialsamplesize,
133 initial=True)
134
135 roundtrips += 1
136 ui.progress(_('searching'), roundtrips, unit=_('queries'))
137 ui.debug("query %i; still undecided: %i, sample size is: %i\n"
138 % (roundtrips, len(undecided), len(sample)))
139 # indices between sample and externalized version must match
140 sample = list(sample)
141 yesno = remote.known(dag.externalizeall(sample))
142
143 while undecided:
144 commoninsample = set(n for i, n in enumerate(sample) if yesno[i])
145 common.update(dag.ancestorset(commoninsample, common))
146
147 missinginsample = [n for i, n in enumerate(sample) if not yesno[i]]
148 missing.update(dag.descendantset(missinginsample, missing))
149
150 undecided.difference_update(missing)
151 undecided.difference_update(common)
152
153 if not undecided:
154 break
155
156 ui.note("sampling from both directions\n")
157 sample = _takefullsample(dag, undecided, size=fullsamplesize)
158
159 roundtrips += 1
160 ui.progress(_('searching'), roundtrips, unit=_('queries'))
161 ui.debug("query %i; still undecided: %i, sample size is: %i\n"
162 % (roundtrips, len(undecided), len(sample)))
163 # indices between sample and externalized version must match
164 sample = list(sample)
165 yesno = remote.known(dag.externalizeall(sample))
166
167 result = dag.headsetofconnecteds(common)
168 ui.progress(_('searching'), None)
169 ui.debug("%d total queries\n" % roundtrips)
170
171 if not result and srvheadhashes != [nullid]:
172 if abortwhenunrelated:
173 raise util.Abort(_("repository is unrelated"))
174 else:
175 ui.warn(_("warning: repository is unrelated\n"))
176 return (set([nullid]), True, srvheadhashes,)
177
178 return (dag.externalizeall(result), True, srvheadhashes,)
@@ -0,0 +1,271 b''
1
2 Function to test discovery between two repos in both directions, using both the local shortcut
3 (which is currently not activated by default) and the full remotable protocol:
4
5 $ testdesc() { # revs_a, revs_b, dagdesc
6 > if [ -e foo ]; then rm -rf foo; fi
7 > hg init foo
8 > cd foo
9 > hg debugbuilddag "$3"
10 > hg clone . a $1 --quiet
11 > hg clone . b $2 --quiet
12 > echo
13 > echo "% -- a -> b tree"
14 > hg -R a debugdiscovery b --verbose --old
15 > echo
16 > echo "% -- a -> b set"
17 > hg -R a debugdiscovery b --verbose --debug
18 > echo
19 > echo "% -- b -> a tree"
20 > hg -R b debugdiscovery a --verbose --old
21 > echo
22 > echo "% -- b -> a set"
23 > hg -R b debugdiscovery a --verbose --debug
24 > cd ..
25 > }
26
27
28 Small superset:
29
30 $ testdesc '-ra1 -ra2' '-rb1 -rb2 -rb3' '
31 > +2:f +1:a1:b1
32 > <f +4 :a2
33 > +5 :b2
34 > <f +3 :b3'
35
36 % -- a -> b tree
37 comparing with b
38 searching for changes
39 unpruned common: b5714e113bc0 66f7d451a68b 01241442b3c2
40 common heads: b5714e113bc0 01241442b3c2
41 local is subset
42
43 % -- a -> b set
44 comparing with b
45 query 1; heads
46 searching for changes
47 taking initial sample
48 searching: 2 queries
49 query 2; still undecided: 4, sample size is: 4
50 2 total queries
51 common heads: b5714e113bc0 01241442b3c2
52 local is subset
53
54 % -- b -> a tree
55 comparing with a
56 searching for changes
57 unpruned common: b5714e113bc0 01241442b3c2
58 common heads: b5714e113bc0 01241442b3c2
59 remote is subset
60
61 % -- b -> a set
62 comparing with a
63 query 1; heads
64 searching for changes
65 all remote heads known locally
66 common heads: b5714e113bc0 01241442b3c2
67 remote is subset
68
69
70 Many new:
71
72 $ testdesc '-ra1 -ra2' '-rb' '
73 > +2:f +3:a1 +3:b
74 > <f +30 :a2'
75
76 % -- a -> b tree
77 comparing with b
78 searching for changes
79 unpruned common: bebd167eb94d
80 common heads: bebd167eb94d
81
82 % -- a -> b set
83 comparing with b
84 query 1; heads
85 searching for changes
86 taking quick initial sample
87 searching: 2 queries
88 query 2; still undecided: 35, sample size is: 35
89 2 total queries
90 common heads: bebd167eb94d
91
92 % -- b -> a tree
93 comparing with a
94 searching for changes
95 unpruned common: bebd167eb94d 66f7d451a68b
96 common heads: bebd167eb94d
97
98 % -- b -> a set
99 comparing with a
100 query 1; heads
101 searching for changes
102 taking initial sample
103 searching: 2 queries
104 query 2; still undecided: 3, sample size is: 3
105 2 total queries
106 common heads: bebd167eb94d
107
108
109 Both sides many new with stub:
110
111 $ testdesc '-ra1 -ra2' '-rb' '
112 > +2:f +2:a1 +30 :b
113 > <f +30 :a2'
114
115 % -- a -> b tree
116 comparing with b
117 searching for changes
118 unpruned common: 2dc09a01254d
119 common heads: 2dc09a01254d
120
121 % -- a -> b set
122 comparing with b
123 query 1; heads
124 searching for changes
125 taking quick initial sample
126 searching: 2 queries
127 query 2; still undecided: 34, sample size is: 34
128 2 total queries
129 common heads: 2dc09a01254d
130
131 % -- b -> a tree
132 comparing with a
133 searching for changes
134 unpruned common: 66f7d451a68b 2dc09a01254d
135 common heads: 2dc09a01254d
136
137 % -- b -> a set
138 comparing with a
139 query 1; heads
140 searching for changes
141 taking initial sample
142 searching: 2 queries
143 query 2; still undecided: 30, sample size is: 30
144 2 total queries
145 common heads: 2dc09a01254d
146
147
148 Both many new:
149
150 $ testdesc '-ra' '-rb' '
151 > +2:f +30 :b
152 > <f +30 :a'
153
154 % -- a -> b tree
155 comparing with b
156 searching for changes
157 unpruned common: 66f7d451a68b
158 common heads: 66f7d451a68b
159
160 % -- a -> b set
161 comparing with b
162 query 1; heads
163 searching for changes
164 taking quick initial sample
165 searching: 2 queries
166 query 2; still undecided: 32, sample size is: 32
167 2 total queries
168 common heads: 66f7d451a68b
169
170 % -- b -> a tree
171 comparing with a
172 searching for changes
173 unpruned common: 66f7d451a68b
174 common heads: 66f7d451a68b
175
176 % -- b -> a set
177 comparing with a
178 query 1; heads
179 searching for changes
180 taking quick initial sample
181 searching: 2 queries
182 query 2; still undecided: 32, sample size is: 32
183 2 total queries
184 common heads: 66f7d451a68b
185
186
187 Both many new skewed:
188
189 $ testdesc '-ra' '-rb' '
190 > +2:f +30 :b
191 > <f +50 :a'
192
193 % -- a -> b tree
194 comparing with b
195 searching for changes
196 unpruned common: 66f7d451a68b
197 common heads: 66f7d451a68b
198
199 % -- a -> b set
200 comparing with b
201 query 1; heads
202 searching for changes
203 taking quick initial sample
204 searching: 2 queries
205 query 2; still undecided: 52, sample size is: 52
206 2 total queries
207 common heads: 66f7d451a68b
208
209 % -- b -> a tree
210 comparing with a
211 searching for changes
212 unpruned common: 66f7d451a68b
213 common heads: 66f7d451a68b
214
215 % -- b -> a set
216 comparing with a
217 query 1; heads
218 searching for changes
219 taking quick initial sample
220 searching: 2 queries
221 query 2; still undecided: 32, sample size is: 32
222 2 total queries
223 common heads: 66f7d451a68b
224
225
226 Both many new on top of long history:
227
228 $ testdesc '-ra' '-rb' '
229 > +1000:f +30 :b
230 > <f +50 :a'
231
232 % -- a -> b tree
233 comparing with b
234 searching for changes
235 unpruned common: 7ead0cba2838
236 common heads: 7ead0cba2838
237
238 % -- a -> b set
239 comparing with b
240 query 1; heads
241 searching for changes
242 taking quick initial sample
243 searching: 2 queries
244 query 2; still undecided: 1050, sample size is: 11
245 sampling from both directions
246 searching: 3 queries
247 query 3; still undecided: 31, sample size is: 31
248 3 total queries
249 common heads: 7ead0cba2838
250
251 % -- b -> a tree
252 comparing with a
253 searching for changes
254 unpruned common: 7ead0cba2838
255 common heads: 7ead0cba2838
256
257 % -- b -> a set
258 comparing with a
259 query 1; heads
260 searching for changes
261 taking quick initial sample
262 searching: 2 queries
263 query 2; still undecided: 1030, sample size is: 11
264 sampling from both directions
265 searching: 3 queries
266 query 3; still undecided: 16, sample size is: 16
267 3 total queries
268 common heads: 7ead0cba2838
269
270
271
@@ -15,6 +15,7 b' import archival, changegroup, cmdutil, s'
15 import merge as mergemod
15 import merge as mergemod
16 import minirst, revset, templatefilters
16 import minirst, revset, templatefilters
17 import dagparser, context, simplemerge
17 import dagparser, context, simplemerge
18 import random, setdiscovery, treediscovery, dagutil
18
19
19 # Commands start here, listed alphabetically
20 # Commands start here, listed alphabetically
20
21
@@ -1471,6 +1472,65 b' def debugignore(ui, repo, *values, **opt'
1471 else:
1472 else:
1472 raise util.Abort(_("no ignore patterns found"))
1473 raise util.Abort(_("no ignore patterns found"))
1473
1474
1475 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1476 """runs the changeset discovery protocol in isolation"""
1477 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1478 remote = hg.repository(hg.remoteui(repo, opts), remoteurl)
1479 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1480
1481 # make sure tests are repeatable
1482 random.seed(12323)
1483
1484 def doit(localheads, remoteheads):
1485 if opts.get('old'):
1486 if localheads:
1487 raise util.Abort('cannot use localheads with old style discovery')
1488 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1489 force=True)
1490 common = set(common)
1491 if not opts.get('nonheads'):
1492 ui.write("unpruned common: %s\n" % " ".join([short(n)
1493 for n in common]))
1494 dag = dagutil.revlogdag(repo.changelog)
1495 all = dag.ancestorset(dag.internalizeall(common))
1496 common = dag.externalizeall(dag.headsetofconnecteds(all))
1497 else:
1498 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1499 common = set(common)
1500 rheads = set(hds)
1501 lheads = set(repo.heads())
1502 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1503 if lheads <= common:
1504 ui.write("local is subset\n")
1505 elif rheads <= common:
1506 ui.write("remote is subset\n")
1507
1508 serverlogs = opts.get('serverlog')
1509 if serverlogs:
1510 for filename in serverlogs:
1511 logfile = open(filename, 'r')
1512 try:
1513 line = logfile.readline()
1514 while line:
1515 parts = line.strip().split(';')
1516 op = parts[1]
1517 if op == 'cg':
1518 pass
1519 elif op == 'cgss':
1520 doit(parts[2].split(' '), parts[3].split(' '))
1521 elif op == 'unb':
1522 doit(parts[3].split(' '), parts[2].split(' '))
1523 line = logfile.readline()
1524 finally:
1525 logfile.close()
1526
1527 else:
1528 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1529 opts.get('remote_head'))
1530 localrevs = opts.get('local_head')
1531 doit(localrevs, remoterevs)
1532
1533
1474 def debugindex(ui, repo, file_, **opts):
1534 def debugindex(ui, repo, file_, **opts):
1475 """dump the contents of an index file"""
1535 """dump the contents of an index file"""
1476 r = None
1536 r = None
@@ -4513,6 +4573,14 b' table = {'
4513 [('e', 'extended', None, _('try extended date formats'))],
4573 [('e', 'extended', None, _('try extended date formats'))],
4514 _('[-e] DATE [RANGE]')),
4574 _('[-e] DATE [RANGE]')),
4515 "debugdata": (debugdata, [], _('FILE REV')),
4575 "debugdata": (debugdata, [], _('FILE REV')),
4576 "debugdiscovery": (debugdiscovery,
4577 [('', 'old', None,
4578 _('use old-style discovery')),
4579 ('', 'nonheads', None,
4580 _('use old-style discovery with non-heads included')),
4581 ] + remoteopts,
4582 _('[-l REV] [-r REV] [-b BRANCH]...'
4583 ' [OTHER]')),
4516 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4584 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4517 "debuggetbundle":
4585 "debuggetbundle":
4518 (debuggetbundle,
4586 (debuggetbundle,
@@ -7,7 +7,7 b''
7
7
8 from node import nullid, short
8 from node import nullid, short
9 from i18n import _
9 from i18n import _
10 import util, error
10 import util, error, setdiscovery, treediscovery
11
11
12 def findcommonincoming(repo, remote, heads=None, force=False):
12 def findcommonincoming(repo, remote, heads=None, force=False):
13 """Return a tuple (common, anyincoming, heads) used to identify the common
13 """Return a tuple (common, anyincoming, heads) used to identify the common
@@ -20,145 +20,28 b' def findcommonincoming(repo, remote, hea'
20 changegroupsubset. No code except for pull should be relying on this fact
20 changegroupsubset. No code except for pull should be relying on this fact
21 any longer.
21 any longer.
22 "heads" is either the supplied heads, or else the remote's heads.
22 "heads" is either the supplied heads, or else the remote's heads.
23
24 If you pass heads and they are all known locally, the reponse lists justs
25 these heads in "common" and in "heads".
23 """
26 """
24
27
25 m = repo.changelog.nodemap
28 if not remote.capable('getbundle'):
26 search = []
29 return treediscovery.findcommonincoming(repo, remote, heads, force)
27 fetch = set()
28 seen = set()
29 seenbranch = set()
30 base = set()
31
32 if not heads:
33 heads = remote.heads()
34
35 if repo.changelog.tip() == nullid:
36 base.add(nullid)
37 if heads != [nullid]:
38 return [nullid], [nullid], list(heads)
39 return [nullid], [], []
40
41 # assume we're closer to the tip than the root
42 # and start by examining the heads
43 repo.ui.status(_("searching for changes\n"))
44
45 if remote.capable('getbundle'):
46 myheads = repo.heads()
47 known = remote.known(myheads)
48 if util.all(known):
49 hasincoming = set(heads).difference(set(myheads)) and True
50 return myheads, hasincoming, heads
51
52 unknown = []
53 for h in heads:
54 if h not in m:
55 unknown.append(h)
56 else:
57 base.add(h)
58
59 heads = unknown
60 if not unknown:
61 return list(base), [], []
62
63 req = set(unknown)
64 reqcnt = 0
65
66 # search through remote branches
67 # a 'branch' here is a linear segment of history, with four parts:
68 # head, root, first parent, second parent
69 # (a branch always has two parents (or none) by definition)
70 unknown = remote.branches(unknown)
71 while unknown:
72 r = []
73 while unknown:
74 n = unknown.pop(0)
75 if n[0] in seen:
76 continue
77
30
78 repo.ui.debug("examining %s:%s\n"
31 if heads:
79 % (short(n[0]), short(n[1])))
32 allknown = True
80 if n[0] == nullid: # found the end of the branch
33 nm = repo.changelog.nodemap
81 pass
34 for h in heads:
82 elif n in seenbranch:
35 if nm.get(h) is None:
83 repo.ui.debug("branch already found\n")
36 allknown = False
84 continue
37 break
85 elif n[1] and n[1] in m: # do we know the base?
38 if allknown:
86 repo.ui.debug("found incomplete branch %s:%s\n"
39 return (heads, False, heads)
87 % (short(n[0]), short(n[1])))
88 search.append(n[0:2]) # schedule branch range for scanning
89 seenbranch.add(n)
90 else:
91 if n[1] not in seen and n[1] not in fetch:
92 if n[2] in m and n[3] in m:
93 repo.ui.debug("found new changeset %s\n" %
94 short(n[1]))
95 fetch.add(n[1]) # earliest unknown
96 for p in n[2:4]:
97 if p in m:
98 base.add(p) # latest known
99
100 for p in n[2:4]:
101 if p not in req and p not in m:
102 r.append(p)
103 req.add(p)
104 seen.add(n[0])
105
106 if r:
107 reqcnt += 1
108 repo.ui.progress(_('searching'), reqcnt, unit=_('queries'))
109 repo.ui.debug("request %d: %s\n" %
110 (reqcnt, " ".join(map(short, r))))
111 for p in xrange(0, len(r), 10):
112 for b in remote.branches(r[p:p + 10]):
113 repo.ui.debug("received %s:%s\n" %
114 (short(b[0]), short(b[1])))
115 unknown.append(b)
116
40
117 # do binary search on the branches we found
41 res = setdiscovery.findcommonheads(repo.ui, repo, remote,
118 while search:
42 abortwhenunrelated=not force)
119 newsearch = []
43 common, anyinc, srvheads = res
120 reqcnt += 1
44 return (list(common), anyinc, heads or list(srvheads))
121 repo.ui.progress(_('searching'), reqcnt, unit=_('queries'))
122 for n, l in zip(search, remote.between(search)):
123 l.append(n[1])
124 p = n[0]
125 f = 1
126 for i in l:
127 repo.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
128 if i in m:
129 if f <= 2:
130 repo.ui.debug("found new branch changeset %s\n" %
131 short(p))
132 fetch.add(p)
133 base.add(i)
134 else:
135 repo.ui.debug("narrowed branch search to %s:%s\n"
136 % (short(p), short(i)))
137 newsearch.append((p, i))
138 break
139 p, f = i, f * 2
140 search = newsearch
141
142 # sanity check our fetch list
143 for f in fetch:
144 if f in m:
145 raise error.RepoError(_("already have changeset ")
146 + short(f[:4]))
147
148 base = list(base)
149 if base == [nullid]:
150 if force:
151 repo.ui.warn(_("warning: repository is unrelated\n"))
152 else:
153 raise util.Abort(_("repository is unrelated"))
154
155 repo.ui.debug("found new changesets starting at " +
156 " ".join([short(f) for f in fetch]) + "\n")
157
158 repo.ui.progress(_('searching'), None)
159 repo.ui.debug("%d total queries\n" % reqcnt)
160
161 return base, list(fetch), heads
162
45
163 def prepush(repo, remote, force, revs, newbranch):
46 def prepush(repo, remote, force, revs, newbranch):
164 '''Analyze the local and remote repositories and determine which
47 '''Analyze the local and remote repositories and determine which
@@ -174,9 +57,7 b' def prepush(repo, remote, force, revs, n'
174 changegroup is a readable file-like object whose read() returns
57 changegroup is a readable file-like object whose read() returns
175 successive changegroup chunks ready to be sent over the wire and
58 successive changegroup chunks ready to be sent over the wire and
176 remoteheads is the list of remote heads.'''
59 remoteheads is the list of remote heads.'''
177 remoteheads = remote.heads()
60 common, inc, remoteheads = findcommonincoming(repo, remote, force=force)
178 common, inc, _rheads = findcommonincoming(repo, remote, heads=remoteheads,
179 force=force)
180
61
181 cl = repo.changelog
62 cl = repo.changelog
182 outg = cl.findmissing(common, revs)
63 outg = cl.findmissing(common, revs)
@@ -617,6 +617,17 b' class revlog(object):'
617 assert heads
617 assert heads
618 return (orderedout, roots, heads)
618 return (orderedout, roots, heads)
619
619
620 def headrevs(self):
621 count = len(self)
622 if not count:
623 return [nullrev]
624 ishead = [1] * (count + 1)
625 index = self.index
626 for r in xrange(count):
627 e = index[r]
628 ishead[e[5]] = ishead[e[6]] = 0
629 return [r for r in xrange(count) if ishead[r]]
630
620 def heads(self, start=None, stop=None):
631 def heads(self, start=None, stop=None):
621 """return the list of all nodes that have no children
632 """return the list of all nodes that have no children
622
633
@@ -626,15 +637,9 b' class revlog(object):'
626 as if they had no children
637 as if they had no children
627 """
638 """
628 if start is None and stop is None:
639 if start is None and stop is None:
629 count = len(self)
640 if not len(self):
630 if not count:
631 return [nullid]
641 return [nullid]
632 ishead = [1] * (count + 1)
642 return [self.node(r) for r in self.headrevs()]
633 index = self.index
634 for r in xrange(count):
635 e = index[r]
636 ishead[e[5]] = ishead[e[6]] = 0
637 return [self.node(r) for r in xrange(count) if ishead[r]]
638
643
639 if start is None:
644 if start is None:
640 start = nullid
645 start = nullid
@@ -10,15 +10,12 b' from i18n import _'
10 import util, error
10 import util, error
11
11
12 def findcommonincoming(repo, remote, heads=None, force=False):
12 def findcommonincoming(repo, remote, heads=None, force=False):
13 """Return a tuple (common, anyincoming, heads) used to identify the common
13 """Return a tuple (common, fetch, heads) used to identify the common
14 subset of nodes between repo and remote.
14 subset of nodes between repo and remote.
15
15
16 "common" is a list of (at least) the heads of the common subset.
16 "common" is a list of (at least) the heads of the common subset.
17 "anyincoming" is testable as a boolean indicating if any nodes are missing
17 "fetch" is a list of roots of the nodes that would be incoming, to be
18 locally. If remote does not support getbundle, this actually is a list of
18 supplied to changegroupsubset.
19 roots of the nodes that would be incoming, to be supplied to
20 changegroupsubset. No code except for pull should be relying on this fact
21 any longer.
22 "heads" is either the supplied heads, or else the remote's heads.
19 "heads" is either the supplied heads, or else the remote's heads.
23 """
20 """
24
21
@@ -42,13 +39,6 b' def findcommonincoming(repo, remote, hea'
42 # and start by examining the heads
39 # and start by examining the heads
43 repo.ui.status(_("searching for changes\n"))
40 repo.ui.status(_("searching for changes\n"))
44
41
45 if remote.capable('getbundle'):
46 myheads = repo.heads()
47 known = remote.known(myheads)
48 if util.all(known):
49 hasincoming = set(heads).difference(set(myheads)) and True
50 return myheads, hasincoming, heads
51
52 unknown = []
42 unknown = []
53 for h in heads:
43 for h in heads:
54 if h not in m:
44 if h not in m:
@@ -159,130 +149,3 b' def findcommonincoming(repo, remote, hea'
159 repo.ui.debug("%d total queries\n" % reqcnt)
149 repo.ui.debug("%d total queries\n" % reqcnt)
160
150
161 return base, list(fetch), heads
151 return base, list(fetch), heads
162
163 def prepush(repo, remote, force, revs, newbranch):
164 '''Analyze the local and remote repositories and determine which
165 changesets need to be pushed to the remote. Return value depends
166 on circumstances:
167
168 If we are not going to push anything, return a tuple (None,
169 outgoing) where outgoing is 0 if there are no outgoing
170 changesets and 1 if there are, but we refuse to push them
171 (e.g. would create new remote heads).
172
173 Otherwise, return a tuple (changegroup, remoteheads), where
174 changegroup is a readable file-like object whose read() returns
175 successive changegroup chunks ready to be sent over the wire and
176 remoteheads is the list of remote heads.'''
177 remoteheads = remote.heads()
178 common, inc, _rheads = findcommonincoming(repo, remote, heads=remoteheads,
179 force=force)
180
181 cl = repo.changelog
182 outg = cl.findmissing(common, revs)
183
184 if not outg:
185 repo.ui.status(_("no changes found\n"))
186 return None, 1
187
188 if not force and remoteheads != [nullid]:
189 if remote.capable('branchmap'):
190 # Check for each named branch if we're creating new remote heads.
191 # To be a remote head after push, node must be either:
192 # - unknown locally
193 # - a local outgoing head descended from update
194 # - a remote head that's known locally and not
195 # ancestral to an outgoing head
196
197 # 1. Create set of branches involved in the push.
198 branches = set(repo[n].branch() for n in outg)
199
200 # 2. Check for new branches on the remote.
201 remotemap = remote.branchmap()
202 newbranches = branches - set(remotemap)
203 if newbranches and not newbranch: # new branch requires --new-branch
204 branchnames = ', '.join(sorted(newbranches))
205 raise util.Abort(_("push creates new remote branches: %s!")
206 % branchnames,
207 hint=_("use 'hg push --new-branch' to create"
208 " new remote branches"))
209 branches.difference_update(newbranches)
210
211 # 3. Construct the initial oldmap and newmap dicts.
212 # They contain information about the remote heads before and
213 # after the push, respectively.
214 # Heads not found locally are not included in either dict,
215 # since they won't be affected by the push.
216 # unsynced contains all branches with incoming changesets.
217 oldmap = {}
218 newmap = {}
219 unsynced = set()
220 for branch in branches:
221 remotebrheads = remotemap[branch]
222 prunedbrheads = [h for h in remotebrheads if h in cl.nodemap]
223 oldmap[branch] = prunedbrheads
224 newmap[branch] = list(prunedbrheads)
225 if len(remotebrheads) > len(prunedbrheads):
226 unsynced.add(branch)
227
228 # 4. Update newmap with outgoing changes.
229 # This will possibly add new heads and remove existing ones.
230 ctxgen = (repo[n] for n in outg)
231 repo._updatebranchcache(newmap, ctxgen)
232
233 else:
234 # 1-4b. old servers: Check for new topological heads.
235 # Construct {old,new}map with branch = None (topological branch).
236 # (code based on _updatebranchcache)
237 oldheads = set(h for h in remoteheads if h in cl.nodemap)
238 newheads = oldheads.union(outg)
239 if len(newheads) > 1:
240 for latest in reversed(outg):
241 if latest not in newheads:
242 continue
243 minhrev = min(cl.rev(h) for h in newheads)
244 reachable = cl.reachable(latest, cl.node(minhrev))
245 reachable.remove(latest)
246 newheads.difference_update(reachable)
247 branches = set([None])
248 newmap = {None: newheads}
249 oldmap = {None: oldheads}
250 unsynced = inc and branches or set()
251
252 # 5. Check for new heads.
253 # If there are more heads after the push than before, a suitable
254 # error message, depending on unsynced status, is displayed.
255 error = None
256 for branch in branches:
257 newhs = set(newmap[branch])
258 oldhs = set(oldmap[branch])
259 if len(newhs) > len(oldhs):
260 if error is None:
261 if branch:
262 error = _("push creates new remote heads "
263 "on branch '%s'!") % branch
264 else:
265 error = _("push creates new remote heads!")
266 if branch in unsynced:
267 hint = _("you should pull and merge or "
268 "use push -f to force")
269 else:
270 hint = _("did you forget to merge? "
271 "use push -f to force")
272 if branch:
273 repo.ui.debug("new remote heads on branch '%s'\n" % branch)
274 for h in (newhs - oldhs):
275 repo.ui.debug("new remote head %s\n" % short(h))
276 if error:
277 raise util.Abort(error, hint=hint)
278
279 # 6. Check for unsynced changes on involved branches.
280 if unsynced:
281 repo.ui.warn(_("note: unsynced remote changes!\n"))
282
283 if revs is None:
284 # use the fast path, no race possible on push
285 cg = repo._changegroup(outg, 'push')
286 else:
287 cg = repo.getbundle('push', heads=revs, common=common)
288 return cg, remoteheads
@@ -82,7 +82,9 b' Extension disabled for lack of a hook'
82 hgrc = """
82 hgrc = """
83 """
83 """
84 pushing to ../b
84 pushing to ../b
85 query 1; heads
85 searching for changes
86 searching for changes
87 all remote heads known locally
86 3 changesets found
88 3 changesets found
87 list of changesets:
89 list of changesets:
88 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
90 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -135,7 +137,9 b' Extension disabled for lack of acl.sourc'
135 pretxnchangegroup.acl = python:hgext.acl.hook
137 pretxnchangegroup.acl = python:hgext.acl.hook
136 """
138 """
137 pushing to ../b
139 pushing to ../b
140 query 1; heads
138 searching for changes
141 searching for changes
142 all remote heads known locally
139 invalidating branch cache (tip differs)
143 invalidating branch cache (tip differs)
140 3 changesets found
144 3 changesets found
141 list of changesets:
145 list of changesets:
@@ -192,7 +196,9 b' No [acl.allow]/[acl.deny]'
192 sources = push
196 sources = push
193 """
197 """
194 pushing to ../b
198 pushing to ../b
199 query 1; heads
195 searching for changes
200 searching for changes
201 all remote heads known locally
196 invalidating branch cache (tip differs)
202 invalidating branch cache (tip differs)
197 3 changesets found
203 3 changesets found
198 list of changesets:
204 list of changesets:
@@ -258,7 +264,9 b' Empty [acl.allow]'
258 [acl.allow]
264 [acl.allow]
259 """
265 """
260 pushing to ../b
266 pushing to ../b
267 query 1; heads
261 searching for changes
268 searching for changes
269 all remote heads known locally
262 invalidating branch cache (tip differs)
270 invalidating branch cache (tip differs)
263 3 changesets found
271 3 changesets found
264 list of changesets:
272 list of changesets:
@@ -322,7 +330,9 b' fred is allowed inside foo/'
322 foo/** = fred
330 foo/** = fred
323 """
331 """
324 pushing to ../b
332 pushing to ../b
333 query 1; heads
325 searching for changes
334 searching for changes
335 all remote heads known locally
326 3 changesets found
336 3 changesets found
327 list of changesets:
337 list of changesets:
328 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
338 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -390,7 +400,9 b' Empty [acl.deny]'
390 [acl.deny]
400 [acl.deny]
391 """
401 """
392 pushing to ../b
402 pushing to ../b
403 query 1; heads
393 searching for changes
404 searching for changes
405 all remote heads known locally
394 3 changesets found
406 3 changesets found
395 list of changesets:
407 list of changesets:
396 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
408 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -455,7 +467,9 b' fred is allowed inside foo/, but not foo'
455 foo/bar/** = fred
467 foo/bar/** = fred
456 """
468 """
457 pushing to ../b
469 pushing to ../b
470 query 1; heads
458 searching for changes
471 searching for changes
472 all remote heads known locally
459 3 changesets found
473 3 changesets found
460 list of changesets:
474 list of changesets:
461 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
475 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -525,7 +539,9 b' fred is allowed inside foo/, but not foo'
525 foo/Bar/** = fred
539 foo/Bar/** = fred
526 """
540 """
527 pushing to ../b
541 pushing to ../b
542 query 1; heads
528 searching for changes
543 searching for changes
544 all remote heads known locally
529 3 changesets found
545 3 changesets found
530 list of changesets:
546 list of changesets:
531 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
547 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -592,7 +608,9 b' fred is allowed inside foo/, but not foo'
592 foo/Bar/** = fred
608 foo/Bar/** = fred
593 """
609 """
594 pushing to ../b
610 pushing to ../b
611 query 1; heads
595 searching for changes
612 searching for changes
613 all remote heads known locally
596 3 changesets found
614 3 changesets found
597 list of changesets:
615 list of changesets:
598 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
616 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -661,7 +679,9 b' barney is allowed everywhere'
661 ** = barney
679 ** = barney
662 """
680 """
663 pushing to ../b
681 pushing to ../b
682 query 1; heads
664 searching for changes
683 searching for changes
684 all remote heads known locally
665 3 changesets found
685 3 changesets found
666 list of changesets:
686 list of changesets:
667 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
687 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -733,7 +753,9 b' wilma can change files with a .txt exten'
733 **/*.txt = wilma
753 **/*.txt = wilma
734 """
754 """
735 pushing to ../b
755 pushing to ../b
756 query 1; heads
736 searching for changes
757 searching for changes
758 all remote heads known locally
737 invalidating branch cache (tip differs)
759 invalidating branch cache (tip differs)
738 3 changesets found
760 3 changesets found
739 list of changesets:
761 list of changesets:
@@ -810,7 +832,9 b' file specified by acl.config does not ex'
810 config = ../acl.config
832 config = ../acl.config
811 """
833 """
812 pushing to ../b
834 pushing to ../b
835 query 1; heads
813 searching for changes
836 searching for changes
837 all remote heads known locally
814 3 changesets found
838 3 changesets found
815 list of changesets:
839 list of changesets:
816 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
840 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -880,7 +904,9 b' betty is allowed inside foo/ by a acl.co'
880 foo/** = betty
904 foo/** = betty
881 """
905 """
882 pushing to ../b
906 pushing to ../b
907 query 1; heads
883 searching for changes
908 searching for changes
909 all remote heads known locally
884 3 changesets found
910 3 changesets found
885 list of changesets:
911 list of changesets:
886 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
912 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -962,7 +988,9 b' acl.config can set only [acl.allow]/[acl'
962 changegroup.acl = false
988 changegroup.acl = false
963 """
989 """
964 pushing to ../b
990 pushing to ../b
991 query 1; heads
965 searching for changes
992 searching for changes
993 all remote heads known locally
966 3 changesets found
994 3 changesets found
967 list of changesets:
995 list of changesets:
968 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
996 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -1035,7 +1063,9 b' fred is always allowed'
1035 ** = fred
1063 ** = fred
1036 """
1064 """
1037 pushing to ../b
1065 pushing to ../b
1066 query 1; heads
1038 searching for changes
1067 searching for changes
1068 all remote heads known locally
1039 invalidating branch cache (tip differs)
1069 invalidating branch cache (tip differs)
1040 3 changesets found
1070 3 changesets found
1041 list of changesets:
1071 list of changesets:
@@ -1105,7 +1135,9 b' no one is allowed inside foo/Bar/'
1105 foo/Bar/** = *
1135 foo/Bar/** = *
1106 """
1136 """
1107 pushing to ../b
1137 pushing to ../b
1138 query 1; heads
1108 searching for changes
1139 searching for changes
1140 all remote heads known locally
1109 invalidating branch cache (tip differs)
1141 invalidating branch cache (tip differs)
1110 3 changesets found
1142 3 changesets found
1111 list of changesets:
1143 list of changesets:
@@ -1178,7 +1210,9 b' OS-level groups'
1178 ** = @group1
1210 ** = @group1
1179 """
1211 """
1180 pushing to ../b
1212 pushing to ../b
1213 query 1; heads
1181 searching for changes
1214 searching for changes
1215 all remote heads known locally
1182 3 changesets found
1216 3 changesets found
1183 list of changesets:
1217 list of changesets:
1184 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1218 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -1248,7 +1282,9 b' OS-level groups'
1248 foo/Bar/** = @group1
1282 foo/Bar/** = @group1
1249 """
1283 """
1250 pushing to ../b
1284 pushing to ../b
1285 query 1; heads
1251 searching for changes
1286 searching for changes
1287 all remote heads known locally
1252 invalidating branch cache (tip differs)
1288 invalidating branch cache (tip differs)
1253 3 changesets found
1289 3 changesets found
1254 list of changesets:
1290 list of changesets:
@@ -1359,7 +1395,9 b' No branch acls specified'
1359 [extensions]
1395 [extensions]
1360 """
1396 """
1361 pushing to ../b
1397 pushing to ../b
1398 query 1; heads
1362 searching for changes
1399 searching for changes
1400 all remote heads known locally
1363 4 changesets found
1401 4 changesets found
1364 list of changesets:
1402 list of changesets:
1365 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1403 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -1436,7 +1474,9 b' Branch acl deny test'
1436 foobar = *
1474 foobar = *
1437 """
1475 """
1438 pushing to ../b
1476 pushing to ../b
1477 query 1; heads
1439 searching for changes
1478 searching for changes
1479 all remote heads known locally
1440 invalidating branch cache (tip differs)
1480 invalidating branch cache (tip differs)
1441 4 changesets found
1481 4 changesets found
1442 list of changesets:
1482 list of changesets:
@@ -1512,7 +1552,9 b' Branch acl empty allow test'
1512 [acl.allow.branches]
1552 [acl.allow.branches]
1513 """
1553 """
1514 pushing to ../b
1554 pushing to ../b
1555 query 1; heads
1515 searching for changes
1556 searching for changes
1557 all remote heads known locally
1516 4 changesets found
1558 4 changesets found
1517 list of changesets:
1559 list of changesets:
1518 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1560 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -1583,7 +1625,9 b' Branch acl allow other'
1583 * = george
1625 * = george
1584 """
1626 """
1585 pushing to ../b
1627 pushing to ../b
1628 query 1; heads
1586 searching for changes
1629 searching for changes
1630 all remote heads known locally
1587 4 changesets found
1631 4 changesets found
1588 list of changesets:
1632 list of changesets:
1589 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1633 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -1648,7 +1692,9 b' Branch acl allow other'
1648 * = george
1692 * = george
1649 """
1693 """
1650 pushing to ../b
1694 pushing to ../b
1695 query 1; heads
1651 searching for changes
1696 searching for changes
1697 all remote heads known locally
1652 4 changesets found
1698 4 changesets found
1653 list of changesets:
1699 list of changesets:
1654 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1700 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
@@ -1730,7 +1776,9 b' push foobar into the remote'
1730 * = george
1776 * = george
1731 """
1777 """
1732 pushing to ../b
1778 pushing to ../b
1779 query 1; heads
1733 searching for changes
1780 searching for changes
1781 all remote heads known locally
1734 invalidating branch cache (tip differs)
1782 invalidating branch cache (tip differs)
1735 4 changesets found
1783 4 changesets found
1736 list of changesets:
1784 list of changesets:
@@ -1812,7 +1860,9 b' Branch acl conflicting deny'
1812 * = george
1860 * = george
1813 """
1861 """
1814 pushing to ../b
1862 pushing to ../b
1863 query 1; heads
1815 searching for changes
1864 searching for changes
1865 all remote heads known locally
1816 invalidating branch cache (tip differs)
1866 invalidating branch cache (tip differs)
1817 4 changesets found
1867 4 changesets found
1818 list of changesets:
1868 list of changesets:
@@ -39,7 +39,6 b' import bookmark by name'
39 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
39 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
40 $ hg pull -B X ../a
40 $ hg pull -B X ../a
41 pulling from ../a
41 pulling from ../a
42 searching for changes
43 no changes found
42 no changes found
44 importing bookmark X
43 importing bookmark X
45 $ hg bookmark
44 $ hg bookmark
@@ -173,7 +172,6 b' hgweb'
173 foobar 000000000000
172 foobar 000000000000
174 $ hg pull -B Z http://localhost:$HGPORT/
173 $ hg pull -B Z http://localhost:$HGPORT/
175 pulling from http://localhost:$HGPORT/
174 pulling from http://localhost:$HGPORT/
176 searching for changes
177 no changes found
175 no changes found
178 not updating divergent bookmark X
176 not updating divergent bookmark X
179 importing bookmark Z
177 importing bookmark Z
@@ -561,7 +561,9 b' bundle single branch'
561 == bundling
561 == bundling
562
562
563 $ hg bundle bundle.hg part --debug
563 $ hg bundle bundle.hg part --debug
564 query 1; heads
564 searching for changes
565 searching for changes
566 all remote heads known locally
565 2 changesets found
567 2 changesets found
566 list of changesets:
568 list of changesets:
567 d2ae7f538514cd87c17547b0de4cea71fe1af9fb
569 d2ae7f538514cd87c17547b0de4cea71fe1af9fb
@@ -75,6 +75,7 b' Show debug commands if there are no othe'
75 debugdag
75 debugdag
76 debugdata
76 debugdata
77 debugdate
77 debugdate
78 debugdiscovery
78 debugfsinfo
79 debugfsinfo
79 debuggetbundle
80 debuggetbundle
80 debugignore
81 debugignore
@@ -219,6 +220,7 b' Show all commands + options'
219 debugdag: tags, branches, dots, spaces
220 debugdag: tags, branches, dots, spaces
220 debugdata:
221 debugdata:
221 debugdate: extended
222 debugdate: extended
223 debugdiscovery: old, nonheads, ssh, remotecmd, insecure
222 debugfsinfo:
224 debugfsinfo:
223 debuggetbundle: head, common, type
225 debuggetbundle: head, common, type
224 debugignore:
226 debugignore:
@@ -189,7 +189,7 b' listkeys hook'
189 $ hg pull -B bar ../a
189 $ hg pull -B bar ../a
190 pulling from ../a
190 pulling from ../a
191 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
191 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
192 searching for changes
192 no changes found
193 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
193 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
194 importing bookmark bar
194 importing bookmark bar
195 $ cd ../a
195 $ cd ../a
@@ -31,14 +31,12 b''
31
31
32 $ hg push --debug ../a
32 $ hg push --debug ../a
33 pushing to ../a
33 pushing to ../a
34 query 1; heads
34 searching for changes
35 searching for changes
35 examining 1c9246a22a0a:d8d565842d04
36 taking quick initial sample
36 found incomplete branch 1c9246a22a0a:d8d565842d04
37 searching: 2 queries
37 searching: 1 queries
38 query 2; still undecided: 2, sample size is: 2
38 narrowing 1:1 d8d565842d04
39 2 total queries
39 found new branch changeset 1c9246a22a0a
40 found new changesets starting at 1c9246a22a0a
41 1 total queries
42 new remote heads on branch 'default'
40 new remote heads on branch 'default'
43 new remote head 1e108cc5548c
41 new remote head 1e108cc5548c
44 abort: push creates new remote heads on branch 'default'!
42 abort: push creates new remote heads on branch 'default'!
@@ -27,9 +27,10 b' check that {1} syntax works'
27 using http://localhost:$HGPORT/
27 using http://localhost:$HGPORT/
28 sending capabilities command
28 sending capabilities command
29 comparing with parts://localhost/
29 comparing with parts://localhost/
30 query 1; heads
30 sending heads command
31 sending heads command
31 searching for changes
32 searching for changes
32 sending known command
33 all remote heads known locally
33 no changes found
34 no changes found
34 [1]
35 [1]
35
36
@@ -219,7 +219,6 b' test pushkeys and bookmarks'
219 $ hg book -f -r 0 foo
219 $ hg book -f -r 0 foo
220 $ hg pull -B foo
220 $ hg pull -B foo
221 pulling from ssh://user@dummy/remote
221 pulling from ssh://user@dummy/remote
222 searching for changes
223 no changes found
222 no changes found
224 updating bookmark foo
223 updating bookmark foo
225 importing bookmark foo
224 importing bookmark foo
General Comments 0
You need to be logged in to leave comments. Login now