##// 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
@@ -1,4901 +1,4969 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
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
7
8 from node import hex, bin, nullid, nullrev, short
8 from node import hex, bin, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, sys, difflib, time, tempfile
11 import os, re, sys, difflib, time, tempfile
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
13 import patch, help, url, encoding, templatekw, discovery
13 import patch, help, url, encoding, templatekw, discovery
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
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
21 def add(ui, repo, *pats, **opts):
22 def add(ui, repo, *pats, **opts):
22 """add the specified files on the next commit
23 """add the specified files on the next commit
23
24
24 Schedule files to be version controlled and added to the
25 Schedule files to be version controlled and added to the
25 repository.
26 repository.
26
27
27 The files will be added to the repository at the next commit. To
28 The files will be added to the repository at the next commit. To
28 undo an add before that, see :hg:`forget`.
29 undo an add before that, see :hg:`forget`.
29
30
30 If no names are given, add all files to the repository.
31 If no names are given, add all files to the repository.
31
32
32 .. container:: verbose
33 .. container:: verbose
33
34
34 An example showing how new (unknown) files are added
35 An example showing how new (unknown) files are added
35 automatically by :hg:`add`::
36 automatically by :hg:`add`::
36
37
37 $ ls
38 $ ls
38 foo.c
39 foo.c
39 $ hg status
40 $ hg status
40 ? foo.c
41 ? foo.c
41 $ hg add
42 $ hg add
42 adding foo.c
43 adding foo.c
43 $ hg status
44 $ hg status
44 A foo.c
45 A foo.c
45
46
46 Returns 0 if all files are successfully added.
47 Returns 0 if all files are successfully added.
47 """
48 """
48
49
49 m = cmdutil.match(repo, pats, opts)
50 m = cmdutil.match(repo, pats, opts)
50 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
51 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
51 opts.get('subrepos'), prefix="")
52 opts.get('subrepos'), prefix="")
52 return rejected and 1 or 0
53 return rejected and 1 or 0
53
54
54 def addremove(ui, repo, *pats, **opts):
55 def addremove(ui, repo, *pats, **opts):
55 """add all new files, delete all missing files
56 """add all new files, delete all missing files
56
57
57 Add all new files and remove all missing files from the
58 Add all new files and remove all missing files from the
58 repository.
59 repository.
59
60
60 New files are ignored if they match any of the patterns in
61 New files are ignored if they match any of the patterns in
61 ``.hgignore``. As with add, these changes take effect at the next
62 ``.hgignore``. As with add, these changes take effect at the next
62 commit.
63 commit.
63
64
64 Use the -s/--similarity option to detect renamed files. With a
65 Use the -s/--similarity option to detect renamed files. With a
65 parameter greater than 0, this compares every removed file with
66 parameter greater than 0, this compares every removed file with
66 every added file and records those similar enough as renames. This
67 every added file and records those similar enough as renames. This
67 option takes a percentage between 0 (disabled) and 100 (files must
68 option takes a percentage between 0 (disabled) and 100 (files must
68 be identical) as its parameter. Detecting renamed files this way
69 be identical) as its parameter. Detecting renamed files this way
69 can be expensive. After using this option, :hg:`status -C` can be
70 can be expensive. After using this option, :hg:`status -C` can be
70 used to check which files were identified as moved or renamed.
71 used to check which files were identified as moved or renamed.
71
72
72 Returns 0 if all files are successfully added.
73 Returns 0 if all files are successfully added.
73 """
74 """
74 try:
75 try:
75 sim = float(opts.get('similarity') or 100)
76 sim = float(opts.get('similarity') or 100)
76 except ValueError:
77 except ValueError:
77 raise util.Abort(_('similarity must be a number'))
78 raise util.Abort(_('similarity must be a number'))
78 if sim < 0 or sim > 100:
79 if sim < 0 or sim > 100:
79 raise util.Abort(_('similarity must be between 0 and 100'))
80 raise util.Abort(_('similarity must be between 0 and 100'))
80 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
81 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
81
82
82 def annotate(ui, repo, *pats, **opts):
83 def annotate(ui, repo, *pats, **opts):
83 """show changeset information by line for each file
84 """show changeset information by line for each file
84
85
85 List changes in files, showing the revision id responsible for
86 List changes in files, showing the revision id responsible for
86 each line
87 each line
87
88
88 This command is useful for discovering when a change was made and
89 This command is useful for discovering when a change was made and
89 by whom.
90 by whom.
90
91
91 Without the -a/--text option, annotate will avoid processing files
92 Without the -a/--text option, annotate will avoid processing files
92 it detects as binary. With -a, annotate will annotate the file
93 it detects as binary. With -a, annotate will annotate the file
93 anyway, although the results will probably be neither useful
94 anyway, although the results will probably be neither useful
94 nor desirable.
95 nor desirable.
95
96
96 Returns 0 on success.
97 Returns 0 on success.
97 """
98 """
98 if opts.get('follow'):
99 if opts.get('follow'):
99 # --follow is deprecated and now just an alias for -f/--file
100 # --follow is deprecated and now just an alias for -f/--file
100 # to mimic the behavior of Mercurial before version 1.5
101 # to mimic the behavior of Mercurial before version 1.5
101 opts['file'] = 1
102 opts['file'] = 1
102
103
103 datefunc = ui.quiet and util.shortdate or util.datestr
104 datefunc = ui.quiet and util.shortdate or util.datestr
104 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
105 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
105
106
106 if not pats:
107 if not pats:
107 raise util.Abort(_('at least one filename or pattern is required'))
108 raise util.Abort(_('at least one filename or pattern is required'))
108
109
109 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
110 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
110 ('number', lambda x: str(x[0].rev())),
111 ('number', lambda x: str(x[0].rev())),
111 ('changeset', lambda x: short(x[0].node())),
112 ('changeset', lambda x: short(x[0].node())),
112 ('date', getdate),
113 ('date', getdate),
113 ('file', lambda x: x[0].path()),
114 ('file', lambda x: x[0].path()),
114 ]
115 ]
115
116
116 if (not opts.get('user') and not opts.get('changeset')
117 if (not opts.get('user') and not opts.get('changeset')
117 and not opts.get('date') and not opts.get('file')):
118 and not opts.get('date') and not opts.get('file')):
118 opts['number'] = 1
119 opts['number'] = 1
119
120
120 linenumber = opts.get('line_number') is not None
121 linenumber = opts.get('line_number') is not None
121 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
122 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
122 raise util.Abort(_('at least one of -n/-c is required for -l'))
123 raise util.Abort(_('at least one of -n/-c is required for -l'))
123
124
124 funcmap = [func for op, func in opmap if opts.get(op)]
125 funcmap = [func for op, func in opmap if opts.get(op)]
125 if linenumber:
126 if linenumber:
126 lastfunc = funcmap[-1]
127 lastfunc = funcmap[-1]
127 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
128 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
128
129
129 def bad(x, y):
130 def bad(x, y):
130 raise util.Abort("%s: %s" % (x, y))
131 raise util.Abort("%s: %s" % (x, y))
131
132
132 ctx = cmdutil.revsingle(repo, opts.get('rev'))
133 ctx = cmdutil.revsingle(repo, opts.get('rev'))
133 m = cmdutil.match(repo, pats, opts)
134 m = cmdutil.match(repo, pats, opts)
134 m.bad = bad
135 m.bad = bad
135 follow = not opts.get('no_follow')
136 follow = not opts.get('no_follow')
136 for abs in ctx.walk(m):
137 for abs in ctx.walk(m):
137 fctx = ctx[abs]
138 fctx = ctx[abs]
138 if not opts.get('text') and util.binary(fctx.data()):
139 if not opts.get('text') and util.binary(fctx.data()):
139 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
140 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
140 continue
141 continue
141
142
142 lines = fctx.annotate(follow=follow, linenumber=linenumber)
143 lines = fctx.annotate(follow=follow, linenumber=linenumber)
143 pieces = []
144 pieces = []
144
145
145 for f in funcmap:
146 for f in funcmap:
146 l = [f(n) for n, dummy in lines]
147 l = [f(n) for n, dummy in lines]
147 if l:
148 if l:
148 sized = [(x, encoding.colwidth(x)) for x in l]
149 sized = [(x, encoding.colwidth(x)) for x in l]
149 ml = max([w for x, w in sized])
150 ml = max([w for x, w in sized])
150 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
151 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
151
152
152 if pieces:
153 if pieces:
153 for p, l in zip(zip(*pieces), lines):
154 for p, l in zip(zip(*pieces), lines):
154 ui.write("%s: %s" % (" ".join(p), l[1]))
155 ui.write("%s: %s" % (" ".join(p), l[1]))
155
156
156 def archive(ui, repo, dest, **opts):
157 def archive(ui, repo, dest, **opts):
157 '''create an unversioned archive of a repository revision
158 '''create an unversioned archive of a repository revision
158
159
159 By default, the revision used is the parent of the working
160 By default, the revision used is the parent of the working
160 directory; use -r/--rev to specify a different revision.
161 directory; use -r/--rev to specify a different revision.
161
162
162 The archive type is automatically detected based on file
163 The archive type is automatically detected based on file
163 extension (or override using -t/--type).
164 extension (or override using -t/--type).
164
165
165 Valid types are:
166 Valid types are:
166
167
167 :``files``: a directory full of files (default)
168 :``files``: a directory full of files (default)
168 :``tar``: tar archive, uncompressed
169 :``tar``: tar archive, uncompressed
169 :``tbz2``: tar archive, compressed using bzip2
170 :``tbz2``: tar archive, compressed using bzip2
170 :``tgz``: tar archive, compressed using gzip
171 :``tgz``: tar archive, compressed using gzip
171 :``uzip``: zip archive, uncompressed
172 :``uzip``: zip archive, uncompressed
172 :``zip``: zip archive, compressed using deflate
173 :``zip``: zip archive, compressed using deflate
173
174
174 The exact name of the destination archive or directory is given
175 The exact name of the destination archive or directory is given
175 using a format string; see :hg:`help export` for details.
176 using a format string; see :hg:`help export` for details.
176
177
177 Each member added to an archive file has a directory prefix
178 Each member added to an archive file has a directory prefix
178 prepended. Use -p/--prefix to specify a format string for the
179 prepended. Use -p/--prefix to specify a format string for the
179 prefix. The default is the basename of the archive, with suffixes
180 prefix. The default is the basename of the archive, with suffixes
180 removed.
181 removed.
181
182
182 Returns 0 on success.
183 Returns 0 on success.
183 '''
184 '''
184
185
185 ctx = cmdutil.revsingle(repo, opts.get('rev'))
186 ctx = cmdutil.revsingle(repo, opts.get('rev'))
186 if not ctx:
187 if not ctx:
187 raise util.Abort(_('no working directory: please specify a revision'))
188 raise util.Abort(_('no working directory: please specify a revision'))
188 node = ctx.node()
189 node = ctx.node()
189 dest = cmdutil.make_filename(repo, dest, node)
190 dest = cmdutil.make_filename(repo, dest, node)
190 if os.path.realpath(dest) == repo.root:
191 if os.path.realpath(dest) == repo.root:
191 raise util.Abort(_('repository root cannot be destination'))
192 raise util.Abort(_('repository root cannot be destination'))
192
193
193 kind = opts.get('type') or archival.guesskind(dest) or 'files'
194 kind = opts.get('type') or archival.guesskind(dest) or 'files'
194 prefix = opts.get('prefix')
195 prefix = opts.get('prefix')
195
196
196 if dest == '-':
197 if dest == '-':
197 if kind == 'files':
198 if kind == 'files':
198 raise util.Abort(_('cannot archive plain files to stdout'))
199 raise util.Abort(_('cannot archive plain files to stdout'))
199 dest = sys.stdout
200 dest = sys.stdout
200 if not prefix:
201 if not prefix:
201 prefix = os.path.basename(repo.root) + '-%h'
202 prefix = os.path.basename(repo.root) + '-%h'
202
203
203 prefix = cmdutil.make_filename(repo, prefix, node)
204 prefix = cmdutil.make_filename(repo, prefix, node)
204 matchfn = cmdutil.match(repo, [], opts)
205 matchfn = cmdutil.match(repo, [], opts)
205 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
206 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
206 matchfn, prefix, subrepos=opts.get('subrepos'))
207 matchfn, prefix, subrepos=opts.get('subrepos'))
207
208
208 def backout(ui, repo, node=None, rev=None, **opts):
209 def backout(ui, repo, node=None, rev=None, **opts):
209 '''reverse effect of earlier changeset
210 '''reverse effect of earlier changeset
210
211
211 Prepare a new changeset with the effect of REV undone in the
212 Prepare a new changeset with the effect of REV undone in the
212 current working directory.
213 current working directory.
213
214
214 If REV is the parent of the working directory, then this new changeset
215 If REV is the parent of the working directory, then this new changeset
215 is committed automatically. Otherwise, hg needs to merge the
216 is committed automatically. Otherwise, hg needs to merge the
216 changes and the merged result is left uncommitted.
217 changes and the merged result is left uncommitted.
217
218
218 By default, the pending changeset will have one parent,
219 By default, the pending changeset will have one parent,
219 maintaining a linear history. With --merge, the pending changeset
220 maintaining a linear history. With --merge, the pending changeset
220 will instead have two parents: the old parent of the working
221 will instead have two parents: the old parent of the working
221 directory and a new child of REV that simply undoes REV.
222 directory and a new child of REV that simply undoes REV.
222
223
223 Before version 1.7, the behavior without --merge was equivalent to
224 Before version 1.7, the behavior without --merge was equivalent to
224 specifying --merge followed by :hg:`update --clean .` to cancel
225 specifying --merge followed by :hg:`update --clean .` to cancel
225 the merge and leave the child of REV as a head to be merged
226 the merge and leave the child of REV as a head to be merged
226 separately.
227 separately.
227
228
228 See :hg:`help dates` for a list of formats valid for -d/--date.
229 See :hg:`help dates` for a list of formats valid for -d/--date.
229
230
230 Returns 0 on success.
231 Returns 0 on success.
231 '''
232 '''
232 if rev and node:
233 if rev and node:
233 raise util.Abort(_("please specify just one revision"))
234 raise util.Abort(_("please specify just one revision"))
234
235
235 if not rev:
236 if not rev:
236 rev = node
237 rev = node
237
238
238 if not rev:
239 if not rev:
239 raise util.Abort(_("please specify a revision to backout"))
240 raise util.Abort(_("please specify a revision to backout"))
240
241
241 date = opts.get('date')
242 date = opts.get('date')
242 if date:
243 if date:
243 opts['date'] = util.parsedate(date)
244 opts['date'] = util.parsedate(date)
244
245
245 cmdutil.bail_if_changed(repo)
246 cmdutil.bail_if_changed(repo)
246 node = cmdutil.revsingle(repo, rev).node()
247 node = cmdutil.revsingle(repo, rev).node()
247
248
248 op1, op2 = repo.dirstate.parents()
249 op1, op2 = repo.dirstate.parents()
249 a = repo.changelog.ancestor(op1, node)
250 a = repo.changelog.ancestor(op1, node)
250 if a != node:
251 if a != node:
251 raise util.Abort(_('cannot backout change on a different branch'))
252 raise util.Abort(_('cannot backout change on a different branch'))
252
253
253 p1, p2 = repo.changelog.parents(node)
254 p1, p2 = repo.changelog.parents(node)
254 if p1 == nullid:
255 if p1 == nullid:
255 raise util.Abort(_('cannot backout a change with no parents'))
256 raise util.Abort(_('cannot backout a change with no parents'))
256 if p2 != nullid:
257 if p2 != nullid:
257 if not opts.get('parent'):
258 if not opts.get('parent'):
258 raise util.Abort(_('cannot backout a merge changeset without '
259 raise util.Abort(_('cannot backout a merge changeset without '
259 '--parent'))
260 '--parent'))
260 p = repo.lookup(opts['parent'])
261 p = repo.lookup(opts['parent'])
261 if p not in (p1, p2):
262 if p not in (p1, p2):
262 raise util.Abort(_('%s is not a parent of %s') %
263 raise util.Abort(_('%s is not a parent of %s') %
263 (short(p), short(node)))
264 (short(p), short(node)))
264 parent = p
265 parent = p
265 else:
266 else:
266 if opts.get('parent'):
267 if opts.get('parent'):
267 raise util.Abort(_('cannot use --parent on non-merge changeset'))
268 raise util.Abort(_('cannot use --parent on non-merge changeset'))
268 parent = p1
269 parent = p1
269
270
270 # the backout should appear on the same branch
271 # the backout should appear on the same branch
271 branch = repo.dirstate.branch()
272 branch = repo.dirstate.branch()
272 hg.clean(repo, node, show_stats=False)
273 hg.clean(repo, node, show_stats=False)
273 repo.dirstate.setbranch(branch)
274 repo.dirstate.setbranch(branch)
274 revert_opts = opts.copy()
275 revert_opts = opts.copy()
275 revert_opts['date'] = None
276 revert_opts['date'] = None
276 revert_opts['all'] = True
277 revert_opts['all'] = True
277 revert_opts['rev'] = hex(parent)
278 revert_opts['rev'] = hex(parent)
278 revert_opts['no_backup'] = None
279 revert_opts['no_backup'] = None
279 revert(ui, repo, **revert_opts)
280 revert(ui, repo, **revert_opts)
280 if not opts.get('merge') and op1 != node:
281 if not opts.get('merge') and op1 != node:
281 try:
282 try:
282 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
283 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
283 return hg.update(repo, op1)
284 return hg.update(repo, op1)
284 finally:
285 finally:
285 ui.setconfig('ui', 'forcemerge', '')
286 ui.setconfig('ui', 'forcemerge', '')
286
287
287 commit_opts = opts.copy()
288 commit_opts = opts.copy()
288 commit_opts['addremove'] = False
289 commit_opts['addremove'] = False
289 if not commit_opts['message'] and not commit_opts['logfile']:
290 if not commit_opts['message'] and not commit_opts['logfile']:
290 # we don't translate commit messages
291 # we don't translate commit messages
291 commit_opts['message'] = "Backed out changeset %s" % short(node)
292 commit_opts['message'] = "Backed out changeset %s" % short(node)
292 commit_opts['force_editor'] = True
293 commit_opts['force_editor'] = True
293 commit(ui, repo, **commit_opts)
294 commit(ui, repo, **commit_opts)
294 def nice(node):
295 def nice(node):
295 return '%d:%s' % (repo.changelog.rev(node), short(node))
296 return '%d:%s' % (repo.changelog.rev(node), short(node))
296 ui.status(_('changeset %s backs out changeset %s\n') %
297 ui.status(_('changeset %s backs out changeset %s\n') %
297 (nice(repo.changelog.tip()), nice(node)))
298 (nice(repo.changelog.tip()), nice(node)))
298 if opts.get('merge') and op1 != node:
299 if opts.get('merge') and op1 != node:
299 hg.clean(repo, op1, show_stats=False)
300 hg.clean(repo, op1, show_stats=False)
300 ui.status(_('merging with changeset %s\n')
301 ui.status(_('merging with changeset %s\n')
301 % nice(repo.changelog.tip()))
302 % nice(repo.changelog.tip()))
302 try:
303 try:
303 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
304 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
304 return hg.merge(repo, hex(repo.changelog.tip()))
305 return hg.merge(repo, hex(repo.changelog.tip()))
305 finally:
306 finally:
306 ui.setconfig('ui', 'forcemerge', '')
307 ui.setconfig('ui', 'forcemerge', '')
307 return 0
308 return 0
308
309
309 def bisect(ui, repo, rev=None, extra=None, command=None,
310 def bisect(ui, repo, rev=None, extra=None, command=None,
310 reset=None, good=None, bad=None, skip=None, extend=None,
311 reset=None, good=None, bad=None, skip=None, extend=None,
311 noupdate=None):
312 noupdate=None):
312 """subdivision search of changesets
313 """subdivision search of changesets
313
314
314 This command helps to find changesets which introduce problems. To
315 This command helps to find changesets which introduce problems. To
315 use, mark the earliest changeset you know exhibits the problem as
316 use, mark the earliest changeset you know exhibits the problem as
316 bad, then mark the latest changeset which is free from the problem
317 bad, then mark the latest changeset which is free from the problem
317 as good. Bisect will update your working directory to a revision
318 as good. Bisect will update your working directory to a revision
318 for testing (unless the -U/--noupdate option is specified). Once
319 for testing (unless the -U/--noupdate option is specified). Once
319 you have performed tests, mark the working directory as good or
320 you have performed tests, mark the working directory as good or
320 bad, and bisect will either update to another candidate changeset
321 bad, and bisect will either update to another candidate changeset
321 or announce that it has found the bad revision.
322 or announce that it has found the bad revision.
322
323
323 As a shortcut, you can also use the revision argument to mark a
324 As a shortcut, you can also use the revision argument to mark a
324 revision as good or bad without checking it out first.
325 revision as good or bad without checking it out first.
325
326
326 If you supply a command, it will be used for automatic bisection.
327 If you supply a command, it will be used for automatic bisection.
327 Its exit status will be used to mark revisions as good or bad:
328 Its exit status will be used to mark revisions as good or bad:
328 status 0 means good, 125 means to skip the revision, 127
329 status 0 means good, 125 means to skip the revision, 127
329 (command not found) will abort the bisection, and any other
330 (command not found) will abort the bisection, and any other
330 non-zero exit status means the revision is bad.
331 non-zero exit status means the revision is bad.
331
332
332 Returns 0 on success.
333 Returns 0 on success.
333 """
334 """
334 def extendbisectrange(nodes, good):
335 def extendbisectrange(nodes, good):
335 # bisect is incomplete when it ends on a merge node and
336 # bisect is incomplete when it ends on a merge node and
336 # one of the parent was not checked.
337 # one of the parent was not checked.
337 parents = repo[nodes[0]].parents()
338 parents = repo[nodes[0]].parents()
338 if len(parents) > 1:
339 if len(parents) > 1:
339 side = good and state['bad'] or state['good']
340 side = good and state['bad'] or state['good']
340 num = len(set(i.node() for i in parents) & set(side))
341 num = len(set(i.node() for i in parents) & set(side))
341 if num == 1:
342 if num == 1:
342 return parents[0].ancestor(parents[1])
343 return parents[0].ancestor(parents[1])
343 return None
344 return None
344
345
345 def print_result(nodes, good):
346 def print_result(nodes, good):
346 displayer = cmdutil.show_changeset(ui, repo, {})
347 displayer = cmdutil.show_changeset(ui, repo, {})
347 if len(nodes) == 1:
348 if len(nodes) == 1:
348 # narrowed it down to a single revision
349 # narrowed it down to a single revision
349 if good:
350 if good:
350 ui.write(_("The first good revision is:\n"))
351 ui.write(_("The first good revision is:\n"))
351 else:
352 else:
352 ui.write(_("The first bad revision is:\n"))
353 ui.write(_("The first bad revision is:\n"))
353 displayer.show(repo[nodes[0]])
354 displayer.show(repo[nodes[0]])
354 extendnode = extendbisectrange(nodes, good)
355 extendnode = extendbisectrange(nodes, good)
355 if extendnode is not None:
356 if extendnode is not None:
356 ui.write(_('Not all ancestors of this changeset have been'
357 ui.write(_('Not all ancestors of this changeset have been'
357 ' checked.\nUse bisect --extend to continue the '
358 ' checked.\nUse bisect --extend to continue the '
358 'bisection from\nthe common ancestor, %s.\n')
359 'bisection from\nthe common ancestor, %s.\n')
359 % extendnode)
360 % extendnode)
360 else:
361 else:
361 # multiple possible revisions
362 # multiple possible revisions
362 if good:
363 if good:
363 ui.write(_("Due to skipped revisions, the first "
364 ui.write(_("Due to skipped revisions, the first "
364 "good revision could be any of:\n"))
365 "good revision could be any of:\n"))
365 else:
366 else:
366 ui.write(_("Due to skipped revisions, the first "
367 ui.write(_("Due to skipped revisions, the first "
367 "bad revision could be any of:\n"))
368 "bad revision could be any of:\n"))
368 for n in nodes:
369 for n in nodes:
369 displayer.show(repo[n])
370 displayer.show(repo[n])
370 displayer.close()
371 displayer.close()
371
372
372 def check_state(state, interactive=True):
373 def check_state(state, interactive=True):
373 if not state['good'] or not state['bad']:
374 if not state['good'] or not state['bad']:
374 if (good or bad or skip or reset) and interactive:
375 if (good or bad or skip or reset) and interactive:
375 return
376 return
376 if not state['good']:
377 if not state['good']:
377 raise util.Abort(_('cannot bisect (no known good revisions)'))
378 raise util.Abort(_('cannot bisect (no known good revisions)'))
378 else:
379 else:
379 raise util.Abort(_('cannot bisect (no known bad revisions)'))
380 raise util.Abort(_('cannot bisect (no known bad revisions)'))
380 return True
381 return True
381
382
382 # backward compatibility
383 # backward compatibility
383 if rev in "good bad reset init".split():
384 if rev in "good bad reset init".split():
384 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
385 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
385 cmd, rev, extra = rev, extra, None
386 cmd, rev, extra = rev, extra, None
386 if cmd == "good":
387 if cmd == "good":
387 good = True
388 good = True
388 elif cmd == "bad":
389 elif cmd == "bad":
389 bad = True
390 bad = True
390 else:
391 else:
391 reset = True
392 reset = True
392 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
393 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
393 raise util.Abort(_('incompatible arguments'))
394 raise util.Abort(_('incompatible arguments'))
394
395
395 if reset:
396 if reset:
396 p = repo.join("bisect.state")
397 p = repo.join("bisect.state")
397 if os.path.exists(p):
398 if os.path.exists(p):
398 os.unlink(p)
399 os.unlink(p)
399 return
400 return
400
401
401 state = hbisect.load_state(repo)
402 state = hbisect.load_state(repo)
402
403
403 if command:
404 if command:
404 changesets = 1
405 changesets = 1
405 try:
406 try:
406 while changesets:
407 while changesets:
407 # update state
408 # update state
408 status = util.system(command)
409 status = util.system(command)
409 if status == 125:
410 if status == 125:
410 transition = "skip"
411 transition = "skip"
411 elif status == 0:
412 elif status == 0:
412 transition = "good"
413 transition = "good"
413 # status < 0 means process was killed
414 # status < 0 means process was killed
414 elif status == 127:
415 elif status == 127:
415 raise util.Abort(_("failed to execute %s") % command)
416 raise util.Abort(_("failed to execute %s") % command)
416 elif status < 0:
417 elif status < 0:
417 raise util.Abort(_("%s killed") % command)
418 raise util.Abort(_("%s killed") % command)
418 else:
419 else:
419 transition = "bad"
420 transition = "bad"
420 ctx = cmdutil.revsingle(repo, rev)
421 ctx = cmdutil.revsingle(repo, rev)
421 rev = None # clear for future iterations
422 rev = None # clear for future iterations
422 state[transition].append(ctx.node())
423 state[transition].append(ctx.node())
423 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
424 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
424 check_state(state, interactive=False)
425 check_state(state, interactive=False)
425 # bisect
426 # bisect
426 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
427 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
427 # update to next check
428 # update to next check
428 cmdutil.bail_if_changed(repo)
429 cmdutil.bail_if_changed(repo)
429 hg.clean(repo, nodes[0], show_stats=False)
430 hg.clean(repo, nodes[0], show_stats=False)
430 finally:
431 finally:
431 hbisect.save_state(repo, state)
432 hbisect.save_state(repo, state)
432 print_result(nodes, good)
433 print_result(nodes, good)
433 return
434 return
434
435
435 # update state
436 # update state
436
437
437 if rev:
438 if rev:
438 nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])]
439 nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])]
439 else:
440 else:
440 nodes = [repo.lookup('.')]
441 nodes = [repo.lookup('.')]
441
442
442 if good or bad or skip:
443 if good or bad or skip:
443 if good:
444 if good:
444 state['good'] += nodes
445 state['good'] += nodes
445 elif bad:
446 elif bad:
446 state['bad'] += nodes
447 state['bad'] += nodes
447 elif skip:
448 elif skip:
448 state['skip'] += nodes
449 state['skip'] += nodes
449 hbisect.save_state(repo, state)
450 hbisect.save_state(repo, state)
450
451
451 if not check_state(state):
452 if not check_state(state):
452 return
453 return
453
454
454 # actually bisect
455 # actually bisect
455 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
456 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
456 if extend:
457 if extend:
457 if not changesets:
458 if not changesets:
458 extendnode = extendbisectrange(nodes, good)
459 extendnode = extendbisectrange(nodes, good)
459 if extendnode is not None:
460 if extendnode is not None:
460 ui.write(_("Extending search to changeset %d:%s\n"
461 ui.write(_("Extending search to changeset %d:%s\n"
461 % (extendnode.rev(), extendnode)))
462 % (extendnode.rev(), extendnode)))
462 if noupdate:
463 if noupdate:
463 return
464 return
464 cmdutil.bail_if_changed(repo)
465 cmdutil.bail_if_changed(repo)
465 return hg.clean(repo, extendnode.node())
466 return hg.clean(repo, extendnode.node())
466 raise util.Abort(_("nothing to extend"))
467 raise util.Abort(_("nothing to extend"))
467
468
468 if changesets == 0:
469 if changesets == 0:
469 print_result(nodes, good)
470 print_result(nodes, good)
470 else:
471 else:
471 assert len(nodes) == 1 # only a single node can be tested next
472 assert len(nodes) == 1 # only a single node can be tested next
472 node = nodes[0]
473 node = nodes[0]
473 # compute the approximate number of remaining tests
474 # compute the approximate number of remaining tests
474 tests, size = 0, 2
475 tests, size = 0, 2
475 while size <= changesets:
476 while size <= changesets:
476 tests, size = tests + 1, size * 2
477 tests, size = tests + 1, size * 2
477 rev = repo.changelog.rev(node)
478 rev = repo.changelog.rev(node)
478 ui.write(_("Testing changeset %d:%s "
479 ui.write(_("Testing changeset %d:%s "
479 "(%d changesets remaining, ~%d tests)\n")
480 "(%d changesets remaining, ~%d tests)\n")
480 % (rev, short(node), changesets, tests))
481 % (rev, short(node), changesets, tests))
481 if not noupdate:
482 if not noupdate:
482 cmdutil.bail_if_changed(repo)
483 cmdutil.bail_if_changed(repo)
483 return hg.clean(repo, node)
484 return hg.clean(repo, node)
484
485
485 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
486 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
486 '''track a line of development with movable markers
487 '''track a line of development with movable markers
487
488
488 Bookmarks are pointers to certain commits that move when
489 Bookmarks are pointers to certain commits that move when
489 committing. Bookmarks are local. They can be renamed, copied and
490 committing. Bookmarks are local. They can be renamed, copied and
490 deleted. It is possible to use bookmark names in :hg:`merge` and
491 deleted. It is possible to use bookmark names in :hg:`merge` and
491 :hg:`update` to merge and update respectively to a given bookmark.
492 :hg:`update` to merge and update respectively to a given bookmark.
492
493
493 You can use :hg:`bookmark NAME` to set a bookmark on the working
494 You can use :hg:`bookmark NAME` to set a bookmark on the working
494 directory's parent revision with the given name. If you specify
495 directory's parent revision with the given name. If you specify
495 a revision using -r REV (where REV may be an existing bookmark),
496 a revision using -r REV (where REV may be an existing bookmark),
496 the bookmark is assigned to that revision.
497 the bookmark is assigned to that revision.
497
498
498 Bookmarks can be pushed and pulled between repositories (see :hg:`help
499 Bookmarks can be pushed and pulled between repositories (see :hg:`help
499 push` and :hg:`help pull`). This requires both the local and remote
500 push` and :hg:`help pull`). This requires both the local and remote
500 repositories to support bookmarks. For versions prior to 1.8, this means
501 repositories to support bookmarks. For versions prior to 1.8, this means
501 the bookmarks extension must be enabled.
502 the bookmarks extension must be enabled.
502 '''
503 '''
503 hexfn = ui.debugflag and hex or short
504 hexfn = ui.debugflag and hex or short
504 marks = repo._bookmarks
505 marks = repo._bookmarks
505 cur = repo.changectx('.').node()
506 cur = repo.changectx('.').node()
506
507
507 if rename:
508 if rename:
508 if rename not in marks:
509 if rename not in marks:
509 raise util.Abort(_("bookmark '%s' does not exist") % rename)
510 raise util.Abort(_("bookmark '%s' does not exist") % rename)
510 if mark in marks and not force:
511 if mark in marks and not force:
511 raise util.Abort(_("bookmark '%s' already exists "
512 raise util.Abort(_("bookmark '%s' already exists "
512 "(use -f to force)") % mark)
513 "(use -f to force)") % mark)
513 if mark is None:
514 if mark is None:
514 raise util.Abort(_("new bookmark name required"))
515 raise util.Abort(_("new bookmark name required"))
515 marks[mark] = marks[rename]
516 marks[mark] = marks[rename]
516 if repo._bookmarkcurrent == rename:
517 if repo._bookmarkcurrent == rename:
517 bookmarks.setcurrent(repo, mark)
518 bookmarks.setcurrent(repo, mark)
518 del marks[rename]
519 del marks[rename]
519 bookmarks.write(repo)
520 bookmarks.write(repo)
520 return
521 return
521
522
522 if delete:
523 if delete:
523 if mark is None:
524 if mark is None:
524 raise util.Abort(_("bookmark name required"))
525 raise util.Abort(_("bookmark name required"))
525 if mark not in marks:
526 if mark not in marks:
526 raise util.Abort(_("bookmark '%s' does not exist") % mark)
527 raise util.Abort(_("bookmark '%s' does not exist") % mark)
527 if mark == repo._bookmarkcurrent:
528 if mark == repo._bookmarkcurrent:
528 bookmarks.setcurrent(repo, None)
529 bookmarks.setcurrent(repo, None)
529 del marks[mark]
530 del marks[mark]
530 bookmarks.write(repo)
531 bookmarks.write(repo)
531 return
532 return
532
533
533 if mark is not None:
534 if mark is not None:
534 if "\n" in mark:
535 if "\n" in mark:
535 raise util.Abort(_("bookmark name cannot contain newlines"))
536 raise util.Abort(_("bookmark name cannot contain newlines"))
536 mark = mark.strip()
537 mark = mark.strip()
537 if not mark:
538 if not mark:
538 raise util.Abort(_("bookmark names cannot consist entirely of "
539 raise util.Abort(_("bookmark names cannot consist entirely of "
539 "whitespace"))
540 "whitespace"))
540 if mark in marks and not force:
541 if mark in marks and not force:
541 raise util.Abort(_("bookmark '%s' already exists "
542 raise util.Abort(_("bookmark '%s' already exists "
542 "(use -f to force)") % mark)
543 "(use -f to force)") % mark)
543 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
544 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
544 and not force):
545 and not force):
545 raise util.Abort(
546 raise util.Abort(
546 _("a bookmark cannot have the name of an existing branch"))
547 _("a bookmark cannot have the name of an existing branch"))
547 if rev:
548 if rev:
548 marks[mark] = repo.lookup(rev)
549 marks[mark] = repo.lookup(rev)
549 else:
550 else:
550 marks[mark] = repo.changectx('.').node()
551 marks[mark] = repo.changectx('.').node()
551 if repo.changectx('.').node() == marks[mark]:
552 if repo.changectx('.').node() == marks[mark]:
552 bookmarks.setcurrent(repo, mark)
553 bookmarks.setcurrent(repo, mark)
553 bookmarks.write(repo)
554 bookmarks.write(repo)
554 return
555 return
555
556
556 if mark is None:
557 if mark is None:
557 if rev:
558 if rev:
558 raise util.Abort(_("bookmark name required"))
559 raise util.Abort(_("bookmark name required"))
559 if len(marks) == 0:
560 if len(marks) == 0:
560 ui.status(_("no bookmarks set\n"))
561 ui.status(_("no bookmarks set\n"))
561 else:
562 else:
562 for bmark, n in sorted(marks.iteritems()):
563 for bmark, n in sorted(marks.iteritems()):
563 current = repo._bookmarkcurrent
564 current = repo._bookmarkcurrent
564 if bmark == current and n == cur:
565 if bmark == current and n == cur:
565 prefix, label = '*', 'bookmarks.current'
566 prefix, label = '*', 'bookmarks.current'
566 else:
567 else:
567 prefix, label = ' ', ''
568 prefix, label = ' ', ''
568
569
569 if ui.quiet:
570 if ui.quiet:
570 ui.write("%s\n" % bmark, label=label)
571 ui.write("%s\n" % bmark, label=label)
571 else:
572 else:
572 ui.write(" %s %-25s %d:%s\n" % (
573 ui.write(" %s %-25s %d:%s\n" % (
573 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
574 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
574 label=label)
575 label=label)
575 return
576 return
576
577
577 def branch(ui, repo, label=None, **opts):
578 def branch(ui, repo, label=None, **opts):
578 """set or show the current branch name
579 """set or show the current branch name
579
580
580 With no argument, show the current branch name. With one argument,
581 With no argument, show the current branch name. With one argument,
581 set the working directory branch name (the branch will not exist
582 set the working directory branch name (the branch will not exist
582 in the repository until the next commit). Standard practice
583 in the repository until the next commit). Standard practice
583 recommends that primary development take place on the 'default'
584 recommends that primary development take place on the 'default'
584 branch.
585 branch.
585
586
586 Unless -f/--force is specified, branch will not let you set a
587 Unless -f/--force is specified, branch will not let you set a
587 branch name that already exists, even if it's inactive.
588 branch name that already exists, even if it's inactive.
588
589
589 Use -C/--clean to reset the working directory branch to that of
590 Use -C/--clean to reset the working directory branch to that of
590 the parent of the working directory, negating a previous branch
591 the parent of the working directory, negating a previous branch
591 change.
592 change.
592
593
593 Use the command :hg:`update` to switch to an existing branch. Use
594 Use the command :hg:`update` to switch to an existing branch. Use
594 :hg:`commit --close-branch` to mark this branch as closed.
595 :hg:`commit --close-branch` to mark this branch as closed.
595
596
596 Returns 0 on success.
597 Returns 0 on success.
597 """
598 """
598
599
599 if opts.get('clean'):
600 if opts.get('clean'):
600 label = repo[None].p1().branch()
601 label = repo[None].p1().branch()
601 repo.dirstate.setbranch(label)
602 repo.dirstate.setbranch(label)
602 ui.status(_('reset working directory to branch %s\n') % label)
603 ui.status(_('reset working directory to branch %s\n') % label)
603 elif label:
604 elif label:
604 if not opts.get('force') and label in repo.branchtags():
605 if not opts.get('force') and label in repo.branchtags():
605 if label not in [p.branch() for p in repo.parents()]:
606 if label not in [p.branch() for p in repo.parents()]:
606 raise util.Abort(_('a branch of the same name already exists'
607 raise util.Abort(_('a branch of the same name already exists'
607 " (use 'hg update' to switch to it)"))
608 " (use 'hg update' to switch to it)"))
608 repo.dirstate.setbranch(label)
609 repo.dirstate.setbranch(label)
609 ui.status(_('marked working directory as branch %s\n') % label)
610 ui.status(_('marked working directory as branch %s\n') % label)
610 else:
611 else:
611 ui.write("%s\n" % repo.dirstate.branch())
612 ui.write("%s\n" % repo.dirstate.branch())
612
613
613 def branches(ui, repo, active=False, closed=False):
614 def branches(ui, repo, active=False, closed=False):
614 """list repository named branches
615 """list repository named branches
615
616
616 List the repository's named branches, indicating which ones are
617 List the repository's named branches, indicating which ones are
617 inactive. If -c/--closed is specified, also list branches which have
618 inactive. If -c/--closed is specified, also list branches which have
618 been marked closed (see :hg:`commit --close-branch`).
619 been marked closed (see :hg:`commit --close-branch`).
619
620
620 If -a/--active is specified, only show active branches. A branch
621 If -a/--active is specified, only show active branches. A branch
621 is considered active if it contains repository heads.
622 is considered active if it contains repository heads.
622
623
623 Use the command :hg:`update` to switch to an existing branch.
624 Use the command :hg:`update` to switch to an existing branch.
624
625
625 Returns 0.
626 Returns 0.
626 """
627 """
627
628
628 hexfunc = ui.debugflag and hex or short
629 hexfunc = ui.debugflag and hex or short
629 activebranches = [repo[n].branch() for n in repo.heads()]
630 activebranches = [repo[n].branch() for n in repo.heads()]
630 def testactive(tag, node):
631 def testactive(tag, node):
631 realhead = tag in activebranches
632 realhead = tag in activebranches
632 open = node in repo.branchheads(tag, closed=False)
633 open = node in repo.branchheads(tag, closed=False)
633 return realhead and open
634 return realhead and open
634 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
635 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
635 for tag, node in repo.branchtags().items()],
636 for tag, node in repo.branchtags().items()],
636 reverse=True)
637 reverse=True)
637
638
638 for isactive, node, tag in branches:
639 for isactive, node, tag in branches:
639 if (not active) or isactive:
640 if (not active) or isactive:
640 if ui.quiet:
641 if ui.quiet:
641 ui.write("%s\n" % tag)
642 ui.write("%s\n" % tag)
642 else:
643 else:
643 hn = repo.lookup(node)
644 hn = repo.lookup(node)
644 if isactive:
645 if isactive:
645 label = 'branches.active'
646 label = 'branches.active'
646 notice = ''
647 notice = ''
647 elif hn not in repo.branchheads(tag, closed=False):
648 elif hn not in repo.branchheads(tag, closed=False):
648 if not closed:
649 if not closed:
649 continue
650 continue
650 label = 'branches.closed'
651 label = 'branches.closed'
651 notice = _(' (closed)')
652 notice = _(' (closed)')
652 else:
653 else:
653 label = 'branches.inactive'
654 label = 'branches.inactive'
654 notice = _(' (inactive)')
655 notice = _(' (inactive)')
655 if tag == repo.dirstate.branch():
656 if tag == repo.dirstate.branch():
656 label = 'branches.current'
657 label = 'branches.current'
657 rev = str(node).rjust(31 - encoding.colwidth(tag))
658 rev = str(node).rjust(31 - encoding.colwidth(tag))
658 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
659 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
659 tag = ui.label(tag, label)
660 tag = ui.label(tag, label)
660 ui.write("%s %s%s\n" % (tag, rev, notice))
661 ui.write("%s %s%s\n" % (tag, rev, notice))
661
662
662 def bundle(ui, repo, fname, dest=None, **opts):
663 def bundle(ui, repo, fname, dest=None, **opts):
663 """create a changegroup file
664 """create a changegroup file
664
665
665 Generate a compressed changegroup file collecting changesets not
666 Generate a compressed changegroup file collecting changesets not
666 known to be in another repository.
667 known to be in another repository.
667
668
668 If you omit the destination repository, then hg assumes the
669 If you omit the destination repository, then hg assumes the
669 destination will have all the nodes you specify with --base
670 destination will have all the nodes you specify with --base
670 parameters. To create a bundle containing all changesets, use
671 parameters. To create a bundle containing all changesets, use
671 -a/--all (or --base null).
672 -a/--all (or --base null).
672
673
673 You can change compression method with the -t/--type option.
674 You can change compression method with the -t/--type option.
674 The available compression methods are: none, bzip2, and
675 The available compression methods are: none, bzip2, and
675 gzip (by default, bundles are compressed using bzip2).
676 gzip (by default, bundles are compressed using bzip2).
676
677
677 The bundle file can then be transferred using conventional means
678 The bundle file can then be transferred using conventional means
678 and applied to another repository with the unbundle or pull
679 and applied to another repository with the unbundle or pull
679 command. This is useful when direct push and pull are not
680 command. This is useful when direct push and pull are not
680 available or when exporting an entire repository is undesirable.
681 available or when exporting an entire repository is undesirable.
681
682
682 Applying bundles preserves all changeset contents including
683 Applying bundles preserves all changeset contents including
683 permissions, copy/rename information, and revision history.
684 permissions, copy/rename information, and revision history.
684
685
685 Returns 0 on success, 1 if no changes found.
686 Returns 0 on success, 1 if no changes found.
686 """
687 """
687 revs = None
688 revs = None
688 if 'rev' in opts:
689 if 'rev' in opts:
689 revs = cmdutil.revrange(repo, opts['rev'])
690 revs = cmdutil.revrange(repo, opts['rev'])
690
691
691 if opts.get('all'):
692 if opts.get('all'):
692 base = ['null']
693 base = ['null']
693 else:
694 else:
694 base = cmdutil.revrange(repo, opts.get('base'))
695 base = cmdutil.revrange(repo, opts.get('base'))
695 if base:
696 if base:
696 if dest:
697 if dest:
697 raise util.Abort(_("--base is incompatible with specifying "
698 raise util.Abort(_("--base is incompatible with specifying "
698 "a destination"))
699 "a destination"))
699 common = [repo.lookup(rev) for rev in base]
700 common = [repo.lookup(rev) for rev in base]
700 else:
701 else:
701 dest = ui.expandpath(dest or 'default-push', dest or 'default')
702 dest = ui.expandpath(dest or 'default-push', dest or 'default')
702 dest, branches = hg.parseurl(dest, opts.get('branch'))
703 dest, branches = hg.parseurl(dest, opts.get('branch'))
703 other = hg.repository(hg.remoteui(repo, opts), dest)
704 other = hg.repository(hg.remoteui(repo, opts), dest)
704 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
705 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
705 inc = discovery.findcommonincoming(repo, other, force=opts.get('force'))
706 inc = discovery.findcommonincoming(repo, other, force=opts.get('force'))
706 common, _anyinc, _heads = inc
707 common, _anyinc, _heads = inc
707
708
708 nodes = revs and map(repo.lookup, revs) or revs
709 nodes = revs and map(repo.lookup, revs) or revs
709 cg = repo.getbundle('bundle', common=common, heads=nodes)
710 cg = repo.getbundle('bundle', common=common, heads=nodes)
710 if not cg:
711 if not cg:
711 ui.status(_("no changes found\n"))
712 ui.status(_("no changes found\n"))
712 return 1
713 return 1
713
714
714 bundletype = opts.get('type', 'bzip2').lower()
715 bundletype = opts.get('type', 'bzip2').lower()
715 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
716 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
716 bundletype = btypes.get(bundletype)
717 bundletype = btypes.get(bundletype)
717 if bundletype not in changegroup.bundletypes:
718 if bundletype not in changegroup.bundletypes:
718 raise util.Abort(_('unknown bundle type specified with --type'))
719 raise util.Abort(_('unknown bundle type specified with --type'))
719
720
720 changegroup.writebundle(cg, fname, bundletype)
721 changegroup.writebundle(cg, fname, bundletype)
721
722
722 def cat(ui, repo, file1, *pats, **opts):
723 def cat(ui, repo, file1, *pats, **opts):
723 """output the current or given revision of files
724 """output the current or given revision of files
724
725
725 Print the specified files as they were at the given revision. If
726 Print the specified files as they were at the given revision. If
726 no revision is given, the parent of the working directory is used,
727 no revision is given, the parent of the working directory is used,
727 or tip if no revision is checked out.
728 or tip if no revision is checked out.
728
729
729 Output may be to a file, in which case the name of the file is
730 Output may be to a file, in which case the name of the file is
730 given using a format string. The formatting rules are the same as
731 given using a format string. The formatting rules are the same as
731 for the export command, with the following additions:
732 for the export command, with the following additions:
732
733
733 :``%s``: basename of file being printed
734 :``%s``: basename of file being printed
734 :``%d``: dirname of file being printed, or '.' if in repository root
735 :``%d``: dirname of file being printed, or '.' if in repository root
735 :``%p``: root-relative path name of file being printed
736 :``%p``: root-relative path name of file being printed
736
737
737 Returns 0 on success.
738 Returns 0 on success.
738 """
739 """
739 ctx = cmdutil.revsingle(repo, opts.get('rev'))
740 ctx = cmdutil.revsingle(repo, opts.get('rev'))
740 err = 1
741 err = 1
741 m = cmdutil.match(repo, (file1,) + pats, opts)
742 m = cmdutil.match(repo, (file1,) + pats, opts)
742 for abs in ctx.walk(m):
743 for abs in ctx.walk(m):
743 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
744 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
744 data = ctx[abs].data()
745 data = ctx[abs].data()
745 if opts.get('decode'):
746 if opts.get('decode'):
746 data = repo.wwritedata(abs, data)
747 data = repo.wwritedata(abs, data)
747 fp.write(data)
748 fp.write(data)
748 fp.close()
749 fp.close()
749 err = 0
750 err = 0
750 return err
751 return err
751
752
752 def clone(ui, source, dest=None, **opts):
753 def clone(ui, source, dest=None, **opts):
753 """make a copy of an existing repository
754 """make a copy of an existing repository
754
755
755 Create a copy of an existing repository in a new directory.
756 Create a copy of an existing repository in a new directory.
756
757
757 If no destination directory name is specified, it defaults to the
758 If no destination directory name is specified, it defaults to the
758 basename of the source.
759 basename of the source.
759
760
760 The location of the source is added to the new repository's
761 The location of the source is added to the new repository's
761 ``.hg/hgrc`` file, as the default to be used for future pulls.
762 ``.hg/hgrc`` file, as the default to be used for future pulls.
762
763
763 See :hg:`help urls` for valid source format details.
764 See :hg:`help urls` for valid source format details.
764
765
765 It is possible to specify an ``ssh://`` URL as the destination, but no
766 It is possible to specify an ``ssh://`` URL as the destination, but no
766 ``.hg/hgrc`` and working directory will be created on the remote side.
767 ``.hg/hgrc`` and working directory will be created on the remote side.
767 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
768 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
768
769
769 A set of changesets (tags, or branch names) to pull may be specified
770 A set of changesets (tags, or branch names) to pull may be specified
770 by listing each changeset (tag, or branch name) with -r/--rev.
771 by listing each changeset (tag, or branch name) with -r/--rev.
771 If -r/--rev is used, the cloned repository will contain only a subset
772 If -r/--rev is used, the cloned repository will contain only a subset
772 of the changesets of the source repository. Only the set of changesets
773 of the changesets of the source repository. Only the set of changesets
773 defined by all -r/--rev options (including all their ancestors)
774 defined by all -r/--rev options (including all their ancestors)
774 will be pulled into the destination repository.
775 will be pulled into the destination repository.
775 No subsequent changesets (including subsequent tags) will be present
776 No subsequent changesets (including subsequent tags) will be present
776 in the destination.
777 in the destination.
777
778
778 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
779 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
779 local source repositories.
780 local source repositories.
780
781
781 For efficiency, hardlinks are used for cloning whenever the source
782 For efficiency, hardlinks are used for cloning whenever the source
782 and destination are on the same filesystem (note this applies only
783 and destination are on the same filesystem (note this applies only
783 to the repository data, not to the working directory). Some
784 to the repository data, not to the working directory). Some
784 filesystems, such as AFS, implement hardlinking incorrectly, but
785 filesystems, such as AFS, implement hardlinking incorrectly, but
785 do not report errors. In these cases, use the --pull option to
786 do not report errors. In these cases, use the --pull option to
786 avoid hardlinking.
787 avoid hardlinking.
787
788
788 In some cases, you can clone repositories and the working directory
789 In some cases, you can clone repositories and the working directory
789 using full hardlinks with ::
790 using full hardlinks with ::
790
791
791 $ cp -al REPO REPOCLONE
792 $ cp -al REPO REPOCLONE
792
793
793 This is the fastest way to clone, but it is not always safe. The
794 This is the fastest way to clone, but it is not always safe. The
794 operation is not atomic (making sure REPO is not modified during
795 operation is not atomic (making sure REPO is not modified during
795 the operation is up to you) and you have to make sure your editor
796 the operation is up to you) and you have to make sure your editor
796 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
797 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
797 this is not compatible with certain extensions that place their
798 this is not compatible with certain extensions that place their
798 metadata under the .hg directory, such as mq.
799 metadata under the .hg directory, such as mq.
799
800
800 Mercurial will update the working directory to the first applicable
801 Mercurial will update the working directory to the first applicable
801 revision from this list:
802 revision from this list:
802
803
803 a) null if -U or the source repository has no changesets
804 a) null if -U or the source repository has no changesets
804 b) if -u . and the source repository is local, the first parent of
805 b) if -u . and the source repository is local, the first parent of
805 the source repository's working directory
806 the source repository's working directory
806 c) the changeset specified with -u (if a branch name, this means the
807 c) the changeset specified with -u (if a branch name, this means the
807 latest head of that branch)
808 latest head of that branch)
808 d) the changeset specified with -r
809 d) the changeset specified with -r
809 e) the tipmost head specified with -b
810 e) the tipmost head specified with -b
810 f) the tipmost head specified with the url#branch source syntax
811 f) the tipmost head specified with the url#branch source syntax
811 g) the tipmost head of the default branch
812 g) the tipmost head of the default branch
812 h) tip
813 h) tip
813
814
814 Returns 0 on success.
815 Returns 0 on success.
815 """
816 """
816 if opts.get('noupdate') and opts.get('updaterev'):
817 if opts.get('noupdate') and opts.get('updaterev'):
817 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
818 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
818
819
819 r = hg.clone(hg.remoteui(ui, opts), source, dest,
820 r = hg.clone(hg.remoteui(ui, opts), source, dest,
820 pull=opts.get('pull'),
821 pull=opts.get('pull'),
821 stream=opts.get('uncompressed'),
822 stream=opts.get('uncompressed'),
822 rev=opts.get('rev'),
823 rev=opts.get('rev'),
823 update=opts.get('updaterev') or not opts.get('noupdate'),
824 update=opts.get('updaterev') or not opts.get('noupdate'),
824 branch=opts.get('branch'))
825 branch=opts.get('branch'))
825
826
826 return r is None
827 return r is None
827
828
828 def commit(ui, repo, *pats, **opts):
829 def commit(ui, repo, *pats, **opts):
829 """commit the specified files or all outstanding changes
830 """commit the specified files or all outstanding changes
830
831
831 Commit changes to the given files into the repository. Unlike a
832 Commit changes to the given files into the repository. Unlike a
832 centralized SCM, this operation is a local operation. See
833 centralized SCM, this operation is a local operation. See
833 :hg:`push` for a way to actively distribute your changes.
834 :hg:`push` for a way to actively distribute your changes.
834
835
835 If a list of files is omitted, all changes reported by :hg:`status`
836 If a list of files is omitted, all changes reported by :hg:`status`
836 will be committed.
837 will be committed.
837
838
838 If you are committing the result of a merge, do not provide any
839 If you are committing the result of a merge, do not provide any
839 filenames or -I/-X filters.
840 filenames or -I/-X filters.
840
841
841 If no commit message is specified, Mercurial starts your
842 If no commit message is specified, Mercurial starts your
842 configured editor where you can enter a message. In case your
843 configured editor where you can enter a message. In case your
843 commit fails, you will find a backup of your message in
844 commit fails, you will find a backup of your message in
844 ``.hg/last-message.txt``.
845 ``.hg/last-message.txt``.
845
846
846 See :hg:`help dates` for a list of formats valid for -d/--date.
847 See :hg:`help dates` for a list of formats valid for -d/--date.
847
848
848 Returns 0 on success, 1 if nothing changed.
849 Returns 0 on success, 1 if nothing changed.
849 """
850 """
850 extra = {}
851 extra = {}
851 if opts.get('close_branch'):
852 if opts.get('close_branch'):
852 if repo['.'].node() not in repo.branchheads():
853 if repo['.'].node() not in repo.branchheads():
853 # The topo heads set is included in the branch heads set of the
854 # The topo heads set is included in the branch heads set of the
854 # current branch, so it's sufficient to test branchheads
855 # current branch, so it's sufficient to test branchheads
855 raise util.Abort(_('can only close branch heads'))
856 raise util.Abort(_('can only close branch heads'))
856 extra['close'] = 1
857 extra['close'] = 1
857 e = cmdutil.commiteditor
858 e = cmdutil.commiteditor
858 if opts.get('force_editor'):
859 if opts.get('force_editor'):
859 e = cmdutil.commitforceeditor
860 e = cmdutil.commitforceeditor
860
861
861 def commitfunc(ui, repo, message, match, opts):
862 def commitfunc(ui, repo, message, match, opts):
862 return repo.commit(message, opts.get('user'), opts.get('date'), match,
863 return repo.commit(message, opts.get('user'), opts.get('date'), match,
863 editor=e, extra=extra)
864 editor=e, extra=extra)
864
865
865 branch = repo[None].branch()
866 branch = repo[None].branch()
866 bheads = repo.branchheads(branch)
867 bheads = repo.branchheads(branch)
867
868
868 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
869 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
869 if not node:
870 if not node:
870 stat = repo.status(match=cmdutil.match(repo, pats, opts))
871 stat = repo.status(match=cmdutil.match(repo, pats, opts))
871 if stat[3]:
872 if stat[3]:
872 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
873 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
873 % len(stat[3]))
874 % len(stat[3]))
874 else:
875 else:
875 ui.status(_("nothing changed\n"))
876 ui.status(_("nothing changed\n"))
876 return 1
877 return 1
877
878
878 ctx = repo[node]
879 ctx = repo[node]
879 parents = ctx.parents()
880 parents = ctx.parents()
880
881
881 if bheads and not [x for x in parents
882 if bheads and not [x for x in parents
882 if x.node() in bheads and x.branch() == branch]:
883 if x.node() in bheads and x.branch() == branch]:
883 ui.status(_('created new head\n'))
884 ui.status(_('created new head\n'))
884 # The message is not printed for initial roots. For the other
885 # The message is not printed for initial roots. For the other
885 # changesets, it is printed in the following situations:
886 # changesets, it is printed in the following situations:
886 #
887 #
887 # Par column: for the 2 parents with ...
888 # Par column: for the 2 parents with ...
888 # N: null or no parent
889 # N: null or no parent
889 # B: parent is on another named branch
890 # B: parent is on another named branch
890 # C: parent is a regular non head changeset
891 # C: parent is a regular non head changeset
891 # H: parent was a branch head of the current branch
892 # H: parent was a branch head of the current branch
892 # Msg column: whether we print "created new head" message
893 # Msg column: whether we print "created new head" message
893 # In the following, it is assumed that there already exists some
894 # In the following, it is assumed that there already exists some
894 # initial branch heads of the current branch, otherwise nothing is
895 # initial branch heads of the current branch, otherwise nothing is
895 # printed anyway.
896 # printed anyway.
896 #
897 #
897 # Par Msg Comment
898 # Par Msg Comment
898 # NN y additional topo root
899 # NN y additional topo root
899 #
900 #
900 # BN y additional branch root
901 # BN y additional branch root
901 # CN y additional topo head
902 # CN y additional topo head
902 # HN n usual case
903 # HN n usual case
903 #
904 #
904 # BB y weird additional branch root
905 # BB y weird additional branch root
905 # CB y branch merge
906 # CB y branch merge
906 # HB n merge with named branch
907 # HB n merge with named branch
907 #
908 #
908 # CC y additional head from merge
909 # CC y additional head from merge
909 # CH n merge with a head
910 # CH n merge with a head
910 #
911 #
911 # HH n head merge: head count decreases
912 # HH n head merge: head count decreases
912
913
913 if not opts.get('close_branch'):
914 if not opts.get('close_branch'):
914 for r in parents:
915 for r in parents:
915 if r.extra().get('close') and r.branch() == branch:
916 if r.extra().get('close') and r.branch() == branch:
916 ui.status(_('reopening closed branch head %d\n') % r)
917 ui.status(_('reopening closed branch head %d\n') % r)
917
918
918 if ui.debugflag:
919 if ui.debugflag:
919 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
920 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
920 elif ui.verbose:
921 elif ui.verbose:
921 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
922 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
922
923
923 def copy(ui, repo, *pats, **opts):
924 def copy(ui, repo, *pats, **opts):
924 """mark files as copied for the next commit
925 """mark files as copied for the next commit
925
926
926 Mark dest as having copies of source files. If dest is a
927 Mark dest as having copies of source files. If dest is a
927 directory, copies are put in that directory. If dest is a file,
928 directory, copies are put in that directory. If dest is a file,
928 the source must be a single file.
929 the source must be a single file.
929
930
930 By default, this command copies the contents of files as they
931 By default, this command copies the contents of files as they
931 exist in the working directory. If invoked with -A/--after, the
932 exist in the working directory. If invoked with -A/--after, the
932 operation is recorded, but no copying is performed.
933 operation is recorded, but no copying is performed.
933
934
934 This command takes effect with the next commit. To undo a copy
935 This command takes effect with the next commit. To undo a copy
935 before that, see :hg:`revert`.
936 before that, see :hg:`revert`.
936
937
937 Returns 0 on success, 1 if errors are encountered.
938 Returns 0 on success, 1 if errors are encountered.
938 """
939 """
939 wlock = repo.wlock(False)
940 wlock = repo.wlock(False)
940 try:
941 try:
941 return cmdutil.copy(ui, repo, pats, opts)
942 return cmdutil.copy(ui, repo, pats, opts)
942 finally:
943 finally:
943 wlock.release()
944 wlock.release()
944
945
945 def debugancestor(ui, repo, *args):
946 def debugancestor(ui, repo, *args):
946 """find the ancestor revision of two revisions in a given index"""
947 """find the ancestor revision of two revisions in a given index"""
947 if len(args) == 3:
948 if len(args) == 3:
948 index, rev1, rev2 = args
949 index, rev1, rev2 = args
949 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
950 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
950 lookup = r.lookup
951 lookup = r.lookup
951 elif len(args) == 2:
952 elif len(args) == 2:
952 if not repo:
953 if not repo:
953 raise util.Abort(_("there is no Mercurial repository here "
954 raise util.Abort(_("there is no Mercurial repository here "
954 "(.hg not found)"))
955 "(.hg not found)"))
955 rev1, rev2 = args
956 rev1, rev2 = args
956 r = repo.changelog
957 r = repo.changelog
957 lookup = repo.lookup
958 lookup = repo.lookup
958 else:
959 else:
959 raise util.Abort(_('either two or three arguments required'))
960 raise util.Abort(_('either two or three arguments required'))
960 a = r.ancestor(lookup(rev1), lookup(rev2))
961 a = r.ancestor(lookup(rev1), lookup(rev2))
961 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
962 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
962
963
963 def debugbuilddag(ui, repo, text,
964 def debugbuilddag(ui, repo, text,
964 mergeable_file=False,
965 mergeable_file=False,
965 overwritten_file=False,
966 overwritten_file=False,
966 new_file=False):
967 new_file=False):
967 """builds a repo with a given dag from scratch in the current empty repo
968 """builds a repo with a given dag from scratch in the current empty repo
968
969
969 Elements:
970 Elements:
970
971
971 - "+n" is a linear run of n nodes based on the current default parent
972 - "+n" is a linear run of n nodes based on the current default parent
972 - "." is a single node based on the current default parent
973 - "." is a single node based on the current default parent
973 - "$" resets the default parent to null (implied at the start);
974 - "$" resets the default parent to null (implied at the start);
974 otherwise the default parent is always the last node created
975 otherwise the default parent is always the last node created
975 - "<p" sets the default parent to the backref p
976 - "<p" sets the default parent to the backref p
976 - "*p" is a fork at parent p, which is a backref
977 - "*p" is a fork at parent p, which is a backref
977 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
978 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
978 - "/p2" is a merge of the preceding node and p2
979 - "/p2" is a merge of the preceding node and p2
979 - ":tag" defines a local tag for the preceding node
980 - ":tag" defines a local tag for the preceding node
980 - "@branch" sets the named branch for subsequent nodes
981 - "@branch" sets the named branch for subsequent nodes
981 - "#...\\n" is a comment up to the end of the line
982 - "#...\\n" is a comment up to the end of the line
982
983
983 Whitespace between the above elements is ignored.
984 Whitespace between the above elements is ignored.
984
985
985 A backref is either
986 A backref is either
986
987
987 - a number n, which references the node curr-n, where curr is the current
988 - a number n, which references the node curr-n, where curr is the current
988 node, or
989 node, or
989 - the name of a local tag you placed earlier using ":tag", or
990 - the name of a local tag you placed earlier using ":tag", or
990 - empty to denote the default parent.
991 - empty to denote the default parent.
991
992
992 All string valued-elements are either strictly alphanumeric, or must
993 All string valued-elements are either strictly alphanumeric, or must
993 be enclosed in double quotes ("..."), with "\\" as escape character.
994 be enclosed in double quotes ("..."), with "\\" as escape character.
994 """
995 """
995
996
996 cl = repo.changelog
997 cl = repo.changelog
997 if len(cl) > 0:
998 if len(cl) > 0:
998 raise util.Abort(_('repository is not empty'))
999 raise util.Abort(_('repository is not empty'))
999
1000
1000 if mergeable_file:
1001 if mergeable_file:
1001 linesperrev = 2
1002 linesperrev = 2
1002 # determine number of revs in DAG
1003 # determine number of revs in DAG
1003 n = 0
1004 n = 0
1004 for type, data in dagparser.parsedag(text):
1005 for type, data in dagparser.parsedag(text):
1005 if type == 'n':
1006 if type == 'n':
1006 n += 1
1007 n += 1
1007 # make a file with k lines per rev
1008 # make a file with k lines per rev
1008 initialmergedlines = [str(i) for i in xrange(0, n * linesperrev)]
1009 initialmergedlines = [str(i) for i in xrange(0, n * linesperrev)]
1009 initialmergedlines.append("")
1010 initialmergedlines.append("")
1010
1011
1011 tags = []
1012 tags = []
1012
1013
1013 tr = repo.transaction("builddag")
1014 tr = repo.transaction("builddag")
1014 try:
1015 try:
1015
1016
1016 at = -1
1017 at = -1
1017 atbranch = 'default'
1018 atbranch = 'default'
1018 nodeids = []
1019 nodeids = []
1019 for type, data in dagparser.parsedag(text):
1020 for type, data in dagparser.parsedag(text):
1020 if type == 'n':
1021 if type == 'n':
1021 ui.note('node %s\n' % str(data))
1022 ui.note('node %s\n' % str(data))
1022 id, ps = data
1023 id, ps = data
1023
1024
1024 files = []
1025 files = []
1025 fctxs = {}
1026 fctxs = {}
1026
1027
1027 p2 = None
1028 p2 = None
1028 if mergeable_file:
1029 if mergeable_file:
1029 fn = "mf"
1030 fn = "mf"
1030 p1 = repo[ps[0]]
1031 p1 = repo[ps[0]]
1031 if len(ps) > 1:
1032 if len(ps) > 1:
1032 p2 = repo[ps[1]]
1033 p2 = repo[ps[1]]
1033 pa = p1.ancestor(p2)
1034 pa = p1.ancestor(p2)
1034 base, local, other = [x[fn].data() for x in pa, p1, p2]
1035 base, local, other = [x[fn].data() for x in pa, p1, p2]
1035 m3 = simplemerge.Merge3Text(base, local, other)
1036 m3 = simplemerge.Merge3Text(base, local, other)
1036 ml = [l.strip() for l in m3.merge_lines()]
1037 ml = [l.strip() for l in m3.merge_lines()]
1037 ml.append("")
1038 ml.append("")
1038 elif at > 0:
1039 elif at > 0:
1039 ml = p1[fn].data().split("\n")
1040 ml = p1[fn].data().split("\n")
1040 else:
1041 else:
1041 ml = initialmergedlines
1042 ml = initialmergedlines
1042 ml[id * linesperrev] += " r%i" % id
1043 ml[id * linesperrev] += " r%i" % id
1043 mergedtext = "\n".join(ml)
1044 mergedtext = "\n".join(ml)
1044 files.append(fn)
1045 files.append(fn)
1045 fctxs[fn] = context.memfilectx(fn, mergedtext)
1046 fctxs[fn] = context.memfilectx(fn, mergedtext)
1046
1047
1047 if overwritten_file:
1048 if overwritten_file:
1048 fn = "of"
1049 fn = "of"
1049 files.append(fn)
1050 files.append(fn)
1050 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1051 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1051
1052
1052 if new_file:
1053 if new_file:
1053 fn = "nf%i" % id
1054 fn = "nf%i" % id
1054 files.append(fn)
1055 files.append(fn)
1055 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1056 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1056 if len(ps) > 1:
1057 if len(ps) > 1:
1057 if not p2:
1058 if not p2:
1058 p2 = repo[ps[1]]
1059 p2 = repo[ps[1]]
1059 for fn in p2:
1060 for fn in p2:
1060 if fn.startswith("nf"):
1061 if fn.startswith("nf"):
1061 files.append(fn)
1062 files.append(fn)
1062 fctxs[fn] = p2[fn]
1063 fctxs[fn] = p2[fn]
1063
1064
1064 def fctxfn(repo, cx, path):
1065 def fctxfn(repo, cx, path):
1065 return fctxs.get(path)
1066 return fctxs.get(path)
1066
1067
1067 if len(ps) == 0 or ps[0] < 0:
1068 if len(ps) == 0 or ps[0] < 0:
1068 pars = [None, None]
1069 pars = [None, None]
1069 elif len(ps) == 1:
1070 elif len(ps) == 1:
1070 pars = [nodeids[ps[0]], None]
1071 pars = [nodeids[ps[0]], None]
1071 else:
1072 else:
1072 pars = [nodeids[p] for p in ps]
1073 pars = [nodeids[p] for p in ps]
1073 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1074 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1074 date=(id, 0),
1075 date=(id, 0),
1075 user="debugbuilddag",
1076 user="debugbuilddag",
1076 extra={'branch': atbranch})
1077 extra={'branch': atbranch})
1077 nodeid = repo.commitctx(cx)
1078 nodeid = repo.commitctx(cx)
1078 nodeids.append(nodeid)
1079 nodeids.append(nodeid)
1079 at = id
1080 at = id
1080 elif type == 'l':
1081 elif type == 'l':
1081 id, name = data
1082 id, name = data
1082 ui.note('tag %s\n' % name)
1083 ui.note('tag %s\n' % name)
1083 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1084 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1084 elif type == 'a':
1085 elif type == 'a':
1085 ui.note('branch %s\n' % data)
1086 ui.note('branch %s\n' % data)
1086 atbranch = data
1087 atbranch = data
1087 tr.close()
1088 tr.close()
1088 finally:
1089 finally:
1089 tr.release()
1090 tr.release()
1090
1091
1091 if tags:
1092 if tags:
1092 tagsf = repo.opener("localtags", "w")
1093 tagsf = repo.opener("localtags", "w")
1093 try:
1094 try:
1094 tagsf.write("".join(tags))
1095 tagsf.write("".join(tags))
1095 finally:
1096 finally:
1096 tagsf.close()
1097 tagsf.close()
1097
1098
1098 def debugcommands(ui, cmd='', *args):
1099 def debugcommands(ui, cmd='', *args):
1099 """list all available commands and options"""
1100 """list all available commands and options"""
1100 for cmd, vals in sorted(table.iteritems()):
1101 for cmd, vals in sorted(table.iteritems()):
1101 cmd = cmd.split('|')[0].strip('^')
1102 cmd = cmd.split('|')[0].strip('^')
1102 opts = ', '.join([i[1] for i in vals[1]])
1103 opts = ', '.join([i[1] for i in vals[1]])
1103 ui.write('%s: %s\n' % (cmd, opts))
1104 ui.write('%s: %s\n' % (cmd, opts))
1104
1105
1105 def debugcomplete(ui, cmd='', **opts):
1106 def debugcomplete(ui, cmd='', **opts):
1106 """returns the completion list associated with the given command"""
1107 """returns the completion list associated with the given command"""
1107
1108
1108 if opts.get('options'):
1109 if opts.get('options'):
1109 options = []
1110 options = []
1110 otables = [globalopts]
1111 otables = [globalopts]
1111 if cmd:
1112 if cmd:
1112 aliases, entry = cmdutil.findcmd(cmd, table, False)
1113 aliases, entry = cmdutil.findcmd(cmd, table, False)
1113 otables.append(entry[1])
1114 otables.append(entry[1])
1114 for t in otables:
1115 for t in otables:
1115 for o in t:
1116 for o in t:
1116 if "(DEPRECATED)" in o[3]:
1117 if "(DEPRECATED)" in o[3]:
1117 continue
1118 continue
1118 if o[0]:
1119 if o[0]:
1119 options.append('-%s' % o[0])
1120 options.append('-%s' % o[0])
1120 options.append('--%s' % o[1])
1121 options.append('--%s' % o[1])
1121 ui.write("%s\n" % "\n".join(options))
1122 ui.write("%s\n" % "\n".join(options))
1122 return
1123 return
1123
1124
1124 cmdlist = cmdutil.findpossible(cmd, table)
1125 cmdlist = cmdutil.findpossible(cmd, table)
1125 if ui.verbose:
1126 if ui.verbose:
1126 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1127 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1127 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1128 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1128
1129
1129 def debugfsinfo(ui, path = "."):
1130 def debugfsinfo(ui, path = "."):
1130 """show information detected about current filesystem"""
1131 """show information detected about current filesystem"""
1131 open('.debugfsinfo', 'w').write('')
1132 open('.debugfsinfo', 'w').write('')
1132 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1133 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1133 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1134 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1134 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1135 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1135 and 'yes' or 'no'))
1136 and 'yes' or 'no'))
1136 os.unlink('.debugfsinfo')
1137 os.unlink('.debugfsinfo')
1137
1138
1138 def debugrebuildstate(ui, repo, rev="tip"):
1139 def debugrebuildstate(ui, repo, rev="tip"):
1139 """rebuild the dirstate as it would look like for the given revision"""
1140 """rebuild the dirstate as it would look like for the given revision"""
1140 ctx = cmdutil.revsingle(repo, rev)
1141 ctx = cmdutil.revsingle(repo, rev)
1141 wlock = repo.wlock()
1142 wlock = repo.wlock()
1142 try:
1143 try:
1143 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1144 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1144 finally:
1145 finally:
1145 wlock.release()
1146 wlock.release()
1146
1147
1147 def debugcheckstate(ui, repo):
1148 def debugcheckstate(ui, repo):
1148 """validate the correctness of the current dirstate"""
1149 """validate the correctness of the current dirstate"""
1149 parent1, parent2 = repo.dirstate.parents()
1150 parent1, parent2 = repo.dirstate.parents()
1150 m1 = repo[parent1].manifest()
1151 m1 = repo[parent1].manifest()
1151 m2 = repo[parent2].manifest()
1152 m2 = repo[parent2].manifest()
1152 errors = 0
1153 errors = 0
1153 for f in repo.dirstate:
1154 for f in repo.dirstate:
1154 state = repo.dirstate[f]
1155 state = repo.dirstate[f]
1155 if state in "nr" and f not in m1:
1156 if state in "nr" and f not in m1:
1156 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1157 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1157 errors += 1
1158 errors += 1
1158 if state in "a" and f in m1:
1159 if state in "a" and f in m1:
1159 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1160 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1160 errors += 1
1161 errors += 1
1161 if state in "m" and f not in m1 and f not in m2:
1162 if state in "m" and f not in m1 and f not in m2:
1162 ui.warn(_("%s in state %s, but not in either manifest\n") %
1163 ui.warn(_("%s in state %s, but not in either manifest\n") %
1163 (f, state))
1164 (f, state))
1164 errors += 1
1165 errors += 1
1165 for f in m1:
1166 for f in m1:
1166 state = repo.dirstate[f]
1167 state = repo.dirstate[f]
1167 if state not in "nrm":
1168 if state not in "nrm":
1168 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1169 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1169 errors += 1
1170 errors += 1
1170 if errors:
1171 if errors:
1171 error = _(".hg/dirstate inconsistent with current parent's manifest")
1172 error = _(".hg/dirstate inconsistent with current parent's manifest")
1172 raise util.Abort(error)
1173 raise util.Abort(error)
1173
1174
1174 def showconfig(ui, repo, *values, **opts):
1175 def showconfig(ui, repo, *values, **opts):
1175 """show combined config settings from all hgrc files
1176 """show combined config settings from all hgrc files
1176
1177
1177 With no arguments, print names and values of all config items.
1178 With no arguments, print names and values of all config items.
1178
1179
1179 With one argument of the form section.name, print just the value
1180 With one argument of the form section.name, print just the value
1180 of that config item.
1181 of that config item.
1181
1182
1182 With multiple arguments, print names and values of all config
1183 With multiple arguments, print names and values of all config
1183 items with matching section names.
1184 items with matching section names.
1184
1185
1185 With --debug, the source (filename and line number) is printed
1186 With --debug, the source (filename and line number) is printed
1186 for each config item.
1187 for each config item.
1187
1188
1188 Returns 0 on success.
1189 Returns 0 on success.
1189 """
1190 """
1190
1191
1191 for f in scmutil.rcpath():
1192 for f in scmutil.rcpath():
1192 ui.debug(_('read config from: %s\n') % f)
1193 ui.debug(_('read config from: %s\n') % f)
1193 untrusted = bool(opts.get('untrusted'))
1194 untrusted = bool(opts.get('untrusted'))
1194 if values:
1195 if values:
1195 sections = [v for v in values if '.' not in v]
1196 sections = [v for v in values if '.' not in v]
1196 items = [v for v in values if '.' in v]
1197 items = [v for v in values if '.' in v]
1197 if len(items) > 1 or items and sections:
1198 if len(items) > 1 or items and sections:
1198 raise util.Abort(_('only one config item permitted'))
1199 raise util.Abort(_('only one config item permitted'))
1199 for section, name, value in ui.walkconfig(untrusted=untrusted):
1200 for section, name, value in ui.walkconfig(untrusted=untrusted):
1200 value = str(value).replace('\n', '\\n')
1201 value = str(value).replace('\n', '\\n')
1201 sectname = section + '.' + name
1202 sectname = section + '.' + name
1202 if values:
1203 if values:
1203 for v in values:
1204 for v in values:
1204 if v == section:
1205 if v == section:
1205 ui.debug('%s: ' %
1206 ui.debug('%s: ' %
1206 ui.configsource(section, name, untrusted))
1207 ui.configsource(section, name, untrusted))
1207 ui.write('%s=%s\n' % (sectname, value))
1208 ui.write('%s=%s\n' % (sectname, value))
1208 elif v == sectname:
1209 elif v == sectname:
1209 ui.debug('%s: ' %
1210 ui.debug('%s: ' %
1210 ui.configsource(section, name, untrusted))
1211 ui.configsource(section, name, untrusted))
1211 ui.write(value, '\n')
1212 ui.write(value, '\n')
1212 else:
1213 else:
1213 ui.debug('%s: ' %
1214 ui.debug('%s: ' %
1214 ui.configsource(section, name, untrusted))
1215 ui.configsource(section, name, untrusted))
1215 ui.write('%s=%s\n' % (sectname, value))
1216 ui.write('%s=%s\n' % (sectname, value))
1216
1217
1217 def debugknown(ui, repopath, *ids, **opts):
1218 def debugknown(ui, repopath, *ids, **opts):
1218 """test whether node ids are known to a repo
1219 """test whether node ids are known to a repo
1219
1220
1220 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1221 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1221 indicating unknown/known.
1222 indicating unknown/known.
1222 """
1223 """
1223 repo = hg.repository(ui, repopath)
1224 repo = hg.repository(ui, repopath)
1224 if not repo.capable('known'):
1225 if not repo.capable('known'):
1225 raise util.Abort("known() not supported by target repository")
1226 raise util.Abort("known() not supported by target repository")
1226 flags = repo.known([bin(s) for s in ids])
1227 flags = repo.known([bin(s) for s in ids])
1227 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1228 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1228
1229
1229 def debugbundle(ui, bundlepath, all=None, **opts):
1230 def debugbundle(ui, bundlepath, all=None, **opts):
1230 """lists the contents of a bundle"""
1231 """lists the contents of a bundle"""
1231 f = url.open(ui, bundlepath)
1232 f = url.open(ui, bundlepath)
1232 try:
1233 try:
1233 gen = changegroup.readbundle(f, bundlepath)
1234 gen = changegroup.readbundle(f, bundlepath)
1234 if all:
1235 if all:
1235 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1236 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1236
1237
1237 def showchunks(named):
1238 def showchunks(named):
1238 ui.write("\n%s\n" % named)
1239 ui.write("\n%s\n" % named)
1239 chain = None
1240 chain = None
1240 while 1:
1241 while 1:
1241 chunkdata = gen.deltachunk(chain)
1242 chunkdata = gen.deltachunk(chain)
1242 if not chunkdata:
1243 if not chunkdata:
1243 break
1244 break
1244 node = chunkdata['node']
1245 node = chunkdata['node']
1245 p1 = chunkdata['p1']
1246 p1 = chunkdata['p1']
1246 p2 = chunkdata['p2']
1247 p2 = chunkdata['p2']
1247 cs = chunkdata['cs']
1248 cs = chunkdata['cs']
1248 deltabase = chunkdata['deltabase']
1249 deltabase = chunkdata['deltabase']
1249 delta = chunkdata['delta']
1250 delta = chunkdata['delta']
1250 ui.write("%s %s %s %s %s %s\n" %
1251 ui.write("%s %s %s %s %s %s\n" %
1251 (hex(node), hex(p1), hex(p2),
1252 (hex(node), hex(p1), hex(p2),
1252 hex(cs), hex(deltabase), len(delta)))
1253 hex(cs), hex(deltabase), len(delta)))
1253 chain = node
1254 chain = node
1254
1255
1255 chunkdata = gen.changelogheader()
1256 chunkdata = gen.changelogheader()
1256 showchunks("changelog")
1257 showchunks("changelog")
1257 chunkdata = gen.manifestheader()
1258 chunkdata = gen.manifestheader()
1258 showchunks("manifest")
1259 showchunks("manifest")
1259 while 1:
1260 while 1:
1260 chunkdata = gen.filelogheader()
1261 chunkdata = gen.filelogheader()
1261 if not chunkdata:
1262 if not chunkdata:
1262 break
1263 break
1263 fname = chunkdata['filename']
1264 fname = chunkdata['filename']
1264 showchunks(fname)
1265 showchunks(fname)
1265 else:
1266 else:
1266 chunkdata = gen.changelogheader()
1267 chunkdata = gen.changelogheader()
1267 chain = None
1268 chain = None
1268 while 1:
1269 while 1:
1269 chunkdata = gen.deltachunk(chain)
1270 chunkdata = gen.deltachunk(chain)
1270 if not chunkdata:
1271 if not chunkdata:
1271 break
1272 break
1272 node = chunkdata['node']
1273 node = chunkdata['node']
1273 ui.write("%s\n" % hex(node))
1274 ui.write("%s\n" % hex(node))
1274 chain = node
1275 chain = node
1275 finally:
1276 finally:
1276 f.close()
1277 f.close()
1277
1278
1278 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1279 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1279 """retrieves a bundle from a repo
1280 """retrieves a bundle from a repo
1280
1281
1281 Every ID must be a full-length hex node id string. Saves the bundle to the
1282 Every ID must be a full-length hex node id string. Saves the bundle to the
1282 given file.
1283 given file.
1283 """
1284 """
1284 repo = hg.repository(ui, repopath)
1285 repo = hg.repository(ui, repopath)
1285 if not repo.capable('getbundle'):
1286 if not repo.capable('getbundle'):
1286 raise util.Abort("getbundle() not supported by target repository")
1287 raise util.Abort("getbundle() not supported by target repository")
1287 args = {}
1288 args = {}
1288 if common:
1289 if common:
1289 args['common'] = [bin(s) for s in common]
1290 args['common'] = [bin(s) for s in common]
1290 if head:
1291 if head:
1291 args['heads'] = [bin(s) for s in head]
1292 args['heads'] = [bin(s) for s in head]
1292 bundle = repo.getbundle('debug', **args)
1293 bundle = repo.getbundle('debug', **args)
1293
1294
1294 bundletype = opts.get('type', 'bzip2').lower()
1295 bundletype = opts.get('type', 'bzip2').lower()
1295 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1296 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1296 bundletype = btypes.get(bundletype)
1297 bundletype = btypes.get(bundletype)
1297 if bundletype not in changegroup.bundletypes:
1298 if bundletype not in changegroup.bundletypes:
1298 raise util.Abort(_('unknown bundle type specified with --type'))
1299 raise util.Abort(_('unknown bundle type specified with --type'))
1299 changegroup.writebundle(bundle, bundlepath, bundletype)
1300 changegroup.writebundle(bundle, bundlepath, bundletype)
1300
1301
1301 def debugpushkey(ui, repopath, namespace, *keyinfo):
1302 def debugpushkey(ui, repopath, namespace, *keyinfo):
1302 '''access the pushkey key/value protocol
1303 '''access the pushkey key/value protocol
1303
1304
1304 With two args, list the keys in the given namespace.
1305 With two args, list the keys in the given namespace.
1305
1306
1306 With five args, set a key to new if it currently is set to old.
1307 With five args, set a key to new if it currently is set to old.
1307 Reports success or failure.
1308 Reports success or failure.
1308 '''
1309 '''
1309
1310
1310 target = hg.repository(ui, repopath)
1311 target = hg.repository(ui, repopath)
1311 if keyinfo:
1312 if keyinfo:
1312 key, old, new = keyinfo
1313 key, old, new = keyinfo
1313 r = target.pushkey(namespace, key, old, new)
1314 r = target.pushkey(namespace, key, old, new)
1314 ui.status(str(r) + '\n')
1315 ui.status(str(r) + '\n')
1315 return not r
1316 return not r
1316 else:
1317 else:
1317 for k, v in target.listkeys(namespace).iteritems():
1318 for k, v in target.listkeys(namespace).iteritems():
1318 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1319 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1319 v.encode('string-escape')))
1320 v.encode('string-escape')))
1320
1321
1321 def debugrevspec(ui, repo, expr):
1322 def debugrevspec(ui, repo, expr):
1322 '''parse and apply a revision specification'''
1323 '''parse and apply a revision specification'''
1323 if ui.verbose:
1324 if ui.verbose:
1324 tree = revset.parse(expr)[0]
1325 tree = revset.parse(expr)[0]
1325 ui.note(tree, "\n")
1326 ui.note(tree, "\n")
1326 newtree = revset.findaliases(ui, tree)
1327 newtree = revset.findaliases(ui, tree)
1327 if newtree != tree:
1328 if newtree != tree:
1328 ui.note(newtree, "\n")
1329 ui.note(newtree, "\n")
1329 func = revset.match(ui, expr)
1330 func = revset.match(ui, expr)
1330 for c in func(repo, range(len(repo))):
1331 for c in func(repo, range(len(repo))):
1331 ui.write("%s\n" % c)
1332 ui.write("%s\n" % c)
1332
1333
1333 def debugsetparents(ui, repo, rev1, rev2=None):
1334 def debugsetparents(ui, repo, rev1, rev2=None):
1334 """manually set the parents of the current working directory
1335 """manually set the parents of the current working directory
1335
1336
1336 This is useful for writing repository conversion tools, but should
1337 This is useful for writing repository conversion tools, but should
1337 be used with care.
1338 be used with care.
1338
1339
1339 Returns 0 on success.
1340 Returns 0 on success.
1340 """
1341 """
1341
1342
1342 r1 = cmdutil.revsingle(repo, rev1).node()
1343 r1 = cmdutil.revsingle(repo, rev1).node()
1343 r2 = cmdutil.revsingle(repo, rev2, 'null').node()
1344 r2 = cmdutil.revsingle(repo, rev2, 'null').node()
1344
1345
1345 wlock = repo.wlock()
1346 wlock = repo.wlock()
1346 try:
1347 try:
1347 repo.dirstate.setparents(r1, r2)
1348 repo.dirstate.setparents(r1, r2)
1348 finally:
1349 finally:
1349 wlock.release()
1350 wlock.release()
1350
1351
1351 def debugstate(ui, repo, nodates=None, datesort=None):
1352 def debugstate(ui, repo, nodates=None, datesort=None):
1352 """show the contents of the current dirstate"""
1353 """show the contents of the current dirstate"""
1353 timestr = ""
1354 timestr = ""
1354 showdate = not nodates
1355 showdate = not nodates
1355 if datesort:
1356 if datesort:
1356 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
1357 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
1357 else:
1358 else:
1358 keyfunc = None # sort by filename
1359 keyfunc = None # sort by filename
1359 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
1360 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
1360 if showdate:
1361 if showdate:
1361 if ent[3] == -1:
1362 if ent[3] == -1:
1362 # Pad or slice to locale representation
1363 # Pad or slice to locale representation
1363 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1364 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1364 time.localtime(0)))
1365 time.localtime(0)))
1365 timestr = 'unset'
1366 timestr = 'unset'
1366 timestr = (timestr[:locale_len] +
1367 timestr = (timestr[:locale_len] +
1367 ' ' * (locale_len - len(timestr)))
1368 ' ' * (locale_len - len(timestr)))
1368 else:
1369 else:
1369 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1370 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1370 time.localtime(ent[3]))
1371 time.localtime(ent[3]))
1371 if ent[1] & 020000:
1372 if ent[1] & 020000:
1372 mode = 'lnk'
1373 mode = 'lnk'
1373 else:
1374 else:
1374 mode = '%3o' % (ent[1] & 0777)
1375 mode = '%3o' % (ent[1] & 0777)
1375 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1376 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1376 for f in repo.dirstate.copies():
1377 for f in repo.dirstate.copies():
1377 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1378 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1378
1379
1379 def debugsub(ui, repo, rev=None):
1380 def debugsub(ui, repo, rev=None):
1380 ctx = cmdutil.revsingle(repo, rev, None)
1381 ctx = cmdutil.revsingle(repo, rev, None)
1381 for k, v in sorted(ctx.substate.items()):
1382 for k, v in sorted(ctx.substate.items()):
1382 ui.write('path %s\n' % k)
1383 ui.write('path %s\n' % k)
1383 ui.write(' source %s\n' % v[0])
1384 ui.write(' source %s\n' % v[0])
1384 ui.write(' revision %s\n' % v[1])
1385 ui.write(' revision %s\n' % v[1])
1385
1386
1386 def debugdag(ui, repo, file_=None, *revs, **opts):
1387 def debugdag(ui, repo, file_=None, *revs, **opts):
1387 """format the changelog or an index DAG as a concise textual description
1388 """format the changelog or an index DAG as a concise textual description
1388
1389
1389 If you pass a revlog index, the revlog's DAG is emitted. If you list
1390 If you pass a revlog index, the revlog's DAG is emitted. If you list
1390 revision numbers, they get labelled in the output as rN.
1391 revision numbers, they get labelled in the output as rN.
1391
1392
1392 Otherwise, the changelog DAG of the current repo is emitted.
1393 Otherwise, the changelog DAG of the current repo is emitted.
1393 """
1394 """
1394 spaces = opts.get('spaces')
1395 spaces = opts.get('spaces')
1395 dots = opts.get('dots')
1396 dots = opts.get('dots')
1396 if file_:
1397 if file_:
1397 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1398 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1398 revs = set((int(r) for r in revs))
1399 revs = set((int(r) for r in revs))
1399 def events():
1400 def events():
1400 for r in rlog:
1401 for r in rlog:
1401 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1402 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1402 if r in revs:
1403 if r in revs:
1403 yield 'l', (r, "r%i" % r)
1404 yield 'l', (r, "r%i" % r)
1404 elif repo:
1405 elif repo:
1405 cl = repo.changelog
1406 cl = repo.changelog
1406 tags = opts.get('tags')
1407 tags = opts.get('tags')
1407 branches = opts.get('branches')
1408 branches = opts.get('branches')
1408 if tags:
1409 if tags:
1409 labels = {}
1410 labels = {}
1410 for l, n in repo.tags().items():
1411 for l, n in repo.tags().items():
1411 labels.setdefault(cl.rev(n), []).append(l)
1412 labels.setdefault(cl.rev(n), []).append(l)
1412 def events():
1413 def events():
1413 b = "default"
1414 b = "default"
1414 for r in cl:
1415 for r in cl:
1415 if branches:
1416 if branches:
1416 newb = cl.read(cl.node(r))[5]['branch']
1417 newb = cl.read(cl.node(r))[5]['branch']
1417 if newb != b:
1418 if newb != b:
1418 yield 'a', newb
1419 yield 'a', newb
1419 b = newb
1420 b = newb
1420 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1421 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1421 if tags:
1422 if tags:
1422 ls = labels.get(r)
1423 ls = labels.get(r)
1423 if ls:
1424 if ls:
1424 for l in ls:
1425 for l in ls:
1425 yield 'l', (r, l)
1426 yield 'l', (r, l)
1426 else:
1427 else:
1427 raise util.Abort(_('need repo for changelog dag'))
1428 raise util.Abort(_('need repo for changelog dag'))
1428
1429
1429 for line in dagparser.dagtextlines(events(),
1430 for line in dagparser.dagtextlines(events(),
1430 addspaces=spaces,
1431 addspaces=spaces,
1431 wraplabels=True,
1432 wraplabels=True,
1432 wrapannotations=True,
1433 wrapannotations=True,
1433 wrapnonlinear=dots,
1434 wrapnonlinear=dots,
1434 usedots=dots,
1435 usedots=dots,
1435 maxlinewidth=70):
1436 maxlinewidth=70):
1436 ui.write(line)
1437 ui.write(line)
1437 ui.write("\n")
1438 ui.write("\n")
1438
1439
1439 def debugdata(ui, repo, file_, rev):
1440 def debugdata(ui, repo, file_, rev):
1440 """dump the contents of a data file revision"""
1441 """dump the contents of a data file revision"""
1441 r = None
1442 r = None
1442 if repo:
1443 if repo:
1443 filelog = repo.file(file_)
1444 filelog = repo.file(file_)
1444 if len(filelog):
1445 if len(filelog):
1445 r = filelog
1446 r = filelog
1446 if not r:
1447 if not r:
1447 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
1448 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
1448 file_[:-2] + ".i")
1449 file_[:-2] + ".i")
1449 try:
1450 try:
1450 ui.write(r.revision(r.lookup(rev)))
1451 ui.write(r.revision(r.lookup(rev)))
1451 except KeyError:
1452 except KeyError:
1452 raise util.Abort(_('invalid revision identifier %s') % rev)
1453 raise util.Abort(_('invalid revision identifier %s') % rev)
1453
1454
1454 def debugdate(ui, date, range=None, **opts):
1455 def debugdate(ui, date, range=None, **opts):
1455 """parse and display a date"""
1456 """parse and display a date"""
1456 if opts["extended"]:
1457 if opts["extended"]:
1457 d = util.parsedate(date, util.extendeddateformats)
1458 d = util.parsedate(date, util.extendeddateformats)
1458 else:
1459 else:
1459 d = util.parsedate(date)
1460 d = util.parsedate(date)
1460 ui.write("internal: %s %s\n" % d)
1461 ui.write("internal: %s %s\n" % d)
1461 ui.write("standard: %s\n" % util.datestr(d))
1462 ui.write("standard: %s\n" % util.datestr(d))
1462 if range:
1463 if range:
1463 m = util.matchdate(range)
1464 m = util.matchdate(range)
1464 ui.write("match: %s\n" % m(d[0]))
1465 ui.write("match: %s\n" % m(d[0]))
1465
1466
1466 def debugignore(ui, repo, *values, **opts):
1467 def debugignore(ui, repo, *values, **opts):
1467 """display the combined ignore pattern"""
1468 """display the combined ignore pattern"""
1468 ignore = repo.dirstate._ignore
1469 ignore = repo.dirstate._ignore
1469 if hasattr(ignore, 'includepat'):
1470 if hasattr(ignore, 'includepat'):
1470 ui.write("%s\n" % ignore.includepat)
1471 ui.write("%s\n" % ignore.includepat)
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
1477 if repo:
1537 if repo:
1478 filelog = repo.file(file_)
1538 filelog = repo.file(file_)
1479 if len(filelog):
1539 if len(filelog):
1480 r = filelog
1540 r = filelog
1481
1541
1482 format = opts.get('format', 0)
1542 format = opts.get('format', 0)
1483 if format not in (0, 1):
1543 if format not in (0, 1):
1484 raise util.Abort(_("unknown format %d") % format)
1544 raise util.Abort(_("unknown format %d") % format)
1485
1545
1486 if not r:
1546 if not r:
1487 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1547 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1488
1548
1489 if format == 0:
1549 if format == 0:
1490 ui.write(" rev offset length base linkrev"
1550 ui.write(" rev offset length base linkrev"
1491 " nodeid p1 p2\n")
1551 " nodeid p1 p2\n")
1492 elif format == 1:
1552 elif format == 1:
1493 ui.write(" rev flag offset length"
1553 ui.write(" rev flag offset length"
1494 " size base link p1 p2 nodeid\n")
1554 " size base link p1 p2 nodeid\n")
1495
1555
1496 for i in r:
1556 for i in r:
1497 node = r.node(i)
1557 node = r.node(i)
1498 if format == 0:
1558 if format == 0:
1499 try:
1559 try:
1500 pp = r.parents(node)
1560 pp = r.parents(node)
1501 except:
1561 except:
1502 pp = [nullid, nullid]
1562 pp = [nullid, nullid]
1503 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1563 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1504 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1564 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1505 short(node), short(pp[0]), short(pp[1])))
1565 short(node), short(pp[0]), short(pp[1])))
1506 elif format == 1:
1566 elif format == 1:
1507 pr = r.parentrevs(i)
1567 pr = r.parentrevs(i)
1508 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1568 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1509 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1569 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1510 r.base(i), r.linkrev(i), pr[0], pr[1], short(node)))
1570 r.base(i), r.linkrev(i), pr[0], pr[1], short(node)))
1511
1571
1512 def debugindexdot(ui, repo, file_):
1572 def debugindexdot(ui, repo, file_):
1513 """dump an index DAG as a graphviz dot file"""
1573 """dump an index DAG as a graphviz dot file"""
1514 r = None
1574 r = None
1515 if repo:
1575 if repo:
1516 filelog = repo.file(file_)
1576 filelog = repo.file(file_)
1517 if len(filelog):
1577 if len(filelog):
1518 r = filelog
1578 r = filelog
1519 if not r:
1579 if not r:
1520 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1580 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1521 ui.write("digraph G {\n")
1581 ui.write("digraph G {\n")
1522 for i in r:
1582 for i in r:
1523 node = r.node(i)
1583 node = r.node(i)
1524 pp = r.parents(node)
1584 pp = r.parents(node)
1525 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1585 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1526 if pp[1] != nullid:
1586 if pp[1] != nullid:
1527 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1587 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1528 ui.write("}\n")
1588 ui.write("}\n")
1529
1589
1530 def debuginstall(ui):
1590 def debuginstall(ui):
1531 '''test Mercurial installation
1591 '''test Mercurial installation
1532
1592
1533 Returns 0 on success.
1593 Returns 0 on success.
1534 '''
1594 '''
1535
1595
1536 def writetemp(contents):
1596 def writetemp(contents):
1537 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1597 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1538 f = os.fdopen(fd, "wb")
1598 f = os.fdopen(fd, "wb")
1539 f.write(contents)
1599 f.write(contents)
1540 f.close()
1600 f.close()
1541 return name
1601 return name
1542
1602
1543 problems = 0
1603 problems = 0
1544
1604
1545 # encoding
1605 # encoding
1546 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1606 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1547 try:
1607 try:
1548 encoding.fromlocal("test")
1608 encoding.fromlocal("test")
1549 except util.Abort, inst:
1609 except util.Abort, inst:
1550 ui.write(" %s\n" % inst)
1610 ui.write(" %s\n" % inst)
1551 ui.write(_(" (check that your locale is properly set)\n"))
1611 ui.write(_(" (check that your locale is properly set)\n"))
1552 problems += 1
1612 problems += 1
1553
1613
1554 # compiled modules
1614 # compiled modules
1555 ui.status(_("Checking installed modules (%s)...\n")
1615 ui.status(_("Checking installed modules (%s)...\n")
1556 % os.path.dirname(__file__))
1616 % os.path.dirname(__file__))
1557 try:
1617 try:
1558 import bdiff, mpatch, base85, osutil
1618 import bdiff, mpatch, base85, osutil
1559 except Exception, inst:
1619 except Exception, inst:
1560 ui.write(" %s\n" % inst)
1620 ui.write(" %s\n" % inst)
1561 ui.write(_(" One or more extensions could not be found"))
1621 ui.write(_(" One or more extensions could not be found"))
1562 ui.write(_(" (check that you compiled the extensions)\n"))
1622 ui.write(_(" (check that you compiled the extensions)\n"))
1563 problems += 1
1623 problems += 1
1564
1624
1565 # templates
1625 # templates
1566 ui.status(_("Checking templates...\n"))
1626 ui.status(_("Checking templates...\n"))
1567 try:
1627 try:
1568 import templater
1628 import templater
1569 templater.templater(templater.templatepath("map-cmdline.default"))
1629 templater.templater(templater.templatepath("map-cmdline.default"))
1570 except Exception, inst:
1630 except Exception, inst:
1571 ui.write(" %s\n" % inst)
1631 ui.write(" %s\n" % inst)
1572 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1632 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1573 problems += 1
1633 problems += 1
1574
1634
1575 # editor
1635 # editor
1576 ui.status(_("Checking commit editor...\n"))
1636 ui.status(_("Checking commit editor...\n"))
1577 editor = ui.geteditor()
1637 editor = ui.geteditor()
1578 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1638 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1579 if not cmdpath:
1639 if not cmdpath:
1580 if editor == 'vi':
1640 if editor == 'vi':
1581 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1641 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1582 ui.write(_(" (specify a commit editor in your configuration"
1642 ui.write(_(" (specify a commit editor in your configuration"
1583 " file)\n"))
1643 " file)\n"))
1584 else:
1644 else:
1585 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1645 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1586 ui.write(_(" (specify a commit editor in your configuration"
1646 ui.write(_(" (specify a commit editor in your configuration"
1587 " file)\n"))
1647 " file)\n"))
1588 problems += 1
1648 problems += 1
1589
1649
1590 # check username
1650 # check username
1591 ui.status(_("Checking username...\n"))
1651 ui.status(_("Checking username...\n"))
1592 try:
1652 try:
1593 ui.username()
1653 ui.username()
1594 except util.Abort, e:
1654 except util.Abort, e:
1595 ui.write(" %s\n" % e)
1655 ui.write(" %s\n" % e)
1596 ui.write(_(" (specify a username in your configuration file)\n"))
1656 ui.write(_(" (specify a username in your configuration file)\n"))
1597 problems += 1
1657 problems += 1
1598
1658
1599 if not problems:
1659 if not problems:
1600 ui.status(_("No problems detected\n"))
1660 ui.status(_("No problems detected\n"))
1601 else:
1661 else:
1602 ui.write(_("%s problems detected,"
1662 ui.write(_("%s problems detected,"
1603 " please check your install!\n") % problems)
1663 " please check your install!\n") % problems)
1604
1664
1605 return problems
1665 return problems
1606
1666
1607 def debugrename(ui, repo, file1, *pats, **opts):
1667 def debugrename(ui, repo, file1, *pats, **opts):
1608 """dump rename information"""
1668 """dump rename information"""
1609
1669
1610 ctx = cmdutil.revsingle(repo, opts.get('rev'))
1670 ctx = cmdutil.revsingle(repo, opts.get('rev'))
1611 m = cmdutil.match(repo, (file1,) + pats, opts)
1671 m = cmdutil.match(repo, (file1,) + pats, opts)
1612 for abs in ctx.walk(m):
1672 for abs in ctx.walk(m):
1613 fctx = ctx[abs]
1673 fctx = ctx[abs]
1614 o = fctx.filelog().renamed(fctx.filenode())
1674 o = fctx.filelog().renamed(fctx.filenode())
1615 rel = m.rel(abs)
1675 rel = m.rel(abs)
1616 if o:
1676 if o:
1617 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1677 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1618 else:
1678 else:
1619 ui.write(_("%s not renamed\n") % rel)
1679 ui.write(_("%s not renamed\n") % rel)
1620
1680
1621 def debugwalk(ui, repo, *pats, **opts):
1681 def debugwalk(ui, repo, *pats, **opts):
1622 """show how files match on given patterns"""
1682 """show how files match on given patterns"""
1623 m = cmdutil.match(repo, pats, opts)
1683 m = cmdutil.match(repo, pats, opts)
1624 items = list(repo.walk(m))
1684 items = list(repo.walk(m))
1625 if not items:
1685 if not items:
1626 return
1686 return
1627 fmt = 'f %%-%ds %%-%ds %%s' % (
1687 fmt = 'f %%-%ds %%-%ds %%s' % (
1628 max([len(abs) for abs in items]),
1688 max([len(abs) for abs in items]),
1629 max([len(m.rel(abs)) for abs in items]))
1689 max([len(m.rel(abs)) for abs in items]))
1630 for abs in items:
1690 for abs in items:
1631 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1691 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1632 ui.write("%s\n" % line.rstrip())
1692 ui.write("%s\n" % line.rstrip())
1633
1693
1634 def debugwireargs(ui, repopath, *vals, **opts):
1694 def debugwireargs(ui, repopath, *vals, **opts):
1635 repo = hg.repository(hg.remoteui(ui, opts), repopath)
1695 repo = hg.repository(hg.remoteui(ui, opts), repopath)
1636 for opt in remoteopts:
1696 for opt in remoteopts:
1637 del opts[opt[1]]
1697 del opts[opt[1]]
1638 args = {}
1698 args = {}
1639 for k, v in opts.iteritems():
1699 for k, v in opts.iteritems():
1640 if v:
1700 if v:
1641 args[k] = v
1701 args[k] = v
1642 # run twice to check that we don't mess up the stream for the next command
1702 # run twice to check that we don't mess up the stream for the next command
1643 res1 = repo.debugwireargs(*vals, **args)
1703 res1 = repo.debugwireargs(*vals, **args)
1644 res2 = repo.debugwireargs(*vals, **args)
1704 res2 = repo.debugwireargs(*vals, **args)
1645 ui.write("%s\n" % res1)
1705 ui.write("%s\n" % res1)
1646 if res1 != res2:
1706 if res1 != res2:
1647 ui.warn("%s\n" % res2)
1707 ui.warn("%s\n" % res2)
1648
1708
1649 def diff(ui, repo, *pats, **opts):
1709 def diff(ui, repo, *pats, **opts):
1650 """diff repository (or selected files)
1710 """diff repository (or selected files)
1651
1711
1652 Show differences between revisions for the specified files.
1712 Show differences between revisions for the specified files.
1653
1713
1654 Differences between files are shown using the unified diff format.
1714 Differences between files are shown using the unified diff format.
1655
1715
1656 .. note::
1716 .. note::
1657 diff may generate unexpected results for merges, as it will
1717 diff may generate unexpected results for merges, as it will
1658 default to comparing against the working directory's first
1718 default to comparing against the working directory's first
1659 parent changeset if no revisions are specified.
1719 parent changeset if no revisions are specified.
1660
1720
1661 When two revision arguments are given, then changes are shown
1721 When two revision arguments are given, then changes are shown
1662 between those revisions. If only one revision is specified then
1722 between those revisions. If only one revision is specified then
1663 that revision is compared to the working directory, and, when no
1723 that revision is compared to the working directory, and, when no
1664 revisions are specified, the working directory files are compared
1724 revisions are specified, the working directory files are compared
1665 to its parent.
1725 to its parent.
1666
1726
1667 Alternatively you can specify -c/--change with a revision to see
1727 Alternatively you can specify -c/--change with a revision to see
1668 the changes in that changeset relative to its first parent.
1728 the changes in that changeset relative to its first parent.
1669
1729
1670 Without the -a/--text option, diff will avoid generating diffs of
1730 Without the -a/--text option, diff will avoid generating diffs of
1671 files it detects as binary. With -a, diff will generate a diff
1731 files it detects as binary. With -a, diff will generate a diff
1672 anyway, probably with undesirable results.
1732 anyway, probably with undesirable results.
1673
1733
1674 Use the -g/--git option to generate diffs in the git extended diff
1734 Use the -g/--git option to generate diffs in the git extended diff
1675 format. For more information, read :hg:`help diffs`.
1735 format. For more information, read :hg:`help diffs`.
1676
1736
1677 Returns 0 on success.
1737 Returns 0 on success.
1678 """
1738 """
1679
1739
1680 revs = opts.get('rev')
1740 revs = opts.get('rev')
1681 change = opts.get('change')
1741 change = opts.get('change')
1682 stat = opts.get('stat')
1742 stat = opts.get('stat')
1683 reverse = opts.get('reverse')
1743 reverse = opts.get('reverse')
1684
1744
1685 if revs and change:
1745 if revs and change:
1686 msg = _('cannot specify --rev and --change at the same time')
1746 msg = _('cannot specify --rev and --change at the same time')
1687 raise util.Abort(msg)
1747 raise util.Abort(msg)
1688 elif change:
1748 elif change:
1689 node2 = cmdutil.revsingle(repo, change, None).node()
1749 node2 = cmdutil.revsingle(repo, change, None).node()
1690 node1 = repo[node2].p1().node()
1750 node1 = repo[node2].p1().node()
1691 else:
1751 else:
1692 node1, node2 = cmdutil.revpair(repo, revs)
1752 node1, node2 = cmdutil.revpair(repo, revs)
1693
1753
1694 if reverse:
1754 if reverse:
1695 node1, node2 = node2, node1
1755 node1, node2 = node2, node1
1696
1756
1697 diffopts = patch.diffopts(ui, opts)
1757 diffopts = patch.diffopts(ui, opts)
1698 m = cmdutil.match(repo, pats, opts)
1758 m = cmdutil.match(repo, pats, opts)
1699 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1759 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1700 listsubrepos=opts.get('subrepos'))
1760 listsubrepos=opts.get('subrepos'))
1701
1761
1702 def export(ui, repo, *changesets, **opts):
1762 def export(ui, repo, *changesets, **opts):
1703 """dump the header and diffs for one or more changesets
1763 """dump the header and diffs for one or more changesets
1704
1764
1705 Print the changeset header and diffs for one or more revisions.
1765 Print the changeset header and diffs for one or more revisions.
1706
1766
1707 The information shown in the changeset header is: author, date,
1767 The information shown in the changeset header is: author, date,
1708 branch name (if non-default), changeset hash, parent(s) and commit
1768 branch name (if non-default), changeset hash, parent(s) and commit
1709 comment.
1769 comment.
1710
1770
1711 .. note::
1771 .. note::
1712 export may generate unexpected diff output for merge
1772 export may generate unexpected diff output for merge
1713 changesets, as it will compare the merge changeset against its
1773 changesets, as it will compare the merge changeset against its
1714 first parent only.
1774 first parent only.
1715
1775
1716 Output may be to a file, in which case the name of the file is
1776 Output may be to a file, in which case the name of the file is
1717 given using a format string. The formatting rules are as follows:
1777 given using a format string. The formatting rules are as follows:
1718
1778
1719 :``%%``: literal "%" character
1779 :``%%``: literal "%" character
1720 :``%H``: changeset hash (40 hexadecimal digits)
1780 :``%H``: changeset hash (40 hexadecimal digits)
1721 :``%N``: number of patches being generated
1781 :``%N``: number of patches being generated
1722 :``%R``: changeset revision number
1782 :``%R``: changeset revision number
1723 :``%b``: basename of the exporting repository
1783 :``%b``: basename of the exporting repository
1724 :``%h``: short-form changeset hash (12 hexadecimal digits)
1784 :``%h``: short-form changeset hash (12 hexadecimal digits)
1725 :``%n``: zero-padded sequence number, starting at 1
1785 :``%n``: zero-padded sequence number, starting at 1
1726 :``%r``: zero-padded changeset revision number
1786 :``%r``: zero-padded changeset revision number
1727
1787
1728 Without the -a/--text option, export will avoid generating diffs
1788 Without the -a/--text option, export will avoid generating diffs
1729 of files it detects as binary. With -a, export will generate a
1789 of files it detects as binary. With -a, export will generate a
1730 diff anyway, probably with undesirable results.
1790 diff anyway, probably with undesirable results.
1731
1791
1732 Use the -g/--git option to generate diffs in the git extended diff
1792 Use the -g/--git option to generate diffs in the git extended diff
1733 format. See :hg:`help diffs` for more information.
1793 format. See :hg:`help diffs` for more information.
1734
1794
1735 With the --switch-parent option, the diff will be against the
1795 With the --switch-parent option, the diff will be against the
1736 second parent. It can be useful to review a merge.
1796 second parent. It can be useful to review a merge.
1737
1797
1738 Returns 0 on success.
1798 Returns 0 on success.
1739 """
1799 """
1740 changesets += tuple(opts.get('rev', []))
1800 changesets += tuple(opts.get('rev', []))
1741 if not changesets:
1801 if not changesets:
1742 raise util.Abort(_("export requires at least one changeset"))
1802 raise util.Abort(_("export requires at least one changeset"))
1743 revs = cmdutil.revrange(repo, changesets)
1803 revs = cmdutil.revrange(repo, changesets)
1744 if len(revs) > 1:
1804 if len(revs) > 1:
1745 ui.note(_('exporting patches:\n'))
1805 ui.note(_('exporting patches:\n'))
1746 else:
1806 else:
1747 ui.note(_('exporting patch:\n'))
1807 ui.note(_('exporting patch:\n'))
1748 cmdutil.export(repo, revs, template=opts.get('output'),
1808 cmdutil.export(repo, revs, template=opts.get('output'),
1749 switch_parent=opts.get('switch_parent'),
1809 switch_parent=opts.get('switch_parent'),
1750 opts=patch.diffopts(ui, opts))
1810 opts=patch.diffopts(ui, opts))
1751
1811
1752 def forget(ui, repo, *pats, **opts):
1812 def forget(ui, repo, *pats, **opts):
1753 """forget the specified files on the next commit
1813 """forget the specified files on the next commit
1754
1814
1755 Mark the specified files so they will no longer be tracked
1815 Mark the specified files so they will no longer be tracked
1756 after the next commit.
1816 after the next commit.
1757
1817
1758 This only removes files from the current branch, not from the
1818 This only removes files from the current branch, not from the
1759 entire project history, and it does not delete them from the
1819 entire project history, and it does not delete them from the
1760 working directory.
1820 working directory.
1761
1821
1762 To undo a forget before the next commit, see :hg:`add`.
1822 To undo a forget before the next commit, see :hg:`add`.
1763
1823
1764 Returns 0 on success.
1824 Returns 0 on success.
1765 """
1825 """
1766
1826
1767 if not pats:
1827 if not pats:
1768 raise util.Abort(_('no files specified'))
1828 raise util.Abort(_('no files specified'))
1769
1829
1770 m = cmdutil.match(repo, pats, opts)
1830 m = cmdutil.match(repo, pats, opts)
1771 s = repo.status(match=m, clean=True)
1831 s = repo.status(match=m, clean=True)
1772 forget = sorted(s[0] + s[1] + s[3] + s[6])
1832 forget = sorted(s[0] + s[1] + s[3] + s[6])
1773 errs = 0
1833 errs = 0
1774
1834
1775 for f in m.files():
1835 for f in m.files():
1776 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1836 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1777 ui.warn(_('not removing %s: file is already untracked\n')
1837 ui.warn(_('not removing %s: file is already untracked\n')
1778 % m.rel(f))
1838 % m.rel(f))
1779 errs = 1
1839 errs = 1
1780
1840
1781 for f in forget:
1841 for f in forget:
1782 if ui.verbose or not m.exact(f):
1842 if ui.verbose or not m.exact(f):
1783 ui.status(_('removing %s\n') % m.rel(f))
1843 ui.status(_('removing %s\n') % m.rel(f))
1784
1844
1785 repo[None].remove(forget, unlink=False)
1845 repo[None].remove(forget, unlink=False)
1786 return errs
1846 return errs
1787
1847
1788 def grep(ui, repo, pattern, *pats, **opts):
1848 def grep(ui, repo, pattern, *pats, **opts):
1789 """search for a pattern in specified files and revisions
1849 """search for a pattern in specified files and revisions
1790
1850
1791 Search revisions of files for a regular expression.
1851 Search revisions of files for a regular expression.
1792
1852
1793 This command behaves differently than Unix grep. It only accepts
1853 This command behaves differently than Unix grep. It only accepts
1794 Python/Perl regexps. It searches repository history, not the
1854 Python/Perl regexps. It searches repository history, not the
1795 working directory. It always prints the revision number in which a
1855 working directory. It always prints the revision number in which a
1796 match appears.
1856 match appears.
1797
1857
1798 By default, grep only prints output for the first revision of a
1858 By default, grep only prints output for the first revision of a
1799 file in which it finds a match. To get it to print every revision
1859 file in which it finds a match. To get it to print every revision
1800 that contains a change in match status ("-" for a match that
1860 that contains a change in match status ("-" for a match that
1801 becomes a non-match, or "+" for a non-match that becomes a match),
1861 becomes a non-match, or "+" for a non-match that becomes a match),
1802 use the --all flag.
1862 use the --all flag.
1803
1863
1804 Returns 0 if a match is found, 1 otherwise.
1864 Returns 0 if a match is found, 1 otherwise.
1805 """
1865 """
1806 reflags = 0
1866 reflags = 0
1807 if opts.get('ignore_case'):
1867 if opts.get('ignore_case'):
1808 reflags |= re.I
1868 reflags |= re.I
1809 try:
1869 try:
1810 regexp = re.compile(pattern, reflags)
1870 regexp = re.compile(pattern, reflags)
1811 except re.error, inst:
1871 except re.error, inst:
1812 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1872 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1813 return 1
1873 return 1
1814 sep, eol = ':', '\n'
1874 sep, eol = ':', '\n'
1815 if opts.get('print0'):
1875 if opts.get('print0'):
1816 sep = eol = '\0'
1876 sep = eol = '\0'
1817
1877
1818 getfile = util.lrucachefunc(repo.file)
1878 getfile = util.lrucachefunc(repo.file)
1819
1879
1820 def matchlines(body):
1880 def matchlines(body):
1821 begin = 0
1881 begin = 0
1822 linenum = 0
1882 linenum = 0
1823 while True:
1883 while True:
1824 match = regexp.search(body, begin)
1884 match = regexp.search(body, begin)
1825 if not match:
1885 if not match:
1826 break
1886 break
1827 mstart, mend = match.span()
1887 mstart, mend = match.span()
1828 linenum += body.count('\n', begin, mstart) + 1
1888 linenum += body.count('\n', begin, mstart) + 1
1829 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1889 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1830 begin = body.find('\n', mend) + 1 or len(body)
1890 begin = body.find('\n', mend) + 1 or len(body)
1831 lend = begin - 1
1891 lend = begin - 1
1832 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1892 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1833
1893
1834 class linestate(object):
1894 class linestate(object):
1835 def __init__(self, line, linenum, colstart, colend):
1895 def __init__(self, line, linenum, colstart, colend):
1836 self.line = line
1896 self.line = line
1837 self.linenum = linenum
1897 self.linenum = linenum
1838 self.colstart = colstart
1898 self.colstart = colstart
1839 self.colend = colend
1899 self.colend = colend
1840
1900
1841 def __hash__(self):
1901 def __hash__(self):
1842 return hash((self.linenum, self.line))
1902 return hash((self.linenum, self.line))
1843
1903
1844 def __eq__(self, other):
1904 def __eq__(self, other):
1845 return self.line == other.line
1905 return self.line == other.line
1846
1906
1847 matches = {}
1907 matches = {}
1848 copies = {}
1908 copies = {}
1849 def grepbody(fn, rev, body):
1909 def grepbody(fn, rev, body):
1850 matches[rev].setdefault(fn, [])
1910 matches[rev].setdefault(fn, [])
1851 m = matches[rev][fn]
1911 m = matches[rev][fn]
1852 for lnum, cstart, cend, line in matchlines(body):
1912 for lnum, cstart, cend, line in matchlines(body):
1853 s = linestate(line, lnum, cstart, cend)
1913 s = linestate(line, lnum, cstart, cend)
1854 m.append(s)
1914 m.append(s)
1855
1915
1856 def difflinestates(a, b):
1916 def difflinestates(a, b):
1857 sm = difflib.SequenceMatcher(None, a, b)
1917 sm = difflib.SequenceMatcher(None, a, b)
1858 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1918 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1859 if tag == 'insert':
1919 if tag == 'insert':
1860 for i in xrange(blo, bhi):
1920 for i in xrange(blo, bhi):
1861 yield ('+', b[i])
1921 yield ('+', b[i])
1862 elif tag == 'delete':
1922 elif tag == 'delete':
1863 for i in xrange(alo, ahi):
1923 for i in xrange(alo, ahi):
1864 yield ('-', a[i])
1924 yield ('-', a[i])
1865 elif tag == 'replace':
1925 elif tag == 'replace':
1866 for i in xrange(alo, ahi):
1926 for i in xrange(alo, ahi):
1867 yield ('-', a[i])
1927 yield ('-', a[i])
1868 for i in xrange(blo, bhi):
1928 for i in xrange(blo, bhi):
1869 yield ('+', b[i])
1929 yield ('+', b[i])
1870
1930
1871 def display(fn, ctx, pstates, states):
1931 def display(fn, ctx, pstates, states):
1872 rev = ctx.rev()
1932 rev = ctx.rev()
1873 datefunc = ui.quiet and util.shortdate or util.datestr
1933 datefunc = ui.quiet and util.shortdate or util.datestr
1874 found = False
1934 found = False
1875 filerevmatches = {}
1935 filerevmatches = {}
1876 def binary():
1936 def binary():
1877 flog = getfile(fn)
1937 flog = getfile(fn)
1878 return util.binary(flog.read(ctx.filenode(fn)))
1938 return util.binary(flog.read(ctx.filenode(fn)))
1879
1939
1880 if opts.get('all'):
1940 if opts.get('all'):
1881 iter = difflinestates(pstates, states)
1941 iter = difflinestates(pstates, states)
1882 else:
1942 else:
1883 iter = [('', l) for l in states]
1943 iter = [('', l) for l in states]
1884 for change, l in iter:
1944 for change, l in iter:
1885 cols = [fn, str(rev)]
1945 cols = [fn, str(rev)]
1886 before, match, after = None, None, None
1946 before, match, after = None, None, None
1887 if opts.get('line_number'):
1947 if opts.get('line_number'):
1888 cols.append(str(l.linenum))
1948 cols.append(str(l.linenum))
1889 if opts.get('all'):
1949 if opts.get('all'):
1890 cols.append(change)
1950 cols.append(change)
1891 if opts.get('user'):
1951 if opts.get('user'):
1892 cols.append(ui.shortuser(ctx.user()))
1952 cols.append(ui.shortuser(ctx.user()))
1893 if opts.get('date'):
1953 if opts.get('date'):
1894 cols.append(datefunc(ctx.date()))
1954 cols.append(datefunc(ctx.date()))
1895 if opts.get('files_with_matches'):
1955 if opts.get('files_with_matches'):
1896 c = (fn, rev)
1956 c = (fn, rev)
1897 if c in filerevmatches:
1957 if c in filerevmatches:
1898 continue
1958 continue
1899 filerevmatches[c] = 1
1959 filerevmatches[c] = 1
1900 else:
1960 else:
1901 before = l.line[:l.colstart]
1961 before = l.line[:l.colstart]
1902 match = l.line[l.colstart:l.colend]
1962 match = l.line[l.colstart:l.colend]
1903 after = l.line[l.colend:]
1963 after = l.line[l.colend:]
1904 ui.write(sep.join(cols))
1964 ui.write(sep.join(cols))
1905 if before is not None:
1965 if before is not None:
1906 if not opts.get('text') and binary():
1966 if not opts.get('text') and binary():
1907 ui.write(sep + " Binary file matches")
1967 ui.write(sep + " Binary file matches")
1908 else:
1968 else:
1909 ui.write(sep + before)
1969 ui.write(sep + before)
1910 ui.write(match, label='grep.match')
1970 ui.write(match, label='grep.match')
1911 ui.write(after)
1971 ui.write(after)
1912 ui.write(eol)
1972 ui.write(eol)
1913 found = True
1973 found = True
1914 return found
1974 return found
1915
1975
1916 skip = {}
1976 skip = {}
1917 revfiles = {}
1977 revfiles = {}
1918 matchfn = cmdutil.match(repo, pats, opts)
1978 matchfn = cmdutil.match(repo, pats, opts)
1919 found = False
1979 found = False
1920 follow = opts.get('follow')
1980 follow = opts.get('follow')
1921
1981
1922 def prep(ctx, fns):
1982 def prep(ctx, fns):
1923 rev = ctx.rev()
1983 rev = ctx.rev()
1924 pctx = ctx.p1()
1984 pctx = ctx.p1()
1925 parent = pctx.rev()
1985 parent = pctx.rev()
1926 matches.setdefault(rev, {})
1986 matches.setdefault(rev, {})
1927 matches.setdefault(parent, {})
1987 matches.setdefault(parent, {})
1928 files = revfiles.setdefault(rev, [])
1988 files = revfiles.setdefault(rev, [])
1929 for fn in fns:
1989 for fn in fns:
1930 flog = getfile(fn)
1990 flog = getfile(fn)
1931 try:
1991 try:
1932 fnode = ctx.filenode(fn)
1992 fnode = ctx.filenode(fn)
1933 except error.LookupError:
1993 except error.LookupError:
1934 continue
1994 continue
1935
1995
1936 copied = flog.renamed(fnode)
1996 copied = flog.renamed(fnode)
1937 copy = follow and copied and copied[0]
1997 copy = follow and copied and copied[0]
1938 if copy:
1998 if copy:
1939 copies.setdefault(rev, {})[fn] = copy
1999 copies.setdefault(rev, {})[fn] = copy
1940 if fn in skip:
2000 if fn in skip:
1941 if copy:
2001 if copy:
1942 skip[copy] = True
2002 skip[copy] = True
1943 continue
2003 continue
1944 files.append(fn)
2004 files.append(fn)
1945
2005
1946 if fn not in matches[rev]:
2006 if fn not in matches[rev]:
1947 grepbody(fn, rev, flog.read(fnode))
2007 grepbody(fn, rev, flog.read(fnode))
1948
2008
1949 pfn = copy or fn
2009 pfn = copy or fn
1950 if pfn not in matches[parent]:
2010 if pfn not in matches[parent]:
1951 try:
2011 try:
1952 fnode = pctx.filenode(pfn)
2012 fnode = pctx.filenode(pfn)
1953 grepbody(pfn, parent, flog.read(fnode))
2013 grepbody(pfn, parent, flog.read(fnode))
1954 except error.LookupError:
2014 except error.LookupError:
1955 pass
2015 pass
1956
2016
1957 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2017 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1958 rev = ctx.rev()
2018 rev = ctx.rev()
1959 parent = ctx.p1().rev()
2019 parent = ctx.p1().rev()
1960 for fn in sorted(revfiles.get(rev, [])):
2020 for fn in sorted(revfiles.get(rev, [])):
1961 states = matches[rev][fn]
2021 states = matches[rev][fn]
1962 copy = copies.get(rev, {}).get(fn)
2022 copy = copies.get(rev, {}).get(fn)
1963 if fn in skip:
2023 if fn in skip:
1964 if copy:
2024 if copy:
1965 skip[copy] = True
2025 skip[copy] = True
1966 continue
2026 continue
1967 pstates = matches.get(parent, {}).get(copy or fn, [])
2027 pstates = matches.get(parent, {}).get(copy or fn, [])
1968 if pstates or states:
2028 if pstates or states:
1969 r = display(fn, ctx, pstates, states)
2029 r = display(fn, ctx, pstates, states)
1970 found = found or r
2030 found = found or r
1971 if r and not opts.get('all'):
2031 if r and not opts.get('all'):
1972 skip[fn] = True
2032 skip[fn] = True
1973 if copy:
2033 if copy:
1974 skip[copy] = True
2034 skip[copy] = True
1975 del matches[rev]
2035 del matches[rev]
1976 del revfiles[rev]
2036 del revfiles[rev]
1977
2037
1978 return not found
2038 return not found
1979
2039
1980 def heads(ui, repo, *branchrevs, **opts):
2040 def heads(ui, repo, *branchrevs, **opts):
1981 """show current repository heads or show branch heads
2041 """show current repository heads or show branch heads
1982
2042
1983 With no arguments, show all repository branch heads.
2043 With no arguments, show all repository branch heads.
1984
2044
1985 Repository "heads" are changesets with no child changesets. They are
2045 Repository "heads" are changesets with no child changesets. They are
1986 where development generally takes place and are the usual targets
2046 where development generally takes place and are the usual targets
1987 for update and merge operations. Branch heads are changesets that have
2047 for update and merge operations. Branch heads are changesets that have
1988 no child changeset on the same branch.
2048 no child changeset on the same branch.
1989
2049
1990 If one or more REVs are given, only branch heads on the branches
2050 If one or more REVs are given, only branch heads on the branches
1991 associated with the specified changesets are shown.
2051 associated with the specified changesets are shown.
1992
2052
1993 If -c/--closed is specified, also show branch heads marked closed
2053 If -c/--closed is specified, also show branch heads marked closed
1994 (see :hg:`commit --close-branch`).
2054 (see :hg:`commit --close-branch`).
1995
2055
1996 If STARTREV is specified, only those heads that are descendants of
2056 If STARTREV is specified, only those heads that are descendants of
1997 STARTREV will be displayed.
2057 STARTREV will be displayed.
1998
2058
1999 If -t/--topo is specified, named branch mechanics will be ignored and only
2059 If -t/--topo is specified, named branch mechanics will be ignored and only
2000 changesets without children will be shown.
2060 changesets without children will be shown.
2001
2061
2002 Returns 0 if matching heads are found, 1 if not.
2062 Returns 0 if matching heads are found, 1 if not.
2003 """
2063 """
2004
2064
2005 start = None
2065 start = None
2006 if 'rev' in opts:
2066 if 'rev' in opts:
2007 start = cmdutil.revsingle(repo, opts['rev'], None).node()
2067 start = cmdutil.revsingle(repo, opts['rev'], None).node()
2008
2068
2009 if opts.get('topo'):
2069 if opts.get('topo'):
2010 heads = [repo[h] for h in repo.heads(start)]
2070 heads = [repo[h] for h in repo.heads(start)]
2011 else:
2071 else:
2012 heads = []
2072 heads = []
2013 for b, ls in repo.branchmap().iteritems():
2073 for b, ls in repo.branchmap().iteritems():
2014 if start is None:
2074 if start is None:
2015 heads += [repo[h] for h in ls]
2075 heads += [repo[h] for h in ls]
2016 continue
2076 continue
2017 startrev = repo.changelog.rev(start)
2077 startrev = repo.changelog.rev(start)
2018 descendants = set(repo.changelog.descendants(startrev))
2078 descendants = set(repo.changelog.descendants(startrev))
2019 descendants.add(startrev)
2079 descendants.add(startrev)
2020 rev = repo.changelog.rev
2080 rev = repo.changelog.rev
2021 heads += [repo[h] for h in ls if rev(h) in descendants]
2081 heads += [repo[h] for h in ls if rev(h) in descendants]
2022
2082
2023 if branchrevs:
2083 if branchrevs:
2024 branches = set(repo[br].branch() for br in branchrevs)
2084 branches = set(repo[br].branch() for br in branchrevs)
2025 heads = [h for h in heads if h.branch() in branches]
2085 heads = [h for h in heads if h.branch() in branches]
2026
2086
2027 if not opts.get('closed'):
2087 if not opts.get('closed'):
2028 heads = [h for h in heads if not h.extra().get('close')]
2088 heads = [h for h in heads if not h.extra().get('close')]
2029
2089
2030 if opts.get('active') and branchrevs:
2090 if opts.get('active') and branchrevs:
2031 dagheads = repo.heads(start)
2091 dagheads = repo.heads(start)
2032 heads = [h for h in heads if h.node() in dagheads]
2092 heads = [h for h in heads if h.node() in dagheads]
2033
2093
2034 if branchrevs:
2094 if branchrevs:
2035 haveheads = set(h.branch() for h in heads)
2095 haveheads = set(h.branch() for h in heads)
2036 if branches - haveheads:
2096 if branches - haveheads:
2037 headless = ', '.join(b for b in branches - haveheads)
2097 headless = ', '.join(b for b in branches - haveheads)
2038 msg = _('no open branch heads found on branches %s')
2098 msg = _('no open branch heads found on branches %s')
2039 if opts.get('rev'):
2099 if opts.get('rev'):
2040 msg += _(' (started at %s)' % opts['rev'])
2100 msg += _(' (started at %s)' % opts['rev'])
2041 ui.warn((msg + '\n') % headless)
2101 ui.warn((msg + '\n') % headless)
2042
2102
2043 if not heads:
2103 if not heads:
2044 return 1
2104 return 1
2045
2105
2046 heads = sorted(heads, key=lambda x: -x.rev())
2106 heads = sorted(heads, key=lambda x: -x.rev())
2047 displayer = cmdutil.show_changeset(ui, repo, opts)
2107 displayer = cmdutil.show_changeset(ui, repo, opts)
2048 for ctx in heads:
2108 for ctx in heads:
2049 displayer.show(ctx)
2109 displayer.show(ctx)
2050 displayer.close()
2110 displayer.close()
2051
2111
2052 def help_(ui, name=None, with_version=False, unknowncmd=False, full=True):
2112 def help_(ui, name=None, with_version=False, unknowncmd=False, full=True):
2053 """show help for a given topic or a help overview
2113 """show help for a given topic or a help overview
2054
2114
2055 With no arguments, print a list of commands with short help messages.
2115 With no arguments, print a list of commands with short help messages.
2056
2116
2057 Given a topic, extension, or command name, print help for that
2117 Given a topic, extension, or command name, print help for that
2058 topic.
2118 topic.
2059
2119
2060 Returns 0 if successful.
2120 Returns 0 if successful.
2061 """
2121 """
2062 option_lists = []
2122 option_lists = []
2063 textwidth = min(ui.termwidth(), 80) - 2
2123 textwidth = min(ui.termwidth(), 80) - 2
2064
2124
2065 def addglobalopts(aliases):
2125 def addglobalopts(aliases):
2066 if ui.verbose:
2126 if ui.verbose:
2067 option_lists.append((_("global options:"), globalopts))
2127 option_lists.append((_("global options:"), globalopts))
2068 if name == 'shortlist':
2128 if name == 'shortlist':
2069 option_lists.append((_('use "hg help" for the full list '
2129 option_lists.append((_('use "hg help" for the full list '
2070 'of commands'), ()))
2130 'of commands'), ()))
2071 else:
2131 else:
2072 if name == 'shortlist':
2132 if name == 'shortlist':
2073 msg = _('use "hg help" for the full list of commands '
2133 msg = _('use "hg help" for the full list of commands '
2074 'or "hg -v" for details')
2134 'or "hg -v" for details')
2075 elif name and not full:
2135 elif name and not full:
2076 msg = _('use "hg help %s" to show the full help text' % name)
2136 msg = _('use "hg help %s" to show the full help text' % name)
2077 elif aliases:
2137 elif aliases:
2078 msg = _('use "hg -v help%s" to show builtin aliases and '
2138 msg = _('use "hg -v help%s" to show builtin aliases and '
2079 'global options') % (name and " " + name or "")
2139 'global options') % (name and " " + name or "")
2080 else:
2140 else:
2081 msg = _('use "hg -v help %s" to show global options') % name
2141 msg = _('use "hg -v help %s" to show global options') % name
2082 option_lists.append((msg, ()))
2142 option_lists.append((msg, ()))
2083
2143
2084 def helpcmd(name):
2144 def helpcmd(name):
2085 if with_version:
2145 if with_version:
2086 version_(ui)
2146 version_(ui)
2087 ui.write('\n')
2147 ui.write('\n')
2088
2148
2089 try:
2149 try:
2090 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
2150 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
2091 except error.AmbiguousCommand, inst:
2151 except error.AmbiguousCommand, inst:
2092 # py3k fix: except vars can't be used outside the scope of the
2152 # py3k fix: except vars can't be used outside the scope of the
2093 # except block, nor can be used inside a lambda. python issue4617
2153 # except block, nor can be used inside a lambda. python issue4617
2094 prefix = inst.args[0]
2154 prefix = inst.args[0]
2095 select = lambda c: c.lstrip('^').startswith(prefix)
2155 select = lambda c: c.lstrip('^').startswith(prefix)
2096 helplist(_('list of commands:\n\n'), select)
2156 helplist(_('list of commands:\n\n'), select)
2097 return
2157 return
2098
2158
2099 # check if it's an invalid alias and display its error if it is
2159 # check if it's an invalid alias and display its error if it is
2100 if getattr(entry[0], 'badalias', False):
2160 if getattr(entry[0], 'badalias', False):
2101 if not unknowncmd:
2161 if not unknowncmd:
2102 entry[0](ui)
2162 entry[0](ui)
2103 return
2163 return
2104
2164
2105 # synopsis
2165 # synopsis
2106 if len(entry) > 2:
2166 if len(entry) > 2:
2107 if entry[2].startswith('hg'):
2167 if entry[2].startswith('hg'):
2108 ui.write("%s\n" % entry[2])
2168 ui.write("%s\n" % entry[2])
2109 else:
2169 else:
2110 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
2170 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
2111 else:
2171 else:
2112 ui.write('hg %s\n' % aliases[0])
2172 ui.write('hg %s\n' % aliases[0])
2113
2173
2114 # aliases
2174 # aliases
2115 if full and not ui.quiet and len(aliases) > 1:
2175 if full and not ui.quiet and len(aliases) > 1:
2116 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
2176 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
2117
2177
2118 # description
2178 # description
2119 doc = gettext(entry[0].__doc__)
2179 doc = gettext(entry[0].__doc__)
2120 if not doc:
2180 if not doc:
2121 doc = _("(no help text available)")
2181 doc = _("(no help text available)")
2122 if hasattr(entry[0], 'definition'): # aliased command
2182 if hasattr(entry[0], 'definition'): # aliased command
2123 if entry[0].definition.startswith('!'): # shell alias
2183 if entry[0].definition.startswith('!'): # shell alias
2124 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
2184 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
2125 else:
2185 else:
2126 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
2186 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
2127 if ui.quiet or not full:
2187 if ui.quiet or not full:
2128 doc = doc.splitlines()[0]
2188 doc = doc.splitlines()[0]
2129 keep = ui.verbose and ['verbose'] or []
2189 keep = ui.verbose and ['verbose'] or []
2130 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
2190 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
2131 ui.write("\n%s\n" % formatted)
2191 ui.write("\n%s\n" % formatted)
2132 if pruned:
2192 if pruned:
2133 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
2193 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
2134
2194
2135 if not ui.quiet:
2195 if not ui.quiet:
2136 # options
2196 # options
2137 if entry[1]:
2197 if entry[1]:
2138 option_lists.append((_("options:\n"), entry[1]))
2198 option_lists.append((_("options:\n"), entry[1]))
2139
2199
2140 addglobalopts(False)
2200 addglobalopts(False)
2141
2201
2142 def helplist(header, select=None):
2202 def helplist(header, select=None):
2143 h = {}
2203 h = {}
2144 cmds = {}
2204 cmds = {}
2145 for c, e in table.iteritems():
2205 for c, e in table.iteritems():
2146 f = c.split("|", 1)[0]
2206 f = c.split("|", 1)[0]
2147 if select and not select(f):
2207 if select and not select(f):
2148 continue
2208 continue
2149 if (not select and name != 'shortlist' and
2209 if (not select and name != 'shortlist' and
2150 e[0].__module__ != __name__):
2210 e[0].__module__ != __name__):
2151 continue
2211 continue
2152 if name == "shortlist" and not f.startswith("^"):
2212 if name == "shortlist" and not f.startswith("^"):
2153 continue
2213 continue
2154 f = f.lstrip("^")
2214 f = f.lstrip("^")
2155 if not ui.debugflag and f.startswith("debug"):
2215 if not ui.debugflag and f.startswith("debug"):
2156 continue
2216 continue
2157 doc = e[0].__doc__
2217 doc = e[0].__doc__
2158 if doc and 'DEPRECATED' in doc and not ui.verbose:
2218 if doc and 'DEPRECATED' in doc and not ui.verbose:
2159 continue
2219 continue
2160 doc = gettext(doc)
2220 doc = gettext(doc)
2161 if not doc:
2221 if not doc:
2162 doc = _("(no help text available)")
2222 doc = _("(no help text available)")
2163 h[f] = doc.splitlines()[0].rstrip()
2223 h[f] = doc.splitlines()[0].rstrip()
2164 cmds[f] = c.lstrip("^")
2224 cmds[f] = c.lstrip("^")
2165
2225
2166 if not h:
2226 if not h:
2167 ui.status(_('no commands defined\n'))
2227 ui.status(_('no commands defined\n'))
2168 return
2228 return
2169
2229
2170 ui.status(header)
2230 ui.status(header)
2171 fns = sorted(h)
2231 fns = sorted(h)
2172 m = max(map(len, fns))
2232 m = max(map(len, fns))
2173 for f in fns:
2233 for f in fns:
2174 if ui.verbose:
2234 if ui.verbose:
2175 commands = cmds[f].replace("|",", ")
2235 commands = cmds[f].replace("|",", ")
2176 ui.write(" %s:\n %s\n"%(commands, h[f]))
2236 ui.write(" %s:\n %s\n"%(commands, h[f]))
2177 else:
2237 else:
2178 ui.write('%s\n' % (util.wrap(h[f], textwidth,
2238 ui.write('%s\n' % (util.wrap(h[f], textwidth,
2179 initindent=' %-*s ' % (m, f),
2239 initindent=' %-*s ' % (m, f),
2180 hangindent=' ' * (m + 4))))
2240 hangindent=' ' * (m + 4))))
2181
2241
2182 if not ui.quiet:
2242 if not ui.quiet:
2183 addglobalopts(True)
2243 addglobalopts(True)
2184
2244
2185 def helptopic(name):
2245 def helptopic(name):
2186 for names, header, doc in help.helptable:
2246 for names, header, doc in help.helptable:
2187 if name in names:
2247 if name in names:
2188 break
2248 break
2189 else:
2249 else:
2190 raise error.UnknownCommand(name)
2250 raise error.UnknownCommand(name)
2191
2251
2192 # description
2252 # description
2193 if not doc:
2253 if not doc:
2194 doc = _("(no help text available)")
2254 doc = _("(no help text available)")
2195 if hasattr(doc, '__call__'):
2255 if hasattr(doc, '__call__'):
2196 doc = doc()
2256 doc = doc()
2197
2257
2198 ui.write("%s\n\n" % header)
2258 ui.write("%s\n\n" % header)
2199 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2259 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2200
2260
2201 def helpext(name):
2261 def helpext(name):
2202 try:
2262 try:
2203 mod = extensions.find(name)
2263 mod = extensions.find(name)
2204 doc = gettext(mod.__doc__) or _('no help text available')
2264 doc = gettext(mod.__doc__) or _('no help text available')
2205 except KeyError:
2265 except KeyError:
2206 mod = None
2266 mod = None
2207 doc = extensions.disabledext(name)
2267 doc = extensions.disabledext(name)
2208 if not doc:
2268 if not doc:
2209 raise error.UnknownCommand(name)
2269 raise error.UnknownCommand(name)
2210
2270
2211 if '\n' not in doc:
2271 if '\n' not in doc:
2212 head, tail = doc, ""
2272 head, tail = doc, ""
2213 else:
2273 else:
2214 head, tail = doc.split('\n', 1)
2274 head, tail = doc.split('\n', 1)
2215 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2275 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2216 if tail:
2276 if tail:
2217 ui.write(minirst.format(tail, textwidth))
2277 ui.write(minirst.format(tail, textwidth))
2218 ui.status('\n\n')
2278 ui.status('\n\n')
2219
2279
2220 if mod:
2280 if mod:
2221 try:
2281 try:
2222 ct = mod.cmdtable
2282 ct = mod.cmdtable
2223 except AttributeError:
2283 except AttributeError:
2224 ct = {}
2284 ct = {}
2225 modcmds = set([c.split('|', 1)[0] for c in ct])
2285 modcmds = set([c.split('|', 1)[0] for c in ct])
2226 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2286 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2227 else:
2287 else:
2228 ui.write(_('use "hg help extensions" for information on enabling '
2288 ui.write(_('use "hg help extensions" for information on enabling '
2229 'extensions\n'))
2289 'extensions\n'))
2230
2290
2231 def helpextcmd(name):
2291 def helpextcmd(name):
2232 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2292 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2233 doc = gettext(mod.__doc__).splitlines()[0]
2293 doc = gettext(mod.__doc__).splitlines()[0]
2234
2294
2235 msg = help.listexts(_("'%s' is provided by the following "
2295 msg = help.listexts(_("'%s' is provided by the following "
2236 "extension:") % cmd, {ext: doc}, len(ext),
2296 "extension:") % cmd, {ext: doc}, len(ext),
2237 indent=4)
2297 indent=4)
2238 ui.write(minirst.format(msg, textwidth))
2298 ui.write(minirst.format(msg, textwidth))
2239 ui.write('\n\n')
2299 ui.write('\n\n')
2240 ui.write(_('use "hg help extensions" for information on enabling '
2300 ui.write(_('use "hg help extensions" for information on enabling '
2241 'extensions\n'))
2301 'extensions\n'))
2242
2302
2243 help.addtopichook('revsets', revset.makedoc)
2303 help.addtopichook('revsets', revset.makedoc)
2244 help.addtopichook('templates', templatekw.makedoc)
2304 help.addtopichook('templates', templatekw.makedoc)
2245 help.addtopichook('templates', templatefilters.makedoc)
2305 help.addtopichook('templates', templatefilters.makedoc)
2246
2306
2247 if name and name != 'shortlist':
2307 if name and name != 'shortlist':
2248 i = None
2308 i = None
2249 if unknowncmd:
2309 if unknowncmd:
2250 queries = (helpextcmd,)
2310 queries = (helpextcmd,)
2251 else:
2311 else:
2252 queries = (helptopic, helpcmd, helpext, helpextcmd)
2312 queries = (helptopic, helpcmd, helpext, helpextcmd)
2253 for f in queries:
2313 for f in queries:
2254 try:
2314 try:
2255 f(name)
2315 f(name)
2256 i = None
2316 i = None
2257 break
2317 break
2258 except error.UnknownCommand, inst:
2318 except error.UnknownCommand, inst:
2259 i = inst
2319 i = inst
2260 if i:
2320 if i:
2261 raise i
2321 raise i
2262
2322
2263 else:
2323 else:
2264 # program name
2324 # program name
2265 if ui.verbose or with_version:
2325 if ui.verbose or with_version:
2266 version_(ui)
2326 version_(ui)
2267 else:
2327 else:
2268 ui.status(_("Mercurial Distributed SCM\n"))
2328 ui.status(_("Mercurial Distributed SCM\n"))
2269 ui.status('\n')
2329 ui.status('\n')
2270
2330
2271 # list of commands
2331 # list of commands
2272 if name == "shortlist":
2332 if name == "shortlist":
2273 header = _('basic commands:\n\n')
2333 header = _('basic commands:\n\n')
2274 else:
2334 else:
2275 header = _('list of commands:\n\n')
2335 header = _('list of commands:\n\n')
2276
2336
2277 helplist(header)
2337 helplist(header)
2278 if name != 'shortlist':
2338 if name != 'shortlist':
2279 exts, maxlength = extensions.enabled()
2339 exts, maxlength = extensions.enabled()
2280 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2340 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2281 if text:
2341 if text:
2282 ui.write("\n%s\n" % minirst.format(text, textwidth))
2342 ui.write("\n%s\n" % minirst.format(text, textwidth))
2283
2343
2284 # list all option lists
2344 # list all option lists
2285 opt_output = []
2345 opt_output = []
2286 multioccur = False
2346 multioccur = False
2287 for title, options in option_lists:
2347 for title, options in option_lists:
2288 opt_output.append(("\n%s" % title, None))
2348 opt_output.append(("\n%s" % title, None))
2289 for option in options:
2349 for option in options:
2290 if len(option) == 5:
2350 if len(option) == 5:
2291 shortopt, longopt, default, desc, optlabel = option
2351 shortopt, longopt, default, desc, optlabel = option
2292 else:
2352 else:
2293 shortopt, longopt, default, desc = option
2353 shortopt, longopt, default, desc = option
2294 optlabel = _("VALUE") # default label
2354 optlabel = _("VALUE") # default label
2295
2355
2296 if _("DEPRECATED") in desc and not ui.verbose:
2356 if _("DEPRECATED") in desc and not ui.verbose:
2297 continue
2357 continue
2298 if isinstance(default, list):
2358 if isinstance(default, list):
2299 numqualifier = " %s [+]" % optlabel
2359 numqualifier = " %s [+]" % optlabel
2300 multioccur = True
2360 multioccur = True
2301 elif (default is not None) and not isinstance(default, bool):
2361 elif (default is not None) and not isinstance(default, bool):
2302 numqualifier = " %s" % optlabel
2362 numqualifier = " %s" % optlabel
2303 else:
2363 else:
2304 numqualifier = ""
2364 numqualifier = ""
2305 opt_output.append(("%2s%s" %
2365 opt_output.append(("%2s%s" %
2306 (shortopt and "-%s" % shortopt,
2366 (shortopt and "-%s" % shortopt,
2307 longopt and " --%s%s" %
2367 longopt and " --%s%s" %
2308 (longopt, numqualifier)),
2368 (longopt, numqualifier)),
2309 "%s%s" % (desc,
2369 "%s%s" % (desc,
2310 default
2370 default
2311 and _(" (default: %s)") % default
2371 and _(" (default: %s)") % default
2312 or "")))
2372 or "")))
2313 if multioccur:
2373 if multioccur:
2314 msg = _("\n[+] marked option can be specified multiple times")
2374 msg = _("\n[+] marked option can be specified multiple times")
2315 if ui.verbose and name != 'shortlist':
2375 if ui.verbose and name != 'shortlist':
2316 opt_output.append((msg, None))
2376 opt_output.append((msg, None))
2317 else:
2377 else:
2318 opt_output.insert(-1, (msg, None))
2378 opt_output.insert(-1, (msg, None))
2319
2379
2320 if not name:
2380 if not name:
2321 ui.write(_("\nadditional help topics:\n\n"))
2381 ui.write(_("\nadditional help topics:\n\n"))
2322 topics = []
2382 topics = []
2323 for names, header, doc in help.helptable:
2383 for names, header, doc in help.helptable:
2324 topics.append((sorted(names, key=len, reverse=True)[0], header))
2384 topics.append((sorted(names, key=len, reverse=True)[0], header))
2325 topics_len = max([len(s[0]) for s in topics])
2385 topics_len = max([len(s[0]) for s in topics])
2326 for t, desc in topics:
2386 for t, desc in topics:
2327 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2387 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2328
2388
2329 if opt_output:
2389 if opt_output:
2330 colwidth = encoding.colwidth
2390 colwidth = encoding.colwidth
2331 # normalize: (opt or message, desc or None, width of opt)
2391 # normalize: (opt or message, desc or None, width of opt)
2332 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2392 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2333 for opt, desc in opt_output]
2393 for opt, desc in opt_output]
2334 hanging = max([e[2] for e in entries])
2394 hanging = max([e[2] for e in entries])
2335 for opt, desc, width in entries:
2395 for opt, desc, width in entries:
2336 if desc:
2396 if desc:
2337 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2397 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2338 hangindent = ' ' * (hanging + 3)
2398 hangindent = ' ' * (hanging + 3)
2339 ui.write('%s\n' % (util.wrap(desc, textwidth,
2399 ui.write('%s\n' % (util.wrap(desc, textwidth,
2340 initindent=initindent,
2400 initindent=initindent,
2341 hangindent=hangindent)))
2401 hangindent=hangindent)))
2342 else:
2402 else:
2343 ui.write("%s\n" % opt)
2403 ui.write("%s\n" % opt)
2344
2404
2345 def identify(ui, repo, source=None, rev=None,
2405 def identify(ui, repo, source=None, rev=None,
2346 num=None, id=None, branch=None, tags=None, bookmarks=None):
2406 num=None, id=None, branch=None, tags=None, bookmarks=None):
2347 """identify the working copy or specified revision
2407 """identify the working copy or specified revision
2348
2408
2349 Print a summary identifying the repository state at REV using one or
2409 Print a summary identifying the repository state at REV using one or
2350 two parent hash identifiers, followed by a "+" if the working
2410 two parent hash identifiers, followed by a "+" if the working
2351 directory has uncommitted changes, the branch name (if not default),
2411 directory has uncommitted changes, the branch name (if not default),
2352 a list of tags, and a list of bookmarks.
2412 a list of tags, and a list of bookmarks.
2353
2413
2354 When REV is not given, print a summary of the current state of the
2414 When REV is not given, print a summary of the current state of the
2355 repository.
2415 repository.
2356
2416
2357 Specifying a path to a repository root or Mercurial bundle will
2417 Specifying a path to a repository root or Mercurial bundle will
2358 cause lookup to operate on that repository/bundle.
2418 cause lookup to operate on that repository/bundle.
2359
2419
2360 Returns 0 if successful.
2420 Returns 0 if successful.
2361 """
2421 """
2362
2422
2363 if not repo and not source:
2423 if not repo and not source:
2364 raise util.Abort(_("there is no Mercurial repository here "
2424 raise util.Abort(_("there is no Mercurial repository here "
2365 "(.hg not found)"))
2425 "(.hg not found)"))
2366
2426
2367 hexfunc = ui.debugflag and hex or short
2427 hexfunc = ui.debugflag and hex or short
2368 default = not (num or id or branch or tags or bookmarks)
2428 default = not (num or id or branch or tags or bookmarks)
2369 output = []
2429 output = []
2370 revs = []
2430 revs = []
2371
2431
2372 if source:
2432 if source:
2373 source, branches = hg.parseurl(ui.expandpath(source))
2433 source, branches = hg.parseurl(ui.expandpath(source))
2374 repo = hg.repository(ui, source)
2434 repo = hg.repository(ui, source)
2375 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2435 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2376
2436
2377 if not repo.local():
2437 if not repo.local():
2378 if num or branch or tags:
2438 if num or branch or tags:
2379 raise util.Abort(
2439 raise util.Abort(
2380 _("can't query remote revision number, branch, or tags"))
2440 _("can't query remote revision number, branch, or tags"))
2381 if not rev and revs:
2441 if not rev and revs:
2382 rev = revs[0]
2442 rev = revs[0]
2383 if not rev:
2443 if not rev:
2384 rev = "tip"
2444 rev = "tip"
2385
2445
2386 remoterev = repo.lookup(rev)
2446 remoterev = repo.lookup(rev)
2387 if default or id:
2447 if default or id:
2388 output = [hexfunc(remoterev)]
2448 output = [hexfunc(remoterev)]
2389
2449
2390 def getbms():
2450 def getbms():
2391 bms = []
2451 bms = []
2392
2452
2393 if 'bookmarks' in repo.listkeys('namespaces'):
2453 if 'bookmarks' in repo.listkeys('namespaces'):
2394 hexremoterev = hex(remoterev)
2454 hexremoterev = hex(remoterev)
2395 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
2455 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
2396 if bmr == hexremoterev]
2456 if bmr == hexremoterev]
2397
2457
2398 return bms
2458 return bms
2399
2459
2400 if bookmarks:
2460 if bookmarks:
2401 output.extend(getbms())
2461 output.extend(getbms())
2402 elif default and not ui.quiet:
2462 elif default and not ui.quiet:
2403 # multiple bookmarks for a single parent separated by '/'
2463 # multiple bookmarks for a single parent separated by '/'
2404 bm = '/'.join(getbms())
2464 bm = '/'.join(getbms())
2405 if bm:
2465 if bm:
2406 output.append(bm)
2466 output.append(bm)
2407 else:
2467 else:
2408 if not rev:
2468 if not rev:
2409 ctx = repo[None]
2469 ctx = repo[None]
2410 parents = ctx.parents()
2470 parents = ctx.parents()
2411 changed = ""
2471 changed = ""
2412 if default or id or num:
2472 if default or id or num:
2413 changed = util.any(repo.status()) and "+" or ""
2473 changed = util.any(repo.status()) and "+" or ""
2414 if default or id:
2474 if default or id:
2415 output = ["%s%s" %
2475 output = ["%s%s" %
2416 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2476 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2417 if num:
2477 if num:
2418 output.append("%s%s" %
2478 output.append("%s%s" %
2419 ('+'.join([str(p.rev()) for p in parents]), changed))
2479 ('+'.join([str(p.rev()) for p in parents]), changed))
2420 else:
2480 else:
2421 ctx = cmdutil.revsingle(repo, rev)
2481 ctx = cmdutil.revsingle(repo, rev)
2422 if default or id:
2482 if default or id:
2423 output = [hexfunc(ctx.node())]
2483 output = [hexfunc(ctx.node())]
2424 if num:
2484 if num:
2425 output.append(str(ctx.rev()))
2485 output.append(str(ctx.rev()))
2426
2486
2427 if default and not ui.quiet:
2487 if default and not ui.quiet:
2428 b = ctx.branch()
2488 b = ctx.branch()
2429 if b != 'default':
2489 if b != 'default':
2430 output.append("(%s)" % b)
2490 output.append("(%s)" % b)
2431
2491
2432 # multiple tags for a single parent separated by '/'
2492 # multiple tags for a single parent separated by '/'
2433 t = '/'.join(ctx.tags())
2493 t = '/'.join(ctx.tags())
2434 if t:
2494 if t:
2435 output.append(t)
2495 output.append(t)
2436
2496
2437 # multiple bookmarks for a single parent separated by '/'
2497 # multiple bookmarks for a single parent separated by '/'
2438 bm = '/'.join(ctx.bookmarks())
2498 bm = '/'.join(ctx.bookmarks())
2439 if bm:
2499 if bm:
2440 output.append(bm)
2500 output.append(bm)
2441 else:
2501 else:
2442 if branch:
2502 if branch:
2443 output.append(ctx.branch())
2503 output.append(ctx.branch())
2444
2504
2445 if tags:
2505 if tags:
2446 output.extend(ctx.tags())
2506 output.extend(ctx.tags())
2447
2507
2448 if bookmarks:
2508 if bookmarks:
2449 output.extend(ctx.bookmarks())
2509 output.extend(ctx.bookmarks())
2450
2510
2451 ui.write("%s\n" % ' '.join(output))
2511 ui.write("%s\n" % ' '.join(output))
2452
2512
2453 def import_(ui, repo, patch1, *patches, **opts):
2513 def import_(ui, repo, patch1, *patches, **opts):
2454 """import an ordered set of patches
2514 """import an ordered set of patches
2455
2515
2456 Import a list of patches and commit them individually (unless
2516 Import a list of patches and commit them individually (unless
2457 --no-commit is specified).
2517 --no-commit is specified).
2458
2518
2459 If there are outstanding changes in the working directory, import
2519 If there are outstanding changes in the working directory, import
2460 will abort unless given the -f/--force flag.
2520 will abort unless given the -f/--force flag.
2461
2521
2462 You can import a patch straight from a mail message. Even patches
2522 You can import a patch straight from a mail message. Even patches
2463 as attachments work (to use the body part, it must have type
2523 as attachments work (to use the body part, it must have type
2464 text/plain or text/x-patch). From and Subject headers of email
2524 text/plain or text/x-patch). From and Subject headers of email
2465 message are used as default committer and commit message. All
2525 message are used as default committer and commit message. All
2466 text/plain body parts before first diff are added to commit
2526 text/plain body parts before first diff are added to commit
2467 message.
2527 message.
2468
2528
2469 If the imported patch was generated by :hg:`export`, user and
2529 If the imported patch was generated by :hg:`export`, user and
2470 description from patch override values from message headers and
2530 description from patch override values from message headers and
2471 body. Values given on command line with -m/--message and -u/--user
2531 body. Values given on command line with -m/--message and -u/--user
2472 override these.
2532 override these.
2473
2533
2474 If --exact is specified, import will set the working directory to
2534 If --exact is specified, import will set the working directory to
2475 the parent of each patch before applying it, and will abort if the
2535 the parent of each patch before applying it, and will abort if the
2476 resulting changeset has a different ID than the one recorded in
2536 resulting changeset has a different ID than the one recorded in
2477 the patch. This may happen due to character set problems or other
2537 the patch. This may happen due to character set problems or other
2478 deficiencies in the text patch format.
2538 deficiencies in the text patch format.
2479
2539
2480 With -s/--similarity, hg will attempt to discover renames and
2540 With -s/--similarity, hg will attempt to discover renames and
2481 copies in the patch in the same way as 'addremove'.
2541 copies in the patch in the same way as 'addremove'.
2482
2542
2483 To read a patch from standard input, use "-" as the patch name. If
2543 To read a patch from standard input, use "-" as the patch name. If
2484 a URL is specified, the patch will be downloaded from it.
2544 a URL is specified, the patch will be downloaded from it.
2485 See :hg:`help dates` for a list of formats valid for -d/--date.
2545 See :hg:`help dates` for a list of formats valid for -d/--date.
2486
2546
2487 Returns 0 on success.
2547 Returns 0 on success.
2488 """
2548 """
2489 patches = (patch1,) + patches
2549 patches = (patch1,) + patches
2490
2550
2491 date = opts.get('date')
2551 date = opts.get('date')
2492 if date:
2552 if date:
2493 opts['date'] = util.parsedate(date)
2553 opts['date'] = util.parsedate(date)
2494
2554
2495 try:
2555 try:
2496 sim = float(opts.get('similarity') or 0)
2556 sim = float(opts.get('similarity') or 0)
2497 except ValueError:
2557 except ValueError:
2498 raise util.Abort(_('similarity must be a number'))
2558 raise util.Abort(_('similarity must be a number'))
2499 if sim < 0 or sim > 100:
2559 if sim < 0 or sim > 100:
2500 raise util.Abort(_('similarity must be between 0 and 100'))
2560 raise util.Abort(_('similarity must be between 0 and 100'))
2501
2561
2502 if opts.get('exact') or not opts.get('force'):
2562 if opts.get('exact') or not opts.get('force'):
2503 cmdutil.bail_if_changed(repo)
2563 cmdutil.bail_if_changed(repo)
2504
2564
2505 d = opts["base"]
2565 d = opts["base"]
2506 strip = opts["strip"]
2566 strip = opts["strip"]
2507 wlock = lock = None
2567 wlock = lock = None
2508 msgs = []
2568 msgs = []
2509
2569
2510 def tryone(ui, hunk):
2570 def tryone(ui, hunk):
2511 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2571 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2512 patch.extract(ui, hunk)
2572 patch.extract(ui, hunk)
2513
2573
2514 if not tmpname:
2574 if not tmpname:
2515 return None
2575 return None
2516 commitid = _('to working directory')
2576 commitid = _('to working directory')
2517
2577
2518 try:
2578 try:
2519 cmdline_message = cmdutil.logmessage(opts)
2579 cmdline_message = cmdutil.logmessage(opts)
2520 if cmdline_message:
2580 if cmdline_message:
2521 # pickup the cmdline msg
2581 # pickup the cmdline msg
2522 message = cmdline_message
2582 message = cmdline_message
2523 elif message:
2583 elif message:
2524 # pickup the patch msg
2584 # pickup the patch msg
2525 message = message.strip()
2585 message = message.strip()
2526 else:
2586 else:
2527 # launch the editor
2587 # launch the editor
2528 message = None
2588 message = None
2529 ui.debug('message:\n%s\n' % message)
2589 ui.debug('message:\n%s\n' % message)
2530
2590
2531 wp = repo.parents()
2591 wp = repo.parents()
2532 if opts.get('exact'):
2592 if opts.get('exact'):
2533 if not nodeid or not p1:
2593 if not nodeid or not p1:
2534 raise util.Abort(_('not a Mercurial patch'))
2594 raise util.Abort(_('not a Mercurial patch'))
2535 p1 = repo.lookup(p1)
2595 p1 = repo.lookup(p1)
2536 p2 = repo.lookup(p2 or hex(nullid))
2596 p2 = repo.lookup(p2 or hex(nullid))
2537
2597
2538 if p1 != wp[0].node():
2598 if p1 != wp[0].node():
2539 hg.clean(repo, p1)
2599 hg.clean(repo, p1)
2540 repo.dirstate.setparents(p1, p2)
2600 repo.dirstate.setparents(p1, p2)
2541 elif p2:
2601 elif p2:
2542 try:
2602 try:
2543 p1 = repo.lookup(p1)
2603 p1 = repo.lookup(p1)
2544 p2 = repo.lookup(p2)
2604 p2 = repo.lookup(p2)
2545 if p1 == wp[0].node():
2605 if p1 == wp[0].node():
2546 repo.dirstate.setparents(p1, p2)
2606 repo.dirstate.setparents(p1, p2)
2547 except error.RepoError:
2607 except error.RepoError:
2548 pass
2608 pass
2549 if opts.get('exact') or opts.get('import_branch'):
2609 if opts.get('exact') or opts.get('import_branch'):
2550 repo.dirstate.setbranch(branch or 'default')
2610 repo.dirstate.setbranch(branch or 'default')
2551
2611
2552 files = {}
2612 files = {}
2553 try:
2613 try:
2554 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2614 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2555 files=files, eolmode=None)
2615 files=files, eolmode=None)
2556 finally:
2616 finally:
2557 files = cmdutil.updatedir(ui, repo, files,
2617 files = cmdutil.updatedir(ui, repo, files,
2558 similarity=sim / 100.0)
2618 similarity=sim / 100.0)
2559 if opts.get('no_commit'):
2619 if opts.get('no_commit'):
2560 if message:
2620 if message:
2561 msgs.append(message)
2621 msgs.append(message)
2562 else:
2622 else:
2563 if opts.get('exact'):
2623 if opts.get('exact'):
2564 m = None
2624 m = None
2565 else:
2625 else:
2566 m = cmdutil.matchfiles(repo, files or [])
2626 m = cmdutil.matchfiles(repo, files or [])
2567 n = repo.commit(message, opts.get('user') or user,
2627 n = repo.commit(message, opts.get('user') or user,
2568 opts.get('date') or date, match=m,
2628 opts.get('date') or date, match=m,
2569 editor=cmdutil.commiteditor)
2629 editor=cmdutil.commiteditor)
2570 if opts.get('exact'):
2630 if opts.get('exact'):
2571 if hex(n) != nodeid:
2631 if hex(n) != nodeid:
2572 repo.rollback()
2632 repo.rollback()
2573 raise util.Abort(_('patch is damaged'
2633 raise util.Abort(_('patch is damaged'
2574 ' or loses information'))
2634 ' or loses information'))
2575 # Force a dirstate write so that the next transaction
2635 # Force a dirstate write so that the next transaction
2576 # backups an up-do-date file.
2636 # backups an up-do-date file.
2577 repo.dirstate.write()
2637 repo.dirstate.write()
2578 if n:
2638 if n:
2579 commitid = short(n)
2639 commitid = short(n)
2580
2640
2581 return commitid
2641 return commitid
2582 finally:
2642 finally:
2583 os.unlink(tmpname)
2643 os.unlink(tmpname)
2584
2644
2585 try:
2645 try:
2586 wlock = repo.wlock()
2646 wlock = repo.wlock()
2587 lock = repo.lock()
2647 lock = repo.lock()
2588 lastcommit = None
2648 lastcommit = None
2589 for p in patches:
2649 for p in patches:
2590 pf = os.path.join(d, p)
2650 pf = os.path.join(d, p)
2591
2651
2592 if pf == '-':
2652 if pf == '-':
2593 ui.status(_("applying patch from stdin\n"))
2653 ui.status(_("applying patch from stdin\n"))
2594 pf = sys.stdin
2654 pf = sys.stdin
2595 else:
2655 else:
2596 ui.status(_("applying %s\n") % p)
2656 ui.status(_("applying %s\n") % p)
2597 pf = url.open(ui, pf)
2657 pf = url.open(ui, pf)
2598
2658
2599 haspatch = False
2659 haspatch = False
2600 for hunk in patch.split(pf):
2660 for hunk in patch.split(pf):
2601 commitid = tryone(ui, hunk)
2661 commitid = tryone(ui, hunk)
2602 if commitid:
2662 if commitid:
2603 haspatch = True
2663 haspatch = True
2604 if lastcommit:
2664 if lastcommit:
2605 ui.status(_('applied %s\n') % lastcommit)
2665 ui.status(_('applied %s\n') % lastcommit)
2606 lastcommit = commitid
2666 lastcommit = commitid
2607
2667
2608 if not haspatch:
2668 if not haspatch:
2609 raise util.Abort(_('no diffs found'))
2669 raise util.Abort(_('no diffs found'))
2610
2670
2611 if msgs:
2671 if msgs:
2612 repo.opener('last-message.txt', 'wb').write('\n* * *\n'.join(msgs))
2672 repo.opener('last-message.txt', 'wb').write('\n* * *\n'.join(msgs))
2613 finally:
2673 finally:
2614 release(lock, wlock)
2674 release(lock, wlock)
2615
2675
2616 def incoming(ui, repo, source="default", **opts):
2676 def incoming(ui, repo, source="default", **opts):
2617 """show new changesets found in source
2677 """show new changesets found in source
2618
2678
2619 Show new changesets found in the specified path/URL or the default
2679 Show new changesets found in the specified path/URL or the default
2620 pull location. These are the changesets that would have been pulled
2680 pull location. These are the changesets that would have been pulled
2621 if a pull at the time you issued this command.
2681 if a pull at the time you issued this command.
2622
2682
2623 For remote repository, using --bundle avoids downloading the
2683 For remote repository, using --bundle avoids downloading the
2624 changesets twice if the incoming is followed by a pull.
2684 changesets twice if the incoming is followed by a pull.
2625
2685
2626 See pull for valid source format details.
2686 See pull for valid source format details.
2627
2687
2628 Returns 0 if there are incoming changes, 1 otherwise.
2688 Returns 0 if there are incoming changes, 1 otherwise.
2629 """
2689 """
2630 if opts.get('bundle') and opts.get('subrepos'):
2690 if opts.get('bundle') and opts.get('subrepos'):
2631 raise util.Abort(_('cannot combine --bundle and --subrepos'))
2691 raise util.Abort(_('cannot combine --bundle and --subrepos'))
2632
2692
2633 if opts.get('bookmarks'):
2693 if opts.get('bookmarks'):
2634 source, branches = hg.parseurl(ui.expandpath(source),
2694 source, branches = hg.parseurl(ui.expandpath(source),
2635 opts.get('branch'))
2695 opts.get('branch'))
2636 other = hg.repository(hg.remoteui(repo, opts), source)
2696 other = hg.repository(hg.remoteui(repo, opts), source)
2637 if 'bookmarks' not in other.listkeys('namespaces'):
2697 if 'bookmarks' not in other.listkeys('namespaces'):
2638 ui.warn(_("remote doesn't support bookmarks\n"))
2698 ui.warn(_("remote doesn't support bookmarks\n"))
2639 return 0
2699 return 0
2640 ui.status(_('comparing with %s\n') % util.hidepassword(source))
2700 ui.status(_('comparing with %s\n') % util.hidepassword(source))
2641 return bookmarks.diff(ui, repo, other)
2701 return bookmarks.diff(ui, repo, other)
2642
2702
2643 ret = hg.incoming(ui, repo, source, opts)
2703 ret = hg.incoming(ui, repo, source, opts)
2644 return ret
2704 return ret
2645
2705
2646 def init(ui, dest=".", **opts):
2706 def init(ui, dest=".", **opts):
2647 """create a new repository in the given directory
2707 """create a new repository in the given directory
2648
2708
2649 Initialize a new repository in the given directory. If the given
2709 Initialize a new repository in the given directory. If the given
2650 directory does not exist, it will be created.
2710 directory does not exist, it will be created.
2651
2711
2652 If no directory is given, the current directory is used.
2712 If no directory is given, the current directory is used.
2653
2713
2654 It is possible to specify an ``ssh://`` URL as the destination.
2714 It is possible to specify an ``ssh://`` URL as the destination.
2655 See :hg:`help urls` for more information.
2715 See :hg:`help urls` for more information.
2656
2716
2657 Returns 0 on success.
2717 Returns 0 on success.
2658 """
2718 """
2659 hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
2719 hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
2660
2720
2661 def locate(ui, repo, *pats, **opts):
2721 def locate(ui, repo, *pats, **opts):
2662 """locate files matching specific patterns
2722 """locate files matching specific patterns
2663
2723
2664 Print files under Mercurial control in the working directory whose
2724 Print files under Mercurial control in the working directory whose
2665 names match the given patterns.
2725 names match the given patterns.
2666
2726
2667 By default, this command searches all directories in the working
2727 By default, this command searches all directories in the working
2668 directory. To search just the current directory and its
2728 directory. To search just the current directory and its
2669 subdirectories, use "--include .".
2729 subdirectories, use "--include .".
2670
2730
2671 If no patterns are given to match, this command prints the names
2731 If no patterns are given to match, this command prints the names
2672 of all files under Mercurial control in the working directory.
2732 of all files under Mercurial control in the working directory.
2673
2733
2674 If you want to feed the output of this command into the "xargs"
2734 If you want to feed the output of this command into the "xargs"
2675 command, use the -0 option to both this command and "xargs". This
2735 command, use the -0 option to both this command and "xargs". This
2676 will avoid the problem of "xargs" treating single filenames that
2736 will avoid the problem of "xargs" treating single filenames that
2677 contain whitespace as multiple filenames.
2737 contain whitespace as multiple filenames.
2678
2738
2679 Returns 0 if a match is found, 1 otherwise.
2739 Returns 0 if a match is found, 1 otherwise.
2680 """
2740 """
2681 end = opts.get('print0') and '\0' or '\n'
2741 end = opts.get('print0') and '\0' or '\n'
2682 rev = cmdutil.revsingle(repo, opts.get('rev'), None).node()
2742 rev = cmdutil.revsingle(repo, opts.get('rev'), None).node()
2683
2743
2684 ret = 1
2744 ret = 1
2685 m = cmdutil.match(repo, pats, opts, default='relglob')
2745 m = cmdutil.match(repo, pats, opts, default='relglob')
2686 m.bad = lambda x, y: False
2746 m.bad = lambda x, y: False
2687 for abs in repo[rev].walk(m):
2747 for abs in repo[rev].walk(m):
2688 if not rev and abs not in repo.dirstate:
2748 if not rev and abs not in repo.dirstate:
2689 continue
2749 continue
2690 if opts.get('fullpath'):
2750 if opts.get('fullpath'):
2691 ui.write(repo.wjoin(abs), end)
2751 ui.write(repo.wjoin(abs), end)
2692 else:
2752 else:
2693 ui.write(((pats and m.rel(abs)) or abs), end)
2753 ui.write(((pats and m.rel(abs)) or abs), end)
2694 ret = 0
2754 ret = 0
2695
2755
2696 return ret
2756 return ret
2697
2757
2698 def log(ui, repo, *pats, **opts):
2758 def log(ui, repo, *pats, **opts):
2699 """show revision history of entire repository or files
2759 """show revision history of entire repository or files
2700
2760
2701 Print the revision history of the specified files or the entire
2761 Print the revision history of the specified files or the entire
2702 project.
2762 project.
2703
2763
2704 File history is shown without following rename or copy history of
2764 File history is shown without following rename or copy history of
2705 files. Use -f/--follow with a filename to follow history across
2765 files. Use -f/--follow with a filename to follow history across
2706 renames and copies. --follow without a filename will only show
2766 renames and copies. --follow without a filename will only show
2707 ancestors or descendants of the starting revision. --follow-first
2767 ancestors or descendants of the starting revision. --follow-first
2708 only follows the first parent of merge revisions.
2768 only follows the first parent of merge revisions.
2709
2769
2710 If no revision range is specified, the default is ``tip:0`` unless
2770 If no revision range is specified, the default is ``tip:0`` unless
2711 --follow is set, in which case the working directory parent is
2771 --follow is set, in which case the working directory parent is
2712 used as the starting revision. You can specify a revision set for
2772 used as the starting revision. You can specify a revision set for
2713 log, see :hg:`help revsets` for more information.
2773 log, see :hg:`help revsets` for more information.
2714
2774
2715 See :hg:`help dates` for a list of formats valid for -d/--date.
2775 See :hg:`help dates` for a list of formats valid for -d/--date.
2716
2776
2717 By default this command prints revision number and changeset id,
2777 By default this command prints revision number and changeset id,
2718 tags, non-trivial parents, user, date and time, and a summary for
2778 tags, non-trivial parents, user, date and time, and a summary for
2719 each commit. When the -v/--verbose switch is used, the list of
2779 each commit. When the -v/--verbose switch is used, the list of
2720 changed files and full commit message are shown.
2780 changed files and full commit message are shown.
2721
2781
2722 .. note::
2782 .. note::
2723 log -p/--patch may generate unexpected diff output for merge
2783 log -p/--patch may generate unexpected diff output for merge
2724 changesets, as it will only compare the merge changeset against
2784 changesets, as it will only compare the merge changeset against
2725 its first parent. Also, only files different from BOTH parents
2785 its first parent. Also, only files different from BOTH parents
2726 will appear in files:.
2786 will appear in files:.
2727
2787
2728 Returns 0 on success.
2788 Returns 0 on success.
2729 """
2789 """
2730
2790
2731 matchfn = cmdutil.match(repo, pats, opts)
2791 matchfn = cmdutil.match(repo, pats, opts)
2732 limit = cmdutil.loglimit(opts)
2792 limit = cmdutil.loglimit(opts)
2733 count = 0
2793 count = 0
2734
2794
2735 endrev = None
2795 endrev = None
2736 if opts.get('copies') and opts.get('rev'):
2796 if opts.get('copies') and opts.get('rev'):
2737 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2797 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2738
2798
2739 df = False
2799 df = False
2740 if opts["date"]:
2800 if opts["date"]:
2741 df = util.matchdate(opts["date"])
2801 df = util.matchdate(opts["date"])
2742
2802
2743 branches = opts.get('branch', []) + opts.get('only_branch', [])
2803 branches = opts.get('branch', []) + opts.get('only_branch', [])
2744 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2804 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2745
2805
2746 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2806 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2747 def prep(ctx, fns):
2807 def prep(ctx, fns):
2748 rev = ctx.rev()
2808 rev = ctx.rev()
2749 parents = [p for p in repo.changelog.parentrevs(rev)
2809 parents = [p for p in repo.changelog.parentrevs(rev)
2750 if p != nullrev]
2810 if p != nullrev]
2751 if opts.get('no_merges') and len(parents) == 2:
2811 if opts.get('no_merges') and len(parents) == 2:
2752 return
2812 return
2753 if opts.get('only_merges') and len(parents) != 2:
2813 if opts.get('only_merges') and len(parents) != 2:
2754 return
2814 return
2755 if opts.get('branch') and ctx.branch() not in opts['branch']:
2815 if opts.get('branch') and ctx.branch() not in opts['branch']:
2756 return
2816 return
2757 if df and not df(ctx.date()[0]):
2817 if df and not df(ctx.date()[0]):
2758 return
2818 return
2759 if opts['user'] and not [k for k in opts['user']
2819 if opts['user'] and not [k for k in opts['user']
2760 if k.lower() in ctx.user().lower()]:
2820 if k.lower() in ctx.user().lower()]:
2761 return
2821 return
2762 if opts.get('keyword'):
2822 if opts.get('keyword'):
2763 for k in [kw.lower() for kw in opts['keyword']]:
2823 for k in [kw.lower() for kw in opts['keyword']]:
2764 if (k in ctx.user().lower() or
2824 if (k in ctx.user().lower() or
2765 k in ctx.description().lower() or
2825 k in ctx.description().lower() or
2766 k in " ".join(ctx.files()).lower()):
2826 k in " ".join(ctx.files()).lower()):
2767 break
2827 break
2768 else:
2828 else:
2769 return
2829 return
2770
2830
2771 copies = None
2831 copies = None
2772 if opts.get('copies') and rev:
2832 if opts.get('copies') and rev:
2773 copies = []
2833 copies = []
2774 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2834 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2775 for fn in ctx.files():
2835 for fn in ctx.files():
2776 rename = getrenamed(fn, rev)
2836 rename = getrenamed(fn, rev)
2777 if rename:
2837 if rename:
2778 copies.append((fn, rename[0]))
2838 copies.append((fn, rename[0]))
2779
2839
2780 revmatchfn = None
2840 revmatchfn = None
2781 if opts.get('patch') or opts.get('stat'):
2841 if opts.get('patch') or opts.get('stat'):
2782 if opts.get('follow') or opts.get('follow_first'):
2842 if opts.get('follow') or opts.get('follow_first'):
2783 # note: this might be wrong when following through merges
2843 # note: this might be wrong when following through merges
2784 revmatchfn = cmdutil.match(repo, fns, default='path')
2844 revmatchfn = cmdutil.match(repo, fns, default='path')
2785 else:
2845 else:
2786 revmatchfn = matchfn
2846 revmatchfn = matchfn
2787
2847
2788 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2848 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2789
2849
2790 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2850 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2791 if count == limit:
2851 if count == limit:
2792 break
2852 break
2793 if displayer.flush(ctx.rev()):
2853 if displayer.flush(ctx.rev()):
2794 count += 1
2854 count += 1
2795 displayer.close()
2855 displayer.close()
2796
2856
2797 def manifest(ui, repo, node=None, rev=None):
2857 def manifest(ui, repo, node=None, rev=None):
2798 """output the current or given revision of the project manifest
2858 """output the current or given revision of the project manifest
2799
2859
2800 Print a list of version controlled files for the given revision.
2860 Print a list of version controlled files for the given revision.
2801 If no revision is given, the first parent of the working directory
2861 If no revision is given, the first parent of the working directory
2802 is used, or the null revision if no revision is checked out.
2862 is used, or the null revision if no revision is checked out.
2803
2863
2804 With -v, print file permissions, symlink and executable bits.
2864 With -v, print file permissions, symlink and executable bits.
2805 With --debug, print file revision hashes.
2865 With --debug, print file revision hashes.
2806
2866
2807 Returns 0 on success.
2867 Returns 0 on success.
2808 """
2868 """
2809
2869
2810 if rev and node:
2870 if rev and node:
2811 raise util.Abort(_("please specify just one revision"))
2871 raise util.Abort(_("please specify just one revision"))
2812
2872
2813 if not node:
2873 if not node:
2814 node = rev
2874 node = rev
2815
2875
2816 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2876 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2817 ctx = cmdutil.revsingle(repo, node)
2877 ctx = cmdutil.revsingle(repo, node)
2818 for f in ctx:
2878 for f in ctx:
2819 if ui.debugflag:
2879 if ui.debugflag:
2820 ui.write("%40s " % hex(ctx.manifest()[f]))
2880 ui.write("%40s " % hex(ctx.manifest()[f]))
2821 if ui.verbose:
2881 if ui.verbose:
2822 ui.write(decor[ctx.flags(f)])
2882 ui.write(decor[ctx.flags(f)])
2823 ui.write("%s\n" % f)
2883 ui.write("%s\n" % f)
2824
2884
2825 def merge(ui, repo, node=None, **opts):
2885 def merge(ui, repo, node=None, **opts):
2826 """merge working directory with another revision
2886 """merge working directory with another revision
2827
2887
2828 The current working directory is updated with all changes made in
2888 The current working directory is updated with all changes made in
2829 the requested revision since the last common predecessor revision.
2889 the requested revision since the last common predecessor revision.
2830
2890
2831 Files that changed between either parent are marked as changed for
2891 Files that changed between either parent are marked as changed for
2832 the next commit and a commit must be performed before any further
2892 the next commit and a commit must be performed before any further
2833 updates to the repository are allowed. The next commit will have
2893 updates to the repository are allowed. The next commit will have
2834 two parents.
2894 two parents.
2835
2895
2836 ``--tool`` can be used to specify the merge tool used for file
2896 ``--tool`` can be used to specify the merge tool used for file
2837 merges. It overrides the HGMERGE environment variable and your
2897 merges. It overrides the HGMERGE environment variable and your
2838 configuration files. See :hg:`help merge-tools` for options.
2898 configuration files. See :hg:`help merge-tools` for options.
2839
2899
2840 If no revision is specified, the working directory's parent is a
2900 If no revision is specified, the working directory's parent is a
2841 head revision, and the current branch contains exactly one other
2901 head revision, and the current branch contains exactly one other
2842 head, the other head is merged with by default. Otherwise, an
2902 head, the other head is merged with by default. Otherwise, an
2843 explicit revision with which to merge with must be provided.
2903 explicit revision with which to merge with must be provided.
2844
2904
2845 :hg:`resolve` must be used to resolve unresolved files.
2905 :hg:`resolve` must be used to resolve unresolved files.
2846
2906
2847 To undo an uncommitted merge, use :hg:`update --clean .` which
2907 To undo an uncommitted merge, use :hg:`update --clean .` which
2848 will check out a clean copy of the original merge parent, losing
2908 will check out a clean copy of the original merge parent, losing
2849 all changes.
2909 all changes.
2850
2910
2851 Returns 0 on success, 1 if there are unresolved files.
2911 Returns 0 on success, 1 if there are unresolved files.
2852 """
2912 """
2853
2913
2854 if opts.get('rev') and node:
2914 if opts.get('rev') and node:
2855 raise util.Abort(_("please specify just one revision"))
2915 raise util.Abort(_("please specify just one revision"))
2856 if not node:
2916 if not node:
2857 node = opts.get('rev')
2917 node = opts.get('rev')
2858
2918
2859 if not node:
2919 if not node:
2860 branch = repo[None].branch()
2920 branch = repo[None].branch()
2861 bheads = repo.branchheads(branch)
2921 bheads = repo.branchheads(branch)
2862 if len(bheads) > 2:
2922 if len(bheads) > 2:
2863 raise util.Abort(_(
2923 raise util.Abort(_(
2864 'branch \'%s\' has %d heads - '
2924 'branch \'%s\' has %d heads - '
2865 'please merge with an explicit rev\n'
2925 'please merge with an explicit rev\n'
2866 '(run \'hg heads .\' to see heads)')
2926 '(run \'hg heads .\' to see heads)')
2867 % (branch, len(bheads)))
2927 % (branch, len(bheads)))
2868
2928
2869 parent = repo.dirstate.p1()
2929 parent = repo.dirstate.p1()
2870 if len(bheads) == 1:
2930 if len(bheads) == 1:
2871 if len(repo.heads()) > 1:
2931 if len(repo.heads()) > 1:
2872 raise util.Abort(_(
2932 raise util.Abort(_(
2873 'branch \'%s\' has one head - '
2933 'branch \'%s\' has one head - '
2874 'please merge with an explicit rev\n'
2934 'please merge with an explicit rev\n'
2875 '(run \'hg heads\' to see all heads)')
2935 '(run \'hg heads\' to see all heads)')
2876 % branch)
2936 % branch)
2877 msg = _('there is nothing to merge')
2937 msg = _('there is nothing to merge')
2878 if parent != repo.lookup(repo[None].branch()):
2938 if parent != repo.lookup(repo[None].branch()):
2879 msg = _('%s - use "hg update" instead') % msg
2939 msg = _('%s - use "hg update" instead') % msg
2880 raise util.Abort(msg)
2940 raise util.Abort(msg)
2881
2941
2882 if parent not in bheads:
2942 if parent not in bheads:
2883 raise util.Abort(_('working dir not at a head rev - '
2943 raise util.Abort(_('working dir not at a head rev - '
2884 'use "hg update" or merge with an explicit rev'))
2944 'use "hg update" or merge with an explicit rev'))
2885 node = parent == bheads[0] and bheads[-1] or bheads[0]
2945 node = parent == bheads[0] and bheads[-1] or bheads[0]
2886 else:
2946 else:
2887 node = cmdutil.revsingle(repo, node).node()
2947 node = cmdutil.revsingle(repo, node).node()
2888
2948
2889 if opts.get('preview'):
2949 if opts.get('preview'):
2890 # find nodes that are ancestors of p2 but not of p1
2950 # find nodes that are ancestors of p2 but not of p1
2891 p1 = repo.lookup('.')
2951 p1 = repo.lookup('.')
2892 p2 = repo.lookup(node)
2952 p2 = repo.lookup(node)
2893 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2953 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2894
2954
2895 displayer = cmdutil.show_changeset(ui, repo, opts)
2955 displayer = cmdutil.show_changeset(ui, repo, opts)
2896 for node in nodes:
2956 for node in nodes:
2897 displayer.show(repo[node])
2957 displayer.show(repo[node])
2898 displayer.close()
2958 displayer.close()
2899 return 0
2959 return 0
2900
2960
2901 try:
2961 try:
2902 # ui.forcemerge is an internal variable, do not document
2962 # ui.forcemerge is an internal variable, do not document
2903 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2963 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2904 return hg.merge(repo, node, force=opts.get('force'))
2964 return hg.merge(repo, node, force=opts.get('force'))
2905 finally:
2965 finally:
2906 ui.setconfig('ui', 'forcemerge', '')
2966 ui.setconfig('ui', 'forcemerge', '')
2907
2967
2908 def outgoing(ui, repo, dest=None, **opts):
2968 def outgoing(ui, repo, dest=None, **opts):
2909 """show changesets not found in the destination
2969 """show changesets not found in the destination
2910
2970
2911 Show changesets not found in the specified destination repository
2971 Show changesets not found in the specified destination repository
2912 or the default push location. These are the changesets that would
2972 or the default push location. These are the changesets that would
2913 be pushed if a push was requested.
2973 be pushed if a push was requested.
2914
2974
2915 See pull for details of valid destination formats.
2975 See pull for details of valid destination formats.
2916
2976
2917 Returns 0 if there are outgoing changes, 1 otherwise.
2977 Returns 0 if there are outgoing changes, 1 otherwise.
2918 """
2978 """
2919
2979
2920 if opts.get('bookmarks'):
2980 if opts.get('bookmarks'):
2921 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2981 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2922 dest, branches = hg.parseurl(dest, opts.get('branch'))
2982 dest, branches = hg.parseurl(dest, opts.get('branch'))
2923 other = hg.repository(hg.remoteui(repo, opts), dest)
2983 other = hg.repository(hg.remoteui(repo, opts), dest)
2924 if 'bookmarks' not in other.listkeys('namespaces'):
2984 if 'bookmarks' not in other.listkeys('namespaces'):
2925 ui.warn(_("remote doesn't support bookmarks\n"))
2985 ui.warn(_("remote doesn't support bookmarks\n"))
2926 return 0
2986 return 0
2927 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
2987 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
2928 return bookmarks.diff(ui, other, repo)
2988 return bookmarks.diff(ui, other, repo)
2929
2989
2930 ret = hg.outgoing(ui, repo, dest, opts)
2990 ret = hg.outgoing(ui, repo, dest, opts)
2931 return ret
2991 return ret
2932
2992
2933 def parents(ui, repo, file_=None, **opts):
2993 def parents(ui, repo, file_=None, **opts):
2934 """show the parents of the working directory or revision
2994 """show the parents of the working directory or revision
2935
2995
2936 Print the working directory's parent revisions. If a revision is
2996 Print the working directory's parent revisions. If a revision is
2937 given via -r/--rev, the parent of that revision will be printed.
2997 given via -r/--rev, the parent of that revision will be printed.
2938 If a file argument is given, the revision in which the file was
2998 If a file argument is given, the revision in which the file was
2939 last changed (before the working directory revision or the
2999 last changed (before the working directory revision or the
2940 argument to --rev if given) is printed.
3000 argument to --rev if given) is printed.
2941
3001
2942 Returns 0 on success.
3002 Returns 0 on success.
2943 """
3003 """
2944
3004
2945 ctx = cmdutil.revsingle(repo, opts.get('rev'), None)
3005 ctx = cmdutil.revsingle(repo, opts.get('rev'), None)
2946
3006
2947 if file_:
3007 if file_:
2948 m = cmdutil.match(repo, (file_,), opts)
3008 m = cmdutil.match(repo, (file_,), opts)
2949 if m.anypats() or len(m.files()) != 1:
3009 if m.anypats() or len(m.files()) != 1:
2950 raise util.Abort(_('can only specify an explicit filename'))
3010 raise util.Abort(_('can only specify an explicit filename'))
2951 file_ = m.files()[0]
3011 file_ = m.files()[0]
2952 filenodes = []
3012 filenodes = []
2953 for cp in ctx.parents():
3013 for cp in ctx.parents():
2954 if not cp:
3014 if not cp:
2955 continue
3015 continue
2956 try:
3016 try:
2957 filenodes.append(cp.filenode(file_))
3017 filenodes.append(cp.filenode(file_))
2958 except error.LookupError:
3018 except error.LookupError:
2959 pass
3019 pass
2960 if not filenodes:
3020 if not filenodes:
2961 raise util.Abort(_("'%s' not found in manifest!") % file_)
3021 raise util.Abort(_("'%s' not found in manifest!") % file_)
2962 fl = repo.file(file_)
3022 fl = repo.file(file_)
2963 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
3023 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2964 else:
3024 else:
2965 p = [cp.node() for cp in ctx.parents()]
3025 p = [cp.node() for cp in ctx.parents()]
2966
3026
2967 displayer = cmdutil.show_changeset(ui, repo, opts)
3027 displayer = cmdutil.show_changeset(ui, repo, opts)
2968 for n in p:
3028 for n in p:
2969 if n != nullid:
3029 if n != nullid:
2970 displayer.show(repo[n])
3030 displayer.show(repo[n])
2971 displayer.close()
3031 displayer.close()
2972
3032
2973 def paths(ui, repo, search=None):
3033 def paths(ui, repo, search=None):
2974 """show aliases for remote repositories
3034 """show aliases for remote repositories
2975
3035
2976 Show definition of symbolic path name NAME. If no name is given,
3036 Show definition of symbolic path name NAME. If no name is given,
2977 show definition of all available names.
3037 show definition of all available names.
2978
3038
2979 Path names are defined in the [paths] section of your
3039 Path names are defined in the [paths] section of your
2980 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3040 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
2981 repository, ``.hg/hgrc`` is used, too.
3041 repository, ``.hg/hgrc`` is used, too.
2982
3042
2983 The path names ``default`` and ``default-push`` have a special
3043 The path names ``default`` and ``default-push`` have a special
2984 meaning. When performing a push or pull operation, they are used
3044 meaning. When performing a push or pull operation, they are used
2985 as fallbacks if no location is specified on the command-line.
3045 as fallbacks if no location is specified on the command-line.
2986 When ``default-push`` is set, it will be used for push and
3046 When ``default-push`` is set, it will be used for push and
2987 ``default`` will be used for pull; otherwise ``default`` is used
3047 ``default`` will be used for pull; otherwise ``default`` is used
2988 as the fallback for both. When cloning a repository, the clone
3048 as the fallback for both. When cloning a repository, the clone
2989 source is written as ``default`` in ``.hg/hgrc``. Note that
3049 source is written as ``default`` in ``.hg/hgrc``. Note that
2990 ``default`` and ``default-push`` apply to all inbound (e.g.
3050 ``default`` and ``default-push`` apply to all inbound (e.g.
2991 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
3051 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2992 :hg:`bundle`) operations.
3052 :hg:`bundle`) operations.
2993
3053
2994 See :hg:`help urls` for more information.
3054 See :hg:`help urls` for more information.
2995
3055
2996 Returns 0 on success.
3056 Returns 0 on success.
2997 """
3057 """
2998 if search:
3058 if search:
2999 for name, path in ui.configitems("paths"):
3059 for name, path in ui.configitems("paths"):
3000 if name == search:
3060 if name == search:
3001 ui.write("%s\n" % util.hidepassword(path))
3061 ui.write("%s\n" % util.hidepassword(path))
3002 return
3062 return
3003 ui.warn(_("not found!\n"))
3063 ui.warn(_("not found!\n"))
3004 return 1
3064 return 1
3005 else:
3065 else:
3006 for name, path in ui.configitems("paths"):
3066 for name, path in ui.configitems("paths"):
3007 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
3067 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
3008
3068
3009 def postincoming(ui, repo, modheads, optupdate, checkout):
3069 def postincoming(ui, repo, modheads, optupdate, checkout):
3010 if modheads == 0:
3070 if modheads == 0:
3011 return
3071 return
3012 if optupdate:
3072 if optupdate:
3013 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
3073 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
3014 return hg.update(repo, checkout)
3074 return hg.update(repo, checkout)
3015 else:
3075 else:
3016 ui.status(_("not updating, since new heads added\n"))
3076 ui.status(_("not updating, since new heads added\n"))
3017 if modheads > 1:
3077 if modheads > 1:
3018 currentbranchheads = len(repo.branchheads())
3078 currentbranchheads = len(repo.branchheads())
3019 if currentbranchheads == modheads:
3079 if currentbranchheads == modheads:
3020 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3080 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3021 elif currentbranchheads > 1:
3081 elif currentbranchheads > 1:
3022 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
3082 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
3023 else:
3083 else:
3024 ui.status(_("(run 'hg heads' to see heads)\n"))
3084 ui.status(_("(run 'hg heads' to see heads)\n"))
3025 else:
3085 else:
3026 ui.status(_("(run 'hg update' to get a working copy)\n"))
3086 ui.status(_("(run 'hg update' to get a working copy)\n"))
3027
3087
3028 def pull(ui, repo, source="default", **opts):
3088 def pull(ui, repo, source="default", **opts):
3029 """pull changes from the specified source
3089 """pull changes from the specified source
3030
3090
3031 Pull changes from a remote repository to a local one.
3091 Pull changes from a remote repository to a local one.
3032
3092
3033 This finds all changes from the repository at the specified path
3093 This finds all changes from the repository at the specified path
3034 or URL and adds them to a local repository (the current one unless
3094 or URL and adds them to a local repository (the current one unless
3035 -R is specified). By default, this does not update the copy of the
3095 -R is specified). By default, this does not update the copy of the
3036 project in the working directory.
3096 project in the working directory.
3037
3097
3038 Use :hg:`incoming` if you want to see what would have been added
3098 Use :hg:`incoming` if you want to see what would have been added
3039 by a pull at the time you issued this command. If you then decide
3099 by a pull at the time you issued this command. If you then decide
3040 to add those changes to the repository, you should use :hg:`pull
3100 to add those changes to the repository, you should use :hg:`pull
3041 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3101 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3042
3102
3043 If SOURCE is omitted, the 'default' path will be used.
3103 If SOURCE is omitted, the 'default' path will be used.
3044 See :hg:`help urls` for more information.
3104 See :hg:`help urls` for more information.
3045
3105
3046 Returns 0 on success, 1 if an update had unresolved files.
3106 Returns 0 on success, 1 if an update had unresolved files.
3047 """
3107 """
3048 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3108 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3049 other = hg.repository(hg.remoteui(repo, opts), source)
3109 other = hg.repository(hg.remoteui(repo, opts), source)
3050 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3110 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3051 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3111 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3052
3112
3053 if opts.get('bookmark'):
3113 if opts.get('bookmark'):
3054 if not revs:
3114 if not revs:
3055 revs = []
3115 revs = []
3056 rb = other.listkeys('bookmarks')
3116 rb = other.listkeys('bookmarks')
3057 for b in opts['bookmark']:
3117 for b in opts['bookmark']:
3058 if b not in rb:
3118 if b not in rb:
3059 raise util.Abort(_('remote bookmark %s not found!') % b)
3119 raise util.Abort(_('remote bookmark %s not found!') % b)
3060 revs.append(rb[b])
3120 revs.append(rb[b])
3061
3121
3062 if revs:
3122 if revs:
3063 try:
3123 try:
3064 revs = [other.lookup(rev) for rev in revs]
3124 revs = [other.lookup(rev) for rev in revs]
3065 except error.CapabilityError:
3125 except error.CapabilityError:
3066 err = _("other repository doesn't support revision lookup, "
3126 err = _("other repository doesn't support revision lookup, "
3067 "so a rev cannot be specified.")
3127 "so a rev cannot be specified.")
3068 raise util.Abort(err)
3128 raise util.Abort(err)
3069
3129
3070 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
3130 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
3071 bookmarks.updatefromremote(ui, repo, other)
3131 bookmarks.updatefromremote(ui, repo, other)
3072 if checkout:
3132 if checkout:
3073 checkout = str(repo.changelog.rev(other.lookup(checkout)))
3133 checkout = str(repo.changelog.rev(other.lookup(checkout)))
3074 repo._subtoppath = source
3134 repo._subtoppath = source
3075 try:
3135 try:
3076 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
3136 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
3077
3137
3078 finally:
3138 finally:
3079 del repo._subtoppath
3139 del repo._subtoppath
3080
3140
3081 # update specified bookmarks
3141 # update specified bookmarks
3082 if opts.get('bookmark'):
3142 if opts.get('bookmark'):
3083 for b in opts['bookmark']:
3143 for b in opts['bookmark']:
3084 # explicit pull overrides local bookmark if any
3144 # explicit pull overrides local bookmark if any
3085 ui.status(_("importing bookmark %s\n") % b)
3145 ui.status(_("importing bookmark %s\n") % b)
3086 repo._bookmarks[b] = repo[rb[b]].node()
3146 repo._bookmarks[b] = repo[rb[b]].node()
3087 bookmarks.write(repo)
3147 bookmarks.write(repo)
3088
3148
3089 return ret
3149 return ret
3090
3150
3091 def push(ui, repo, dest=None, **opts):
3151 def push(ui, repo, dest=None, **opts):
3092 """push changes to the specified destination
3152 """push changes to the specified destination
3093
3153
3094 Push changesets from the local repository to the specified
3154 Push changesets from the local repository to the specified
3095 destination.
3155 destination.
3096
3156
3097 This operation is symmetrical to pull: it is identical to a pull
3157 This operation is symmetrical to pull: it is identical to a pull
3098 in the destination repository from the current one.
3158 in the destination repository from the current one.
3099
3159
3100 By default, push will not allow creation of new heads at the
3160 By default, push will not allow creation of new heads at the
3101 destination, since multiple heads would make it unclear which head
3161 destination, since multiple heads would make it unclear which head
3102 to use. In this situation, it is recommended to pull and merge
3162 to use. In this situation, it is recommended to pull and merge
3103 before pushing.
3163 before pushing.
3104
3164
3105 Use --new-branch if you want to allow push to create a new named
3165 Use --new-branch if you want to allow push to create a new named
3106 branch that is not present at the destination. This allows you to
3166 branch that is not present at the destination. This allows you to
3107 only create a new branch without forcing other changes.
3167 only create a new branch without forcing other changes.
3108
3168
3109 Use -f/--force to override the default behavior and push all
3169 Use -f/--force to override the default behavior and push all
3110 changesets on all branches.
3170 changesets on all branches.
3111
3171
3112 If -r/--rev is used, the specified revision and all its ancestors
3172 If -r/--rev is used, the specified revision and all its ancestors
3113 will be pushed to the remote repository.
3173 will be pushed to the remote repository.
3114
3174
3115 Please see :hg:`help urls` for important details about ``ssh://``
3175 Please see :hg:`help urls` for important details about ``ssh://``
3116 URLs. If DESTINATION is omitted, a default path will be used.
3176 URLs. If DESTINATION is omitted, a default path will be used.
3117
3177
3118 Returns 0 if push was successful, 1 if nothing to push.
3178 Returns 0 if push was successful, 1 if nothing to push.
3119 """
3179 """
3120
3180
3121 if opts.get('bookmark'):
3181 if opts.get('bookmark'):
3122 for b in opts['bookmark']:
3182 for b in opts['bookmark']:
3123 # translate -B options to -r so changesets get pushed
3183 # translate -B options to -r so changesets get pushed
3124 if b in repo._bookmarks:
3184 if b in repo._bookmarks:
3125 opts.setdefault('rev', []).append(b)
3185 opts.setdefault('rev', []).append(b)
3126 else:
3186 else:
3127 # if we try to push a deleted bookmark, translate it to null
3187 # if we try to push a deleted bookmark, translate it to null
3128 # this lets simultaneous -r, -b options continue working
3188 # this lets simultaneous -r, -b options continue working
3129 opts.setdefault('rev', []).append("null")
3189 opts.setdefault('rev', []).append("null")
3130
3190
3131 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3191 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3132 dest, branches = hg.parseurl(dest, opts.get('branch'))
3192 dest, branches = hg.parseurl(dest, opts.get('branch'))
3133 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
3193 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
3134 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
3194 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
3135 other = hg.repository(hg.remoteui(repo, opts), dest)
3195 other = hg.repository(hg.remoteui(repo, opts), dest)
3136 if revs:
3196 if revs:
3137 revs = [repo.lookup(rev) for rev in revs]
3197 revs = [repo.lookup(rev) for rev in revs]
3138
3198
3139 repo._subtoppath = dest
3199 repo._subtoppath = dest
3140 try:
3200 try:
3141 # push subrepos depth-first for coherent ordering
3201 # push subrepos depth-first for coherent ordering
3142 c = repo['']
3202 c = repo['']
3143 subs = c.substate # only repos that are committed
3203 subs = c.substate # only repos that are committed
3144 for s in sorted(subs):
3204 for s in sorted(subs):
3145 if not c.sub(s).push(opts.get('force')):
3205 if not c.sub(s).push(opts.get('force')):
3146 return False
3206 return False
3147 finally:
3207 finally:
3148 del repo._subtoppath
3208 del repo._subtoppath
3149 result = repo.push(other, opts.get('force'), revs=revs,
3209 result = repo.push(other, opts.get('force'), revs=revs,
3150 newbranch=opts.get('new_branch'))
3210 newbranch=opts.get('new_branch'))
3151
3211
3152 result = (result == 0)
3212 result = (result == 0)
3153
3213
3154 if opts.get('bookmark'):
3214 if opts.get('bookmark'):
3155 rb = other.listkeys('bookmarks')
3215 rb = other.listkeys('bookmarks')
3156 for b in opts['bookmark']:
3216 for b in opts['bookmark']:
3157 # explicit push overrides remote bookmark if any
3217 # explicit push overrides remote bookmark if any
3158 if b in repo._bookmarks:
3218 if b in repo._bookmarks:
3159 ui.status(_("exporting bookmark %s\n") % b)
3219 ui.status(_("exporting bookmark %s\n") % b)
3160 new = repo[b].hex()
3220 new = repo[b].hex()
3161 elif b in rb:
3221 elif b in rb:
3162 ui.status(_("deleting remote bookmark %s\n") % b)
3222 ui.status(_("deleting remote bookmark %s\n") % b)
3163 new = '' # delete
3223 new = '' # delete
3164 else:
3224 else:
3165 ui.warn(_('bookmark %s does not exist on the local '
3225 ui.warn(_('bookmark %s does not exist on the local '
3166 'or remote repository!\n') % b)
3226 'or remote repository!\n') % b)
3167 return 2
3227 return 2
3168 old = rb.get(b, '')
3228 old = rb.get(b, '')
3169 r = other.pushkey('bookmarks', b, old, new)
3229 r = other.pushkey('bookmarks', b, old, new)
3170 if not r:
3230 if not r:
3171 ui.warn(_('updating bookmark %s failed!\n') % b)
3231 ui.warn(_('updating bookmark %s failed!\n') % b)
3172 if not result:
3232 if not result:
3173 result = 2
3233 result = 2
3174
3234
3175 return result
3235 return result
3176
3236
3177 def recover(ui, repo):
3237 def recover(ui, repo):
3178 """roll back an interrupted transaction
3238 """roll back an interrupted transaction
3179
3239
3180 Recover from an interrupted commit or pull.
3240 Recover from an interrupted commit or pull.
3181
3241
3182 This command tries to fix the repository status after an
3242 This command tries to fix the repository status after an
3183 interrupted operation. It should only be necessary when Mercurial
3243 interrupted operation. It should only be necessary when Mercurial
3184 suggests it.
3244 suggests it.
3185
3245
3186 Returns 0 if successful, 1 if nothing to recover or verify fails.
3246 Returns 0 if successful, 1 if nothing to recover or verify fails.
3187 """
3247 """
3188 if repo.recover():
3248 if repo.recover():
3189 return hg.verify(repo)
3249 return hg.verify(repo)
3190 return 1
3250 return 1
3191
3251
3192 def remove(ui, repo, *pats, **opts):
3252 def remove(ui, repo, *pats, **opts):
3193 """remove the specified files on the next commit
3253 """remove the specified files on the next commit
3194
3254
3195 Schedule the indicated files for removal from the repository.
3255 Schedule the indicated files for removal from the repository.
3196
3256
3197 This only removes files from the current branch, not from the
3257 This only removes files from the current branch, not from the
3198 entire project history. -A/--after can be used to remove only
3258 entire project history. -A/--after can be used to remove only
3199 files that have already been deleted, -f/--force can be used to
3259 files that have already been deleted, -f/--force can be used to
3200 force deletion, and -Af can be used to remove files from the next
3260 force deletion, and -Af can be used to remove files from the next
3201 revision without deleting them from the working directory.
3261 revision without deleting them from the working directory.
3202
3262
3203 The following table details the behavior of remove for different
3263 The following table details the behavior of remove for different
3204 file states (columns) and option combinations (rows). The file
3264 file states (columns) and option combinations (rows). The file
3205 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
3265 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
3206 reported by :hg:`status`). The actions are Warn, Remove (from
3266 reported by :hg:`status`). The actions are Warn, Remove (from
3207 branch) and Delete (from disk)::
3267 branch) and Delete (from disk)::
3208
3268
3209 A C M !
3269 A C M !
3210 none W RD W R
3270 none W RD W R
3211 -f R RD RD R
3271 -f R RD RD R
3212 -A W W W R
3272 -A W W W R
3213 -Af R R R R
3273 -Af R R R R
3214
3274
3215 This command schedules the files to be removed at the next commit.
3275 This command schedules the files to be removed at the next commit.
3216 To undo a remove before that, see :hg:`revert`.
3276 To undo a remove before that, see :hg:`revert`.
3217
3277
3218 Returns 0 on success, 1 if any warnings encountered.
3278 Returns 0 on success, 1 if any warnings encountered.
3219 """
3279 """
3220
3280
3221 ret = 0
3281 ret = 0
3222 after, force = opts.get('after'), opts.get('force')
3282 after, force = opts.get('after'), opts.get('force')
3223 if not pats and not after:
3283 if not pats and not after:
3224 raise util.Abort(_('no files specified'))
3284 raise util.Abort(_('no files specified'))
3225
3285
3226 m = cmdutil.match(repo, pats, opts)
3286 m = cmdutil.match(repo, pats, opts)
3227 s = repo.status(match=m, clean=True)
3287 s = repo.status(match=m, clean=True)
3228 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
3288 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
3229
3289
3230 for f in m.files():
3290 for f in m.files():
3231 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
3291 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
3232 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
3292 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
3233 ret = 1
3293 ret = 1
3234
3294
3235 if force:
3295 if force:
3236 remove, forget = modified + deleted + clean, added
3296 remove, forget = modified + deleted + clean, added
3237 elif after:
3297 elif after:
3238 remove, forget = deleted, []
3298 remove, forget = deleted, []
3239 for f in modified + added + clean:
3299 for f in modified + added + clean:
3240 ui.warn(_('not removing %s: file still exists (use -f'
3300 ui.warn(_('not removing %s: file still exists (use -f'
3241 ' to force removal)\n') % m.rel(f))
3301 ' to force removal)\n') % m.rel(f))
3242 ret = 1
3302 ret = 1
3243 else:
3303 else:
3244 remove, forget = deleted + clean, []
3304 remove, forget = deleted + clean, []
3245 for f in modified:
3305 for f in modified:
3246 ui.warn(_('not removing %s: file is modified (use -f'
3306 ui.warn(_('not removing %s: file is modified (use -f'
3247 ' to force removal)\n') % m.rel(f))
3307 ' to force removal)\n') % m.rel(f))
3248 ret = 1
3308 ret = 1
3249 for f in added:
3309 for f in added:
3250 ui.warn(_('not removing %s: file has been marked for add (use -f'
3310 ui.warn(_('not removing %s: file has been marked for add (use -f'
3251 ' to force removal)\n') % m.rel(f))
3311 ' to force removal)\n') % m.rel(f))
3252 ret = 1
3312 ret = 1
3253
3313
3254 for f in sorted(remove + forget):
3314 for f in sorted(remove + forget):
3255 if ui.verbose or not m.exact(f):
3315 if ui.verbose or not m.exact(f):
3256 ui.status(_('removing %s\n') % m.rel(f))
3316 ui.status(_('removing %s\n') % m.rel(f))
3257
3317
3258 repo[None].forget(forget)
3318 repo[None].forget(forget)
3259 repo[None].remove(remove, unlink=not after)
3319 repo[None].remove(remove, unlink=not after)
3260 return ret
3320 return ret
3261
3321
3262 def rename(ui, repo, *pats, **opts):
3322 def rename(ui, repo, *pats, **opts):
3263 """rename files; equivalent of copy + remove
3323 """rename files; equivalent of copy + remove
3264
3324
3265 Mark dest as copies of sources; mark sources for deletion. If dest
3325 Mark dest as copies of sources; mark sources for deletion. If dest
3266 is a directory, copies are put in that directory. If dest is a
3326 is a directory, copies are put in that directory. If dest is a
3267 file, there can only be one source.
3327 file, there can only be one source.
3268
3328
3269 By default, this command copies the contents of files as they
3329 By default, this command copies the contents of files as they
3270 exist in the working directory. If invoked with -A/--after, the
3330 exist in the working directory. If invoked with -A/--after, the
3271 operation is recorded, but no copying is performed.
3331 operation is recorded, but no copying is performed.
3272
3332
3273 This command takes effect at the next commit. To undo a rename
3333 This command takes effect at the next commit. To undo a rename
3274 before that, see :hg:`revert`.
3334 before that, see :hg:`revert`.
3275
3335
3276 Returns 0 on success, 1 if errors are encountered.
3336 Returns 0 on success, 1 if errors are encountered.
3277 """
3337 """
3278 wlock = repo.wlock(False)
3338 wlock = repo.wlock(False)
3279 try:
3339 try:
3280 return cmdutil.copy(ui, repo, pats, opts, rename=True)
3340 return cmdutil.copy(ui, repo, pats, opts, rename=True)
3281 finally:
3341 finally:
3282 wlock.release()
3342 wlock.release()
3283
3343
3284 def resolve(ui, repo, *pats, **opts):
3344 def resolve(ui, repo, *pats, **opts):
3285 """redo merges or set/view the merge status of files
3345 """redo merges or set/view the merge status of files
3286
3346
3287 Merges with unresolved conflicts are often the result of
3347 Merges with unresolved conflicts are often the result of
3288 non-interactive merging using the ``internal:merge`` configuration
3348 non-interactive merging using the ``internal:merge`` configuration
3289 setting, or a command-line merge tool like ``diff3``. The resolve
3349 setting, or a command-line merge tool like ``diff3``. The resolve
3290 command is used to manage the files involved in a merge, after
3350 command is used to manage the files involved in a merge, after
3291 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
3351 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
3292 working directory must have two parents).
3352 working directory must have two parents).
3293
3353
3294 The resolve command can be used in the following ways:
3354 The resolve command can be used in the following ways:
3295
3355
3296 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
3356 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
3297 files, discarding any previous merge attempts. Re-merging is not
3357 files, discarding any previous merge attempts. Re-merging is not
3298 performed for files already marked as resolved. Use ``--all/-a``
3358 performed for files already marked as resolved. Use ``--all/-a``
3299 to selects all unresolved files. ``--tool`` can be used to specify
3359 to selects all unresolved files. ``--tool`` can be used to specify
3300 the merge tool used for the given files. It overrides the HGMERGE
3360 the merge tool used for the given files. It overrides the HGMERGE
3301 environment variable and your configuration files.
3361 environment variable and your configuration files.
3302
3362
3303 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
3363 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
3304 (e.g. after having manually fixed-up the files). The default is
3364 (e.g. after having manually fixed-up the files). The default is
3305 to mark all unresolved files.
3365 to mark all unresolved files.
3306
3366
3307 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
3367 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
3308 default is to mark all resolved files.
3368 default is to mark all resolved files.
3309
3369
3310 - :hg:`resolve -l`: list files which had or still have conflicts.
3370 - :hg:`resolve -l`: list files which had or still have conflicts.
3311 In the printed list, ``U`` = unresolved and ``R`` = resolved.
3371 In the printed list, ``U`` = unresolved and ``R`` = resolved.
3312
3372
3313 Note that Mercurial will not let you commit files with unresolved
3373 Note that Mercurial will not let you commit files with unresolved
3314 merge conflicts. You must use :hg:`resolve -m ...` before you can
3374 merge conflicts. You must use :hg:`resolve -m ...` before you can
3315 commit after a conflicting merge.
3375 commit after a conflicting merge.
3316
3376
3317 Returns 0 on success, 1 if any files fail a resolve attempt.
3377 Returns 0 on success, 1 if any files fail a resolve attempt.
3318 """
3378 """
3319
3379
3320 all, mark, unmark, show, nostatus = \
3380 all, mark, unmark, show, nostatus = \
3321 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
3381 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
3322
3382
3323 if (show and (mark or unmark)) or (mark and unmark):
3383 if (show and (mark or unmark)) or (mark and unmark):
3324 raise util.Abort(_("too many options specified"))
3384 raise util.Abort(_("too many options specified"))
3325 if pats and all:
3385 if pats and all:
3326 raise util.Abort(_("can't specify --all and patterns"))
3386 raise util.Abort(_("can't specify --all and patterns"))
3327 if not (all or pats or show or mark or unmark):
3387 if not (all or pats or show or mark or unmark):
3328 raise util.Abort(_('no files or directories specified; '
3388 raise util.Abort(_('no files or directories specified; '
3329 'use --all to remerge all files'))
3389 'use --all to remerge all files'))
3330
3390
3331 ms = mergemod.mergestate(repo)
3391 ms = mergemod.mergestate(repo)
3332 m = cmdutil.match(repo, pats, opts)
3392 m = cmdutil.match(repo, pats, opts)
3333 ret = 0
3393 ret = 0
3334
3394
3335 for f in ms:
3395 for f in ms:
3336 if m(f):
3396 if m(f):
3337 if show:
3397 if show:
3338 if nostatus:
3398 if nostatus:
3339 ui.write("%s\n" % f)
3399 ui.write("%s\n" % f)
3340 else:
3400 else:
3341 ui.write("%s %s\n" % (ms[f].upper(), f),
3401 ui.write("%s %s\n" % (ms[f].upper(), f),
3342 label='resolve.' +
3402 label='resolve.' +
3343 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3403 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3344 elif mark:
3404 elif mark:
3345 ms.mark(f, "r")
3405 ms.mark(f, "r")
3346 elif unmark:
3406 elif unmark:
3347 ms.mark(f, "u")
3407 ms.mark(f, "u")
3348 else:
3408 else:
3349 wctx = repo[None]
3409 wctx = repo[None]
3350 mctx = wctx.parents()[-1]
3410 mctx = wctx.parents()[-1]
3351
3411
3352 # backup pre-resolve (merge uses .orig for its own purposes)
3412 # backup pre-resolve (merge uses .orig for its own purposes)
3353 a = repo.wjoin(f)
3413 a = repo.wjoin(f)
3354 util.copyfile(a, a + ".resolve")
3414 util.copyfile(a, a + ".resolve")
3355
3415
3356 try:
3416 try:
3357 # resolve file
3417 # resolve file
3358 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3418 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3359 if ms.resolve(f, wctx, mctx):
3419 if ms.resolve(f, wctx, mctx):
3360 ret = 1
3420 ret = 1
3361 finally:
3421 finally:
3362 ui.setconfig('ui', 'forcemerge', '')
3422 ui.setconfig('ui', 'forcemerge', '')
3363
3423
3364 # replace filemerge's .orig file with our resolve file
3424 # replace filemerge's .orig file with our resolve file
3365 util.rename(a + ".resolve", a + ".orig")
3425 util.rename(a + ".resolve", a + ".orig")
3366
3426
3367 ms.commit()
3427 ms.commit()
3368 return ret
3428 return ret
3369
3429
3370 def revert(ui, repo, *pats, **opts):
3430 def revert(ui, repo, *pats, **opts):
3371 """restore individual files or directories to an earlier state
3431 """restore individual files or directories to an earlier state
3372
3432
3373 .. note::
3433 .. note::
3374 This command is most likely not what you are looking for.
3434 This command is most likely not what you are looking for.
3375 Revert will partially overwrite content in the working
3435 Revert will partially overwrite content in the working
3376 directory without changing the working directory parents. Use
3436 directory without changing the working directory parents. Use
3377 :hg:`update -r rev` to check out earlier revisions, or
3437 :hg:`update -r rev` to check out earlier revisions, or
3378 :hg:`update --clean .` to undo a merge which has added another
3438 :hg:`update --clean .` to undo a merge which has added another
3379 parent.
3439 parent.
3380
3440
3381 With no revision specified, revert the named files or directories
3441 With no revision specified, revert the named files or directories
3382 to the contents they had in the parent of the working directory.
3442 to the contents they had in the parent of the working directory.
3383 This restores the contents of the affected files to an unmodified
3443 This restores the contents of the affected files to an unmodified
3384 state and unschedules adds, removes, copies, and renames. If the
3444 state and unschedules adds, removes, copies, and renames. If the
3385 working directory has two parents, you must explicitly specify a
3445 working directory has two parents, you must explicitly specify a
3386 revision.
3446 revision.
3387
3447
3388 Using the -r/--rev option, revert the given files or directories
3448 Using the -r/--rev option, revert the given files or directories
3389 to their contents as of a specific revision. This can be helpful
3449 to their contents as of a specific revision. This can be helpful
3390 to "roll back" some or all of an earlier change. See :hg:`help
3450 to "roll back" some or all of an earlier change. See :hg:`help
3391 dates` for a list of formats valid for -d/--date.
3451 dates` for a list of formats valid for -d/--date.
3392
3452
3393 Revert modifies the working directory. It does not commit any
3453 Revert modifies the working directory. It does not commit any
3394 changes, or change the parent of the working directory. If you
3454 changes, or change the parent of the working directory. If you
3395 revert to a revision other than the parent of the working
3455 revert to a revision other than the parent of the working
3396 directory, the reverted files will thus appear modified
3456 directory, the reverted files will thus appear modified
3397 afterwards.
3457 afterwards.
3398
3458
3399 If a file has been deleted, it is restored. Files scheduled for
3459 If a file has been deleted, it is restored. Files scheduled for
3400 addition are just unscheduled and left as they are. If the
3460 addition are just unscheduled and left as they are. If the
3401 executable mode of a file was changed, it is reset.
3461 executable mode of a file was changed, it is reset.
3402
3462
3403 If names are given, all files matching the names are reverted.
3463 If names are given, all files matching the names are reverted.
3404 If no arguments are given, no files are reverted.
3464 If no arguments are given, no files are reverted.
3405
3465
3406 Modified files are saved with a .orig suffix before reverting.
3466 Modified files are saved with a .orig suffix before reverting.
3407 To disable these backups, use --no-backup.
3467 To disable these backups, use --no-backup.
3408
3468
3409 Returns 0 on success.
3469 Returns 0 on success.
3410 """
3470 """
3411
3471
3412 if opts.get("date"):
3472 if opts.get("date"):
3413 if opts.get("rev"):
3473 if opts.get("rev"):
3414 raise util.Abort(_("you can't specify a revision and a date"))
3474 raise util.Abort(_("you can't specify a revision and a date"))
3415 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3475 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3416
3476
3417 parent, p2 = repo.dirstate.parents()
3477 parent, p2 = repo.dirstate.parents()
3418 if not opts.get('rev') and p2 != nullid:
3478 if not opts.get('rev') and p2 != nullid:
3419 raise util.Abort(_('uncommitted merge - '
3479 raise util.Abort(_('uncommitted merge - '
3420 'use "hg update", see "hg help revert"'))
3480 'use "hg update", see "hg help revert"'))
3421
3481
3422 if not pats and not opts.get('all'):
3482 if not pats and not opts.get('all'):
3423 raise util.Abort(_('no files or directories specified; '
3483 raise util.Abort(_('no files or directories specified; '
3424 'use --all to revert the whole repo'))
3484 'use --all to revert the whole repo'))
3425
3485
3426 ctx = cmdutil.revsingle(repo, opts.get('rev'))
3486 ctx = cmdutil.revsingle(repo, opts.get('rev'))
3427 node = ctx.node()
3487 node = ctx.node()
3428 mf = ctx.manifest()
3488 mf = ctx.manifest()
3429 if node == parent:
3489 if node == parent:
3430 pmf = mf
3490 pmf = mf
3431 else:
3491 else:
3432 pmf = None
3492 pmf = None
3433
3493
3434 # need all matching names in dirstate and manifest of target rev,
3494 # need all matching names in dirstate and manifest of target rev,
3435 # so have to walk both. do not print errors if files exist in one
3495 # so have to walk both. do not print errors if files exist in one
3436 # but not other.
3496 # but not other.
3437
3497
3438 names = {}
3498 names = {}
3439
3499
3440 wlock = repo.wlock()
3500 wlock = repo.wlock()
3441 try:
3501 try:
3442 # walk dirstate.
3502 # walk dirstate.
3443
3503
3444 m = cmdutil.match(repo, pats, opts)
3504 m = cmdutil.match(repo, pats, opts)
3445 m.bad = lambda x, y: False
3505 m.bad = lambda x, y: False
3446 for abs in repo.walk(m):
3506 for abs in repo.walk(m):
3447 names[abs] = m.rel(abs), m.exact(abs)
3507 names[abs] = m.rel(abs), m.exact(abs)
3448
3508
3449 # walk target manifest.
3509 # walk target manifest.
3450
3510
3451 def badfn(path, msg):
3511 def badfn(path, msg):
3452 if path in names:
3512 if path in names:
3453 return
3513 return
3454 path_ = path + '/'
3514 path_ = path + '/'
3455 for f in names:
3515 for f in names:
3456 if f.startswith(path_):
3516 if f.startswith(path_):
3457 return
3517 return
3458 ui.warn("%s: %s\n" % (m.rel(path), msg))
3518 ui.warn("%s: %s\n" % (m.rel(path), msg))
3459
3519
3460 m = cmdutil.match(repo, pats, opts)
3520 m = cmdutil.match(repo, pats, opts)
3461 m.bad = badfn
3521 m.bad = badfn
3462 for abs in repo[node].walk(m):
3522 for abs in repo[node].walk(m):
3463 if abs not in names:
3523 if abs not in names:
3464 names[abs] = m.rel(abs), m.exact(abs)
3524 names[abs] = m.rel(abs), m.exact(abs)
3465
3525
3466 m = cmdutil.matchfiles(repo, names)
3526 m = cmdutil.matchfiles(repo, names)
3467 changes = repo.status(match=m)[:4]
3527 changes = repo.status(match=m)[:4]
3468 modified, added, removed, deleted = map(set, changes)
3528 modified, added, removed, deleted = map(set, changes)
3469
3529
3470 # if f is a rename, also revert the source
3530 # if f is a rename, also revert the source
3471 cwd = repo.getcwd()
3531 cwd = repo.getcwd()
3472 for f in added:
3532 for f in added:
3473 src = repo.dirstate.copied(f)
3533 src = repo.dirstate.copied(f)
3474 if src and src not in names and repo.dirstate[src] == 'r':
3534 if src and src not in names and repo.dirstate[src] == 'r':
3475 removed.add(src)
3535 removed.add(src)
3476 names[src] = (repo.pathto(src, cwd), True)
3536 names[src] = (repo.pathto(src, cwd), True)
3477
3537
3478 def removeforget(abs):
3538 def removeforget(abs):
3479 if repo.dirstate[abs] == 'a':
3539 if repo.dirstate[abs] == 'a':
3480 return _('forgetting %s\n')
3540 return _('forgetting %s\n')
3481 return _('removing %s\n')
3541 return _('removing %s\n')
3482
3542
3483 revert = ([], _('reverting %s\n'))
3543 revert = ([], _('reverting %s\n'))
3484 add = ([], _('adding %s\n'))
3544 add = ([], _('adding %s\n'))
3485 remove = ([], removeforget)
3545 remove = ([], removeforget)
3486 undelete = ([], _('undeleting %s\n'))
3546 undelete = ([], _('undeleting %s\n'))
3487
3547
3488 disptable = (
3548 disptable = (
3489 # dispatch table:
3549 # dispatch table:
3490 # file state
3550 # file state
3491 # action if in target manifest
3551 # action if in target manifest
3492 # action if not in target manifest
3552 # action if not in target manifest
3493 # make backup if in target manifest
3553 # make backup if in target manifest
3494 # make backup if not in target manifest
3554 # make backup if not in target manifest
3495 (modified, revert, remove, True, True),
3555 (modified, revert, remove, True, True),
3496 (added, revert, remove, True, False),
3556 (added, revert, remove, True, False),
3497 (removed, undelete, None, False, False),
3557 (removed, undelete, None, False, False),
3498 (deleted, revert, remove, False, False),
3558 (deleted, revert, remove, False, False),
3499 )
3559 )
3500
3560
3501 for abs, (rel, exact) in sorted(names.items()):
3561 for abs, (rel, exact) in sorted(names.items()):
3502 mfentry = mf.get(abs)
3562 mfentry = mf.get(abs)
3503 target = repo.wjoin(abs)
3563 target = repo.wjoin(abs)
3504 def handle(xlist, dobackup):
3564 def handle(xlist, dobackup):
3505 xlist[0].append(abs)
3565 xlist[0].append(abs)
3506 if (dobackup and not opts.get('no_backup') and
3566 if (dobackup and not opts.get('no_backup') and
3507 os.path.lexists(target)):
3567 os.path.lexists(target)):
3508 bakname = "%s.orig" % rel
3568 bakname = "%s.orig" % rel
3509 ui.note(_('saving current version of %s as %s\n') %
3569 ui.note(_('saving current version of %s as %s\n') %
3510 (rel, bakname))
3570 (rel, bakname))
3511 if not opts.get('dry_run'):
3571 if not opts.get('dry_run'):
3512 util.rename(target, bakname)
3572 util.rename(target, bakname)
3513 if ui.verbose or not exact:
3573 if ui.verbose or not exact:
3514 msg = xlist[1]
3574 msg = xlist[1]
3515 if not isinstance(msg, basestring):
3575 if not isinstance(msg, basestring):
3516 msg = msg(abs)
3576 msg = msg(abs)
3517 ui.status(msg % rel)
3577 ui.status(msg % rel)
3518 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3578 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3519 if abs not in table:
3579 if abs not in table:
3520 continue
3580 continue
3521 # file has changed in dirstate
3581 # file has changed in dirstate
3522 if mfentry:
3582 if mfentry:
3523 handle(hitlist, backuphit)
3583 handle(hitlist, backuphit)
3524 elif misslist is not None:
3584 elif misslist is not None:
3525 handle(misslist, backupmiss)
3585 handle(misslist, backupmiss)
3526 break
3586 break
3527 else:
3587 else:
3528 if abs not in repo.dirstate:
3588 if abs not in repo.dirstate:
3529 if mfentry:
3589 if mfentry:
3530 handle(add, True)
3590 handle(add, True)
3531 elif exact:
3591 elif exact:
3532 ui.warn(_('file not managed: %s\n') % rel)
3592 ui.warn(_('file not managed: %s\n') % rel)
3533 continue
3593 continue
3534 # file has not changed in dirstate
3594 # file has not changed in dirstate
3535 if node == parent:
3595 if node == parent:
3536 if exact:
3596 if exact:
3537 ui.warn(_('no changes needed to %s\n') % rel)
3597 ui.warn(_('no changes needed to %s\n') % rel)
3538 continue
3598 continue
3539 if pmf is None:
3599 if pmf is None:
3540 # only need parent manifest in this unlikely case,
3600 # only need parent manifest in this unlikely case,
3541 # so do not read by default
3601 # so do not read by default
3542 pmf = repo[parent].manifest()
3602 pmf = repo[parent].manifest()
3543 if abs in pmf:
3603 if abs in pmf:
3544 if mfentry:
3604 if mfentry:
3545 # if version of file is same in parent and target
3605 # if version of file is same in parent and target
3546 # manifests, do nothing
3606 # manifests, do nothing
3547 if (pmf[abs] != mfentry or
3607 if (pmf[abs] != mfentry or
3548 pmf.flags(abs) != mf.flags(abs)):
3608 pmf.flags(abs) != mf.flags(abs)):
3549 handle(revert, False)
3609 handle(revert, False)
3550 else:
3610 else:
3551 handle(remove, False)
3611 handle(remove, False)
3552
3612
3553 if not opts.get('dry_run'):
3613 if not opts.get('dry_run'):
3554 def checkout(f):
3614 def checkout(f):
3555 fc = ctx[f]
3615 fc = ctx[f]
3556 repo.wwrite(f, fc.data(), fc.flags())
3616 repo.wwrite(f, fc.data(), fc.flags())
3557
3617
3558 audit_path = scmutil.path_auditor(repo.root)
3618 audit_path = scmutil.path_auditor(repo.root)
3559 for f in remove[0]:
3619 for f in remove[0]:
3560 if repo.dirstate[f] == 'a':
3620 if repo.dirstate[f] == 'a':
3561 repo.dirstate.forget(f)
3621 repo.dirstate.forget(f)
3562 continue
3622 continue
3563 audit_path(f)
3623 audit_path(f)
3564 try:
3624 try:
3565 util.unlinkpath(repo.wjoin(f))
3625 util.unlinkpath(repo.wjoin(f))
3566 except OSError:
3626 except OSError:
3567 pass
3627 pass
3568 repo.dirstate.remove(f)
3628 repo.dirstate.remove(f)
3569
3629
3570 normal = None
3630 normal = None
3571 if node == parent:
3631 if node == parent:
3572 # We're reverting to our parent. If possible, we'd like status
3632 # We're reverting to our parent. If possible, we'd like status
3573 # to report the file as clean. We have to use normallookup for
3633 # to report the file as clean. We have to use normallookup for
3574 # merges to avoid losing information about merged/dirty files.
3634 # merges to avoid losing information about merged/dirty files.
3575 if p2 != nullid:
3635 if p2 != nullid:
3576 normal = repo.dirstate.normallookup
3636 normal = repo.dirstate.normallookup
3577 else:
3637 else:
3578 normal = repo.dirstate.normal
3638 normal = repo.dirstate.normal
3579 for f in revert[0]:
3639 for f in revert[0]:
3580 checkout(f)
3640 checkout(f)
3581 if normal:
3641 if normal:
3582 normal(f)
3642 normal(f)
3583
3643
3584 for f in add[0]:
3644 for f in add[0]:
3585 checkout(f)
3645 checkout(f)
3586 repo.dirstate.add(f)
3646 repo.dirstate.add(f)
3587
3647
3588 normal = repo.dirstate.normallookup
3648 normal = repo.dirstate.normallookup
3589 if node == parent and p2 == nullid:
3649 if node == parent and p2 == nullid:
3590 normal = repo.dirstate.normal
3650 normal = repo.dirstate.normal
3591 for f in undelete[0]:
3651 for f in undelete[0]:
3592 checkout(f)
3652 checkout(f)
3593 normal(f)
3653 normal(f)
3594
3654
3595 finally:
3655 finally:
3596 wlock.release()
3656 wlock.release()
3597
3657
3598 def rollback(ui, repo, **opts):
3658 def rollback(ui, repo, **opts):
3599 """roll back the last transaction (dangerous)
3659 """roll back the last transaction (dangerous)
3600
3660
3601 This command should be used with care. There is only one level of
3661 This command should be used with care. There is only one level of
3602 rollback, and there is no way to undo a rollback. It will also
3662 rollback, and there is no way to undo a rollback. It will also
3603 restore the dirstate at the time of the last transaction, losing
3663 restore the dirstate at the time of the last transaction, losing
3604 any dirstate changes since that time. This command does not alter
3664 any dirstate changes since that time. This command does not alter
3605 the working directory.
3665 the working directory.
3606
3666
3607 Transactions are used to encapsulate the effects of all commands
3667 Transactions are used to encapsulate the effects of all commands
3608 that create new changesets or propagate existing changesets into a
3668 that create new changesets or propagate existing changesets into a
3609 repository. For example, the following commands are transactional,
3669 repository. For example, the following commands are transactional,
3610 and their effects can be rolled back:
3670 and their effects can be rolled back:
3611
3671
3612 - commit
3672 - commit
3613 - import
3673 - import
3614 - pull
3674 - pull
3615 - push (with this repository as the destination)
3675 - push (with this repository as the destination)
3616 - unbundle
3676 - unbundle
3617
3677
3618 This command is not intended for use on public repositories. Once
3678 This command is not intended for use on public repositories. Once
3619 changes are visible for pull by other users, rolling a transaction
3679 changes are visible for pull by other users, rolling a transaction
3620 back locally is ineffective (someone else may already have pulled
3680 back locally is ineffective (someone else may already have pulled
3621 the changes). Furthermore, a race is possible with readers of the
3681 the changes). Furthermore, a race is possible with readers of the
3622 repository; for example an in-progress pull from the repository
3682 repository; for example an in-progress pull from the repository
3623 may fail if a rollback is performed.
3683 may fail if a rollback is performed.
3624
3684
3625 Returns 0 on success, 1 if no rollback data is available.
3685 Returns 0 on success, 1 if no rollback data is available.
3626 """
3686 """
3627 return repo.rollback(opts.get('dry_run'))
3687 return repo.rollback(opts.get('dry_run'))
3628
3688
3629 def root(ui, repo):
3689 def root(ui, repo):
3630 """print the root (top) of the current working directory
3690 """print the root (top) of the current working directory
3631
3691
3632 Print the root directory of the current repository.
3692 Print the root directory of the current repository.
3633
3693
3634 Returns 0 on success.
3694 Returns 0 on success.
3635 """
3695 """
3636 ui.write(repo.root + "\n")
3696 ui.write(repo.root + "\n")
3637
3697
3638 def serve(ui, repo, **opts):
3698 def serve(ui, repo, **opts):
3639 """start stand-alone webserver
3699 """start stand-alone webserver
3640
3700
3641 Start a local HTTP repository browser and pull server. You can use
3701 Start a local HTTP repository browser and pull server. You can use
3642 this for ad-hoc sharing and browsing of repositories. It is
3702 this for ad-hoc sharing and browsing of repositories. It is
3643 recommended to use a real web server to serve a repository for
3703 recommended to use a real web server to serve a repository for
3644 longer periods of time.
3704 longer periods of time.
3645
3705
3646 Please note that the server does not implement access control.
3706 Please note that the server does not implement access control.
3647 This means that, by default, anybody can read from the server and
3707 This means that, by default, anybody can read from the server and
3648 nobody can write to it by default. Set the ``web.allow_push``
3708 nobody can write to it by default. Set the ``web.allow_push``
3649 option to ``*`` to allow everybody to push to the server. You
3709 option to ``*`` to allow everybody to push to the server. You
3650 should use a real web server if you need to authenticate users.
3710 should use a real web server if you need to authenticate users.
3651
3711
3652 By default, the server logs accesses to stdout and errors to
3712 By default, the server logs accesses to stdout and errors to
3653 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3713 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3654 files.
3714 files.
3655
3715
3656 To have the server choose a free port number to listen on, specify
3716 To have the server choose a free port number to listen on, specify
3657 a port number of 0; in this case, the server will print the port
3717 a port number of 0; in this case, the server will print the port
3658 number it uses.
3718 number it uses.
3659
3719
3660 Returns 0 on success.
3720 Returns 0 on success.
3661 """
3721 """
3662
3722
3663 if opts["stdio"]:
3723 if opts["stdio"]:
3664 if repo is None:
3724 if repo is None:
3665 raise error.RepoError(_("There is no Mercurial repository here"
3725 raise error.RepoError(_("There is no Mercurial repository here"
3666 " (.hg not found)"))
3726 " (.hg not found)"))
3667 s = sshserver.sshserver(ui, repo)
3727 s = sshserver.sshserver(ui, repo)
3668 s.serve_forever()
3728 s.serve_forever()
3669
3729
3670 # this way we can check if something was given in the command-line
3730 # this way we can check if something was given in the command-line
3671 if opts.get('port'):
3731 if opts.get('port'):
3672 opts['port'] = util.getport(opts.get('port'))
3732 opts['port'] = util.getport(opts.get('port'))
3673
3733
3674 baseui = repo and repo.baseui or ui
3734 baseui = repo and repo.baseui or ui
3675 optlist = ("name templates style address port prefix ipv6"
3735 optlist = ("name templates style address port prefix ipv6"
3676 " accesslog errorlog certificate encoding")
3736 " accesslog errorlog certificate encoding")
3677 for o in optlist.split():
3737 for o in optlist.split():
3678 val = opts.get(o, '')
3738 val = opts.get(o, '')
3679 if val in (None, ''): # should check against default options instead
3739 if val in (None, ''): # should check against default options instead
3680 continue
3740 continue
3681 baseui.setconfig("web", o, val)
3741 baseui.setconfig("web", o, val)
3682 if repo and repo.ui != baseui:
3742 if repo and repo.ui != baseui:
3683 repo.ui.setconfig("web", o, val)
3743 repo.ui.setconfig("web", o, val)
3684
3744
3685 o = opts.get('web_conf') or opts.get('webdir_conf')
3745 o = opts.get('web_conf') or opts.get('webdir_conf')
3686 if not o:
3746 if not o:
3687 if not repo:
3747 if not repo:
3688 raise error.RepoError(_("There is no Mercurial repository"
3748 raise error.RepoError(_("There is no Mercurial repository"
3689 " here (.hg not found)"))
3749 " here (.hg not found)"))
3690 o = repo.root
3750 o = repo.root
3691
3751
3692 app = hgweb.hgweb(o, baseui=ui)
3752 app = hgweb.hgweb(o, baseui=ui)
3693
3753
3694 class service(object):
3754 class service(object):
3695 def init(self):
3755 def init(self):
3696 util.set_signal_handler()
3756 util.set_signal_handler()
3697 self.httpd = hgweb.server.create_server(ui, app)
3757 self.httpd = hgweb.server.create_server(ui, app)
3698
3758
3699 if opts['port'] and not ui.verbose:
3759 if opts['port'] and not ui.verbose:
3700 return
3760 return
3701
3761
3702 if self.httpd.prefix:
3762 if self.httpd.prefix:
3703 prefix = self.httpd.prefix.strip('/') + '/'
3763 prefix = self.httpd.prefix.strip('/') + '/'
3704 else:
3764 else:
3705 prefix = ''
3765 prefix = ''
3706
3766
3707 port = ':%d' % self.httpd.port
3767 port = ':%d' % self.httpd.port
3708 if port == ':80':
3768 if port == ':80':
3709 port = ''
3769 port = ''
3710
3770
3711 bindaddr = self.httpd.addr
3771 bindaddr = self.httpd.addr
3712 if bindaddr == '0.0.0.0':
3772 if bindaddr == '0.0.0.0':
3713 bindaddr = '*'
3773 bindaddr = '*'
3714 elif ':' in bindaddr: # IPv6
3774 elif ':' in bindaddr: # IPv6
3715 bindaddr = '[%s]' % bindaddr
3775 bindaddr = '[%s]' % bindaddr
3716
3776
3717 fqaddr = self.httpd.fqaddr
3777 fqaddr = self.httpd.fqaddr
3718 if ':' in fqaddr:
3778 if ':' in fqaddr:
3719 fqaddr = '[%s]' % fqaddr
3779 fqaddr = '[%s]' % fqaddr
3720 if opts['port']:
3780 if opts['port']:
3721 write = ui.status
3781 write = ui.status
3722 else:
3782 else:
3723 write = ui.write
3783 write = ui.write
3724 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3784 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3725 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3785 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3726
3786
3727 def run(self):
3787 def run(self):
3728 self.httpd.serve_forever()
3788 self.httpd.serve_forever()
3729
3789
3730 service = service()
3790 service = service()
3731
3791
3732 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3792 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3733
3793
3734 def status(ui, repo, *pats, **opts):
3794 def status(ui, repo, *pats, **opts):
3735 """show changed files in the working directory
3795 """show changed files in the working directory
3736
3796
3737 Show status of files in the repository. If names are given, only
3797 Show status of files in the repository. If names are given, only
3738 files that match are shown. Files that are clean or ignored or
3798 files that match are shown. Files that are clean or ignored or
3739 the source of a copy/move operation, are not listed unless
3799 the source of a copy/move operation, are not listed unless
3740 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3800 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3741 Unless options described with "show only ..." are given, the
3801 Unless options described with "show only ..." are given, the
3742 options -mardu are used.
3802 options -mardu are used.
3743
3803
3744 Option -q/--quiet hides untracked (unknown and ignored) files
3804 Option -q/--quiet hides untracked (unknown and ignored) files
3745 unless explicitly requested with -u/--unknown or -i/--ignored.
3805 unless explicitly requested with -u/--unknown or -i/--ignored.
3746
3806
3747 .. note::
3807 .. note::
3748 status may appear to disagree with diff if permissions have
3808 status may appear to disagree with diff if permissions have
3749 changed or a merge has occurred. The standard diff format does
3809 changed or a merge has occurred. The standard diff format does
3750 not report permission changes and diff only reports changes
3810 not report permission changes and diff only reports changes
3751 relative to one merge parent.
3811 relative to one merge parent.
3752
3812
3753 If one revision is given, it is used as the base revision.
3813 If one revision is given, it is used as the base revision.
3754 If two revisions are given, the differences between them are
3814 If two revisions are given, the differences between them are
3755 shown. The --change option can also be used as a shortcut to list
3815 shown. The --change option can also be used as a shortcut to list
3756 the changed files of a revision from its first parent.
3816 the changed files of a revision from its first parent.
3757
3817
3758 The codes used to show the status of files are::
3818 The codes used to show the status of files are::
3759
3819
3760 M = modified
3820 M = modified
3761 A = added
3821 A = added
3762 R = removed
3822 R = removed
3763 C = clean
3823 C = clean
3764 ! = missing (deleted by non-hg command, but still tracked)
3824 ! = missing (deleted by non-hg command, but still tracked)
3765 ? = not tracked
3825 ? = not tracked
3766 I = ignored
3826 I = ignored
3767 = origin of the previous file listed as A (added)
3827 = origin of the previous file listed as A (added)
3768
3828
3769 Returns 0 on success.
3829 Returns 0 on success.
3770 """
3830 """
3771
3831
3772 revs = opts.get('rev')
3832 revs = opts.get('rev')
3773 change = opts.get('change')
3833 change = opts.get('change')
3774
3834
3775 if revs and change:
3835 if revs and change:
3776 msg = _('cannot specify --rev and --change at the same time')
3836 msg = _('cannot specify --rev and --change at the same time')
3777 raise util.Abort(msg)
3837 raise util.Abort(msg)
3778 elif change:
3838 elif change:
3779 node2 = repo.lookup(change)
3839 node2 = repo.lookup(change)
3780 node1 = repo[node2].p1().node()
3840 node1 = repo[node2].p1().node()
3781 else:
3841 else:
3782 node1, node2 = cmdutil.revpair(repo, revs)
3842 node1, node2 = cmdutil.revpair(repo, revs)
3783
3843
3784 cwd = (pats and repo.getcwd()) or ''
3844 cwd = (pats and repo.getcwd()) or ''
3785 end = opts.get('print0') and '\0' or '\n'
3845 end = opts.get('print0') and '\0' or '\n'
3786 copy = {}
3846 copy = {}
3787 states = 'modified added removed deleted unknown ignored clean'.split()
3847 states = 'modified added removed deleted unknown ignored clean'.split()
3788 show = [k for k in states if opts.get(k)]
3848 show = [k for k in states if opts.get(k)]
3789 if opts.get('all'):
3849 if opts.get('all'):
3790 show += ui.quiet and (states[:4] + ['clean']) or states
3850 show += ui.quiet and (states[:4] + ['clean']) or states
3791 if not show:
3851 if not show:
3792 show = ui.quiet and states[:4] or states[:5]
3852 show = ui.quiet and states[:4] or states[:5]
3793
3853
3794 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3854 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3795 'ignored' in show, 'clean' in show, 'unknown' in show,
3855 'ignored' in show, 'clean' in show, 'unknown' in show,
3796 opts.get('subrepos'))
3856 opts.get('subrepos'))
3797 changestates = zip(states, 'MAR!?IC', stat)
3857 changestates = zip(states, 'MAR!?IC', stat)
3798
3858
3799 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3859 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3800 ctxn = repo[nullid]
3860 ctxn = repo[nullid]
3801 ctx1 = repo[node1]
3861 ctx1 = repo[node1]
3802 ctx2 = repo[node2]
3862 ctx2 = repo[node2]
3803 added = stat[1]
3863 added = stat[1]
3804 if node2 is None:
3864 if node2 is None:
3805 added = stat[0] + stat[1] # merged?
3865 added = stat[0] + stat[1] # merged?
3806
3866
3807 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3867 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3808 if k in added:
3868 if k in added:
3809 copy[k] = v
3869 copy[k] = v
3810 elif v in added:
3870 elif v in added:
3811 copy[v] = k
3871 copy[v] = k
3812
3872
3813 for state, char, files in changestates:
3873 for state, char, files in changestates:
3814 if state in show:
3874 if state in show:
3815 format = "%s %%s%s" % (char, end)
3875 format = "%s %%s%s" % (char, end)
3816 if opts.get('no_status'):
3876 if opts.get('no_status'):
3817 format = "%%s%s" % end
3877 format = "%%s%s" % end
3818
3878
3819 for f in files:
3879 for f in files:
3820 ui.write(format % repo.pathto(f, cwd),
3880 ui.write(format % repo.pathto(f, cwd),
3821 label='status.' + state)
3881 label='status.' + state)
3822 if f in copy:
3882 if f in copy:
3823 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3883 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3824 label='status.copied')
3884 label='status.copied')
3825
3885
3826 def summary(ui, repo, **opts):
3886 def summary(ui, repo, **opts):
3827 """summarize working directory state
3887 """summarize working directory state
3828
3888
3829 This generates a brief summary of the working directory state,
3889 This generates a brief summary of the working directory state,
3830 including parents, branch, commit status, and available updates.
3890 including parents, branch, commit status, and available updates.
3831
3891
3832 With the --remote option, this will check the default paths for
3892 With the --remote option, this will check the default paths for
3833 incoming and outgoing changes. This can be time-consuming.
3893 incoming and outgoing changes. This can be time-consuming.
3834
3894
3835 Returns 0 on success.
3895 Returns 0 on success.
3836 """
3896 """
3837
3897
3838 ctx = repo[None]
3898 ctx = repo[None]
3839 parents = ctx.parents()
3899 parents = ctx.parents()
3840 pnode = parents[0].node()
3900 pnode = parents[0].node()
3841
3901
3842 for p in parents:
3902 for p in parents:
3843 # label with log.changeset (instead of log.parent) since this
3903 # label with log.changeset (instead of log.parent) since this
3844 # shows a working directory parent *changeset*:
3904 # shows a working directory parent *changeset*:
3845 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3905 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3846 label='log.changeset')
3906 label='log.changeset')
3847 ui.write(' '.join(p.tags()), label='log.tag')
3907 ui.write(' '.join(p.tags()), label='log.tag')
3848 if p.bookmarks():
3908 if p.bookmarks():
3849 ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
3909 ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
3850 if p.rev() == -1:
3910 if p.rev() == -1:
3851 if not len(repo):
3911 if not len(repo):
3852 ui.write(_(' (empty repository)'))
3912 ui.write(_(' (empty repository)'))
3853 else:
3913 else:
3854 ui.write(_(' (no revision checked out)'))
3914 ui.write(_(' (no revision checked out)'))
3855 ui.write('\n')
3915 ui.write('\n')
3856 if p.description():
3916 if p.description():
3857 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3917 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3858 label='log.summary')
3918 label='log.summary')
3859
3919
3860 branch = ctx.branch()
3920 branch = ctx.branch()
3861 bheads = repo.branchheads(branch)
3921 bheads = repo.branchheads(branch)
3862 m = _('branch: %s\n') % branch
3922 m = _('branch: %s\n') % branch
3863 if branch != 'default':
3923 if branch != 'default':
3864 ui.write(m, label='log.branch')
3924 ui.write(m, label='log.branch')
3865 else:
3925 else:
3866 ui.status(m, label='log.branch')
3926 ui.status(m, label='log.branch')
3867
3927
3868 st = list(repo.status(unknown=True))[:6]
3928 st = list(repo.status(unknown=True))[:6]
3869
3929
3870 c = repo.dirstate.copies()
3930 c = repo.dirstate.copies()
3871 copied, renamed = [], []
3931 copied, renamed = [], []
3872 for d, s in c.iteritems():
3932 for d, s in c.iteritems():
3873 if s in st[2]:
3933 if s in st[2]:
3874 st[2].remove(s)
3934 st[2].remove(s)
3875 renamed.append(d)
3935 renamed.append(d)
3876 else:
3936 else:
3877 copied.append(d)
3937 copied.append(d)
3878 if d in st[1]:
3938 if d in st[1]:
3879 st[1].remove(d)
3939 st[1].remove(d)
3880 st.insert(3, renamed)
3940 st.insert(3, renamed)
3881 st.insert(4, copied)
3941 st.insert(4, copied)
3882
3942
3883 ms = mergemod.mergestate(repo)
3943 ms = mergemod.mergestate(repo)
3884 st.append([f for f in ms if ms[f] == 'u'])
3944 st.append([f for f in ms if ms[f] == 'u'])
3885
3945
3886 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3946 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3887 st.append(subs)
3947 st.append(subs)
3888
3948
3889 labels = [ui.label(_('%d modified'), 'status.modified'),
3949 labels = [ui.label(_('%d modified'), 'status.modified'),
3890 ui.label(_('%d added'), 'status.added'),
3950 ui.label(_('%d added'), 'status.added'),
3891 ui.label(_('%d removed'), 'status.removed'),
3951 ui.label(_('%d removed'), 'status.removed'),
3892 ui.label(_('%d renamed'), 'status.copied'),
3952 ui.label(_('%d renamed'), 'status.copied'),
3893 ui.label(_('%d copied'), 'status.copied'),
3953 ui.label(_('%d copied'), 'status.copied'),
3894 ui.label(_('%d deleted'), 'status.deleted'),
3954 ui.label(_('%d deleted'), 'status.deleted'),
3895 ui.label(_('%d unknown'), 'status.unknown'),
3955 ui.label(_('%d unknown'), 'status.unknown'),
3896 ui.label(_('%d ignored'), 'status.ignored'),
3956 ui.label(_('%d ignored'), 'status.ignored'),
3897 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3957 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3898 ui.label(_('%d subrepos'), 'status.modified')]
3958 ui.label(_('%d subrepos'), 'status.modified')]
3899 t = []
3959 t = []
3900 for s, l in zip(st, labels):
3960 for s, l in zip(st, labels):
3901 if s:
3961 if s:
3902 t.append(l % len(s))
3962 t.append(l % len(s))
3903
3963
3904 t = ', '.join(t)
3964 t = ', '.join(t)
3905 cleanworkdir = False
3965 cleanworkdir = False
3906
3966
3907 if len(parents) > 1:
3967 if len(parents) > 1:
3908 t += _(' (merge)')
3968 t += _(' (merge)')
3909 elif branch != parents[0].branch():
3969 elif branch != parents[0].branch():
3910 t += _(' (new branch)')
3970 t += _(' (new branch)')
3911 elif (parents[0].extra().get('close') and
3971 elif (parents[0].extra().get('close') and
3912 pnode in repo.branchheads(branch, closed=True)):
3972 pnode in repo.branchheads(branch, closed=True)):
3913 t += _(' (head closed)')
3973 t += _(' (head closed)')
3914 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3974 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3915 t += _(' (clean)')
3975 t += _(' (clean)')
3916 cleanworkdir = True
3976 cleanworkdir = True
3917 elif pnode not in bheads:
3977 elif pnode not in bheads:
3918 t += _(' (new branch head)')
3978 t += _(' (new branch head)')
3919
3979
3920 if cleanworkdir:
3980 if cleanworkdir:
3921 ui.status(_('commit: %s\n') % t.strip())
3981 ui.status(_('commit: %s\n') % t.strip())
3922 else:
3982 else:
3923 ui.write(_('commit: %s\n') % t.strip())
3983 ui.write(_('commit: %s\n') % t.strip())
3924
3984
3925 # all ancestors of branch heads - all ancestors of parent = new csets
3985 # all ancestors of branch heads - all ancestors of parent = new csets
3926 new = [0] * len(repo)
3986 new = [0] * len(repo)
3927 cl = repo.changelog
3987 cl = repo.changelog
3928 for a in [cl.rev(n) for n in bheads]:
3988 for a in [cl.rev(n) for n in bheads]:
3929 new[a] = 1
3989 new[a] = 1
3930 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3990 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3931 new[a] = 1
3991 new[a] = 1
3932 for a in [p.rev() for p in parents]:
3992 for a in [p.rev() for p in parents]:
3933 if a >= 0:
3993 if a >= 0:
3934 new[a] = 0
3994 new[a] = 0
3935 for a in cl.ancestors(*[p.rev() for p in parents]):
3995 for a in cl.ancestors(*[p.rev() for p in parents]):
3936 new[a] = 0
3996 new[a] = 0
3937 new = sum(new)
3997 new = sum(new)
3938
3998
3939 if new == 0:
3999 if new == 0:
3940 ui.status(_('update: (current)\n'))
4000 ui.status(_('update: (current)\n'))
3941 elif pnode not in bheads:
4001 elif pnode not in bheads:
3942 ui.write(_('update: %d new changesets (update)\n') % new)
4002 ui.write(_('update: %d new changesets (update)\n') % new)
3943 else:
4003 else:
3944 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4004 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3945 (new, len(bheads)))
4005 (new, len(bheads)))
3946
4006
3947 if opts.get('remote'):
4007 if opts.get('remote'):
3948 t = []
4008 t = []
3949 source, branches = hg.parseurl(ui.expandpath('default'))
4009 source, branches = hg.parseurl(ui.expandpath('default'))
3950 other = hg.repository(hg.remoteui(repo, {}), source)
4010 other = hg.repository(hg.remoteui(repo, {}), source)
3951 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4011 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3952 ui.debug('comparing with %s\n' % util.hidepassword(source))
4012 ui.debug('comparing with %s\n' % util.hidepassword(source))
3953 repo.ui.pushbuffer()
4013 repo.ui.pushbuffer()
3954 common, incoming, rheads = discovery.findcommonincoming(repo, other)
4014 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3955 repo.ui.popbuffer()
4015 repo.ui.popbuffer()
3956 if incoming:
4016 if incoming:
3957 t.append(_('1 or more incoming'))
4017 t.append(_('1 or more incoming'))
3958
4018
3959 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
4019 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3960 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
4020 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3961 other = hg.repository(hg.remoteui(repo, {}), dest)
4021 other = hg.repository(hg.remoteui(repo, {}), dest)
3962 ui.debug('comparing with %s\n' % util.hidepassword(dest))
4022 ui.debug('comparing with %s\n' % util.hidepassword(dest))
3963 repo.ui.pushbuffer()
4023 repo.ui.pushbuffer()
3964 common, _anyinc, _heads = discovery.findcommonincoming(repo, other)
4024 common, _anyinc, _heads = discovery.findcommonincoming(repo, other)
3965 repo.ui.popbuffer()
4025 repo.ui.popbuffer()
3966 o = repo.changelog.findmissing(common=common)
4026 o = repo.changelog.findmissing(common=common)
3967 if o:
4027 if o:
3968 t.append(_('%d outgoing') % len(o))
4028 t.append(_('%d outgoing') % len(o))
3969 if 'bookmarks' in other.listkeys('namespaces'):
4029 if 'bookmarks' in other.listkeys('namespaces'):
3970 lmarks = repo.listkeys('bookmarks')
4030 lmarks = repo.listkeys('bookmarks')
3971 rmarks = other.listkeys('bookmarks')
4031 rmarks = other.listkeys('bookmarks')
3972 diff = set(rmarks) - set(lmarks)
4032 diff = set(rmarks) - set(lmarks)
3973 if len(diff) > 0:
4033 if len(diff) > 0:
3974 t.append(_('%d incoming bookmarks') % len(diff))
4034 t.append(_('%d incoming bookmarks') % len(diff))
3975 diff = set(lmarks) - set(rmarks)
4035 diff = set(lmarks) - set(rmarks)
3976 if len(diff) > 0:
4036 if len(diff) > 0:
3977 t.append(_('%d outgoing bookmarks') % len(diff))
4037 t.append(_('%d outgoing bookmarks') % len(diff))
3978
4038
3979 if t:
4039 if t:
3980 ui.write(_('remote: %s\n') % (', '.join(t)))
4040 ui.write(_('remote: %s\n') % (', '.join(t)))
3981 else:
4041 else:
3982 ui.status(_('remote: (synced)\n'))
4042 ui.status(_('remote: (synced)\n'))
3983
4043
3984 def tag(ui, repo, name1, *names, **opts):
4044 def tag(ui, repo, name1, *names, **opts):
3985 """add one or more tags for the current or given revision
4045 """add one or more tags for the current or given revision
3986
4046
3987 Name a particular revision using <name>.
4047 Name a particular revision using <name>.
3988
4048
3989 Tags are used to name particular revisions of the repository and are
4049 Tags are used to name particular revisions of the repository and are
3990 very useful to compare different revisions, to go back to significant
4050 very useful to compare different revisions, to go back to significant
3991 earlier versions or to mark branch points as releases, etc. Changing
4051 earlier versions or to mark branch points as releases, etc. Changing
3992 an existing tag is normally disallowed; use -f/--force to override.
4052 an existing tag is normally disallowed; use -f/--force to override.
3993
4053
3994 If no revision is given, the parent of the working directory is
4054 If no revision is given, the parent of the working directory is
3995 used, or tip if no revision is checked out.
4055 used, or tip if no revision is checked out.
3996
4056
3997 To facilitate version control, distribution, and merging of tags,
4057 To facilitate version control, distribution, and merging of tags,
3998 they are stored as a file named ".hgtags" which is managed similarly
4058 they are stored as a file named ".hgtags" which is managed similarly
3999 to other project files and can be hand-edited if necessary. This
4059 to other project files and can be hand-edited if necessary. This
4000 also means that tagging creates a new commit. The file
4060 also means that tagging creates a new commit. The file
4001 ".hg/localtags" is used for local tags (not shared among
4061 ".hg/localtags" is used for local tags (not shared among
4002 repositories).
4062 repositories).
4003
4063
4004 Tag commits are usually made at the head of a branch. If the parent
4064 Tag commits are usually made at the head of a branch. If the parent
4005 of the working directory is not a branch head, :hg:`tag` aborts; use
4065 of the working directory is not a branch head, :hg:`tag` aborts; use
4006 -f/--force to force the tag commit to be based on a non-head
4066 -f/--force to force the tag commit to be based on a non-head
4007 changeset.
4067 changeset.
4008
4068
4009 See :hg:`help dates` for a list of formats valid for -d/--date.
4069 See :hg:`help dates` for a list of formats valid for -d/--date.
4010
4070
4011 Since tag names have priority over branch names during revision
4071 Since tag names have priority over branch names during revision
4012 lookup, using an existing branch name as a tag name is discouraged.
4072 lookup, using an existing branch name as a tag name is discouraged.
4013
4073
4014 Returns 0 on success.
4074 Returns 0 on success.
4015 """
4075 """
4016
4076
4017 rev_ = "."
4077 rev_ = "."
4018 names = [t.strip() for t in (name1,) + names]
4078 names = [t.strip() for t in (name1,) + names]
4019 if len(names) != len(set(names)):
4079 if len(names) != len(set(names)):
4020 raise util.Abort(_('tag names must be unique'))
4080 raise util.Abort(_('tag names must be unique'))
4021 for n in names:
4081 for n in names:
4022 if n in ['tip', '.', 'null']:
4082 if n in ['tip', '.', 'null']:
4023 raise util.Abort(_('the name \'%s\' is reserved') % n)
4083 raise util.Abort(_('the name \'%s\' is reserved') % n)
4024 if not n:
4084 if not n:
4025 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
4085 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
4026 if opts.get('rev') and opts.get('remove'):
4086 if opts.get('rev') and opts.get('remove'):
4027 raise util.Abort(_("--rev and --remove are incompatible"))
4087 raise util.Abort(_("--rev and --remove are incompatible"))
4028 if opts.get('rev'):
4088 if opts.get('rev'):
4029 rev_ = opts['rev']
4089 rev_ = opts['rev']
4030 message = opts.get('message')
4090 message = opts.get('message')
4031 if opts.get('remove'):
4091 if opts.get('remove'):
4032 expectedtype = opts.get('local') and 'local' or 'global'
4092 expectedtype = opts.get('local') and 'local' or 'global'
4033 for n in names:
4093 for n in names:
4034 if not repo.tagtype(n):
4094 if not repo.tagtype(n):
4035 raise util.Abort(_('tag \'%s\' does not exist') % n)
4095 raise util.Abort(_('tag \'%s\' does not exist') % n)
4036 if repo.tagtype(n) != expectedtype:
4096 if repo.tagtype(n) != expectedtype:
4037 if expectedtype == 'global':
4097 if expectedtype == 'global':
4038 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
4098 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
4039 else:
4099 else:
4040 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
4100 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
4041 rev_ = nullid
4101 rev_ = nullid
4042 if not message:
4102 if not message:
4043 # we don't translate commit messages
4103 # we don't translate commit messages
4044 message = 'Removed tag %s' % ', '.join(names)
4104 message = 'Removed tag %s' % ', '.join(names)
4045 elif not opts.get('force'):
4105 elif not opts.get('force'):
4046 for n in names:
4106 for n in names:
4047 if n in repo.tags():
4107 if n in repo.tags():
4048 raise util.Abort(_('tag \'%s\' already exists '
4108 raise util.Abort(_('tag \'%s\' already exists '
4049 '(use -f to force)') % n)
4109 '(use -f to force)') % n)
4050 if not opts.get('local'):
4110 if not opts.get('local'):
4051 p1, p2 = repo.dirstate.parents()
4111 p1, p2 = repo.dirstate.parents()
4052 if p2 != nullid:
4112 if p2 != nullid:
4053 raise util.Abort(_('uncommitted merge'))
4113 raise util.Abort(_('uncommitted merge'))
4054 bheads = repo.branchheads()
4114 bheads = repo.branchheads()
4055 if not opts.get('force') and bheads and p1 not in bheads:
4115 if not opts.get('force') and bheads and p1 not in bheads:
4056 raise util.Abort(_('not at a branch head (use -f to force)'))
4116 raise util.Abort(_('not at a branch head (use -f to force)'))
4057 r = cmdutil.revsingle(repo, rev_).node()
4117 r = cmdutil.revsingle(repo, rev_).node()
4058
4118
4059 if not message:
4119 if not message:
4060 # we don't translate commit messages
4120 # we don't translate commit messages
4061 message = ('Added tag %s for changeset %s' %
4121 message = ('Added tag %s for changeset %s' %
4062 (', '.join(names), short(r)))
4122 (', '.join(names), short(r)))
4063
4123
4064 date = opts.get('date')
4124 date = opts.get('date')
4065 if date:
4125 if date:
4066 date = util.parsedate(date)
4126 date = util.parsedate(date)
4067
4127
4068 if opts.get('edit'):
4128 if opts.get('edit'):
4069 message = ui.edit(message, ui.username())
4129 message = ui.edit(message, ui.username())
4070
4130
4071 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
4131 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
4072
4132
4073 def tags(ui, repo):
4133 def tags(ui, repo):
4074 """list repository tags
4134 """list repository tags
4075
4135
4076 This lists both regular and local tags. When the -v/--verbose
4136 This lists both regular and local tags. When the -v/--verbose
4077 switch is used, a third column "local" is printed for local tags.
4137 switch is used, a third column "local" is printed for local tags.
4078
4138
4079 Returns 0 on success.
4139 Returns 0 on success.
4080 """
4140 """
4081
4141
4082 hexfunc = ui.debugflag and hex or short
4142 hexfunc = ui.debugflag and hex or short
4083 tagtype = ""
4143 tagtype = ""
4084
4144
4085 for t, n in reversed(repo.tagslist()):
4145 for t, n in reversed(repo.tagslist()):
4086 if ui.quiet:
4146 if ui.quiet:
4087 ui.write("%s\n" % t)
4147 ui.write("%s\n" % t)
4088 continue
4148 continue
4089
4149
4090 hn = hexfunc(n)
4150 hn = hexfunc(n)
4091 r = "%5d:%s" % (repo.changelog.rev(n), hn)
4151 r = "%5d:%s" % (repo.changelog.rev(n), hn)
4092 spaces = " " * (30 - encoding.colwidth(t))
4152 spaces = " " * (30 - encoding.colwidth(t))
4093
4153
4094 if ui.verbose:
4154 if ui.verbose:
4095 if repo.tagtype(t) == 'local':
4155 if repo.tagtype(t) == 'local':
4096 tagtype = " local"
4156 tagtype = " local"
4097 else:
4157 else:
4098 tagtype = ""
4158 tagtype = ""
4099 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
4159 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
4100
4160
4101 def tip(ui, repo, **opts):
4161 def tip(ui, repo, **opts):
4102 """show the tip revision
4162 """show the tip revision
4103
4163
4104 The tip revision (usually just called the tip) is the changeset
4164 The tip revision (usually just called the tip) is the changeset
4105 most recently added to the repository (and therefore the most
4165 most recently added to the repository (and therefore the most
4106 recently changed head).
4166 recently changed head).
4107
4167
4108 If you have just made a commit, that commit will be the tip. If
4168 If you have just made a commit, that commit will be the tip. If
4109 you have just pulled changes from another repository, the tip of
4169 you have just pulled changes from another repository, the tip of
4110 that repository becomes the current tip. The "tip" tag is special
4170 that repository becomes the current tip. The "tip" tag is special
4111 and cannot be renamed or assigned to a different changeset.
4171 and cannot be renamed or assigned to a different changeset.
4112
4172
4113 Returns 0 on success.
4173 Returns 0 on success.
4114 """
4174 """
4115 displayer = cmdutil.show_changeset(ui, repo, opts)
4175 displayer = cmdutil.show_changeset(ui, repo, opts)
4116 displayer.show(repo[len(repo) - 1])
4176 displayer.show(repo[len(repo) - 1])
4117 displayer.close()
4177 displayer.close()
4118
4178
4119 def unbundle(ui, repo, fname1, *fnames, **opts):
4179 def unbundle(ui, repo, fname1, *fnames, **opts):
4120 """apply one or more changegroup files
4180 """apply one or more changegroup files
4121
4181
4122 Apply one or more compressed changegroup files generated by the
4182 Apply one or more compressed changegroup files generated by the
4123 bundle command.
4183 bundle command.
4124
4184
4125 Returns 0 on success, 1 if an update has unresolved files.
4185 Returns 0 on success, 1 if an update has unresolved files.
4126 """
4186 """
4127 fnames = (fname1,) + fnames
4187 fnames = (fname1,) + fnames
4128
4188
4129 lock = repo.lock()
4189 lock = repo.lock()
4130 wc = repo['.']
4190 wc = repo['.']
4131 try:
4191 try:
4132 for fname in fnames:
4192 for fname in fnames:
4133 f = url.open(ui, fname)
4193 f = url.open(ui, fname)
4134 gen = changegroup.readbundle(f, fname)
4194 gen = changegroup.readbundle(f, fname)
4135 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
4195 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
4136 lock=lock)
4196 lock=lock)
4137 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
4197 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
4138 finally:
4198 finally:
4139 lock.release()
4199 lock.release()
4140 return postincoming(ui, repo, modheads, opts.get('update'), None)
4200 return postincoming(ui, repo, modheads, opts.get('update'), None)
4141
4201
4142 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
4202 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
4143 """update working directory (or switch revisions)
4203 """update working directory (or switch revisions)
4144
4204
4145 Update the repository's working directory to the specified
4205 Update the repository's working directory to the specified
4146 changeset. If no changeset is specified, update to the tip of the
4206 changeset. If no changeset is specified, update to the tip of the
4147 current named branch.
4207 current named branch.
4148
4208
4149 If the changeset is not a descendant of the working directory's
4209 If the changeset is not a descendant of the working directory's
4150 parent, the update is aborted. With the -c/--check option, the
4210 parent, the update is aborted. With the -c/--check option, the
4151 working directory is checked for uncommitted changes; if none are
4211 working directory is checked for uncommitted changes; if none are
4152 found, the working directory is updated to the specified
4212 found, the working directory is updated to the specified
4153 changeset.
4213 changeset.
4154
4214
4155 The following rules apply when the working directory contains
4215 The following rules apply when the working directory contains
4156 uncommitted changes:
4216 uncommitted changes:
4157
4217
4158 1. If neither -c/--check nor -C/--clean is specified, and if
4218 1. If neither -c/--check nor -C/--clean is specified, and if
4159 the requested changeset is an ancestor or descendant of
4219 the requested changeset is an ancestor or descendant of
4160 the working directory's parent, the uncommitted changes
4220 the working directory's parent, the uncommitted changes
4161 are merged into the requested changeset and the merged
4221 are merged into the requested changeset and the merged
4162 result is left uncommitted. If the requested changeset is
4222 result is left uncommitted. If the requested changeset is
4163 not an ancestor or descendant (that is, it is on another
4223 not an ancestor or descendant (that is, it is on another
4164 branch), the update is aborted and the uncommitted changes
4224 branch), the update is aborted and the uncommitted changes
4165 are preserved.
4225 are preserved.
4166
4226
4167 2. With the -c/--check option, the update is aborted and the
4227 2. With the -c/--check option, the update is aborted and the
4168 uncommitted changes are preserved.
4228 uncommitted changes are preserved.
4169
4229
4170 3. With the -C/--clean option, uncommitted changes are discarded and
4230 3. With the -C/--clean option, uncommitted changes are discarded and
4171 the working directory is updated to the requested changeset.
4231 the working directory is updated to the requested changeset.
4172
4232
4173 Use null as the changeset to remove the working directory (like
4233 Use null as the changeset to remove the working directory (like
4174 :hg:`clone -U`).
4234 :hg:`clone -U`).
4175
4235
4176 If you want to update just one file to an older changeset, use
4236 If you want to update just one file to an older changeset, use
4177 :hg:`revert`.
4237 :hg:`revert`.
4178
4238
4179 See :hg:`help dates` for a list of formats valid for -d/--date.
4239 See :hg:`help dates` for a list of formats valid for -d/--date.
4180
4240
4181 Returns 0 on success, 1 if there are unresolved files.
4241 Returns 0 on success, 1 if there are unresolved files.
4182 """
4242 """
4183 if rev and node:
4243 if rev and node:
4184 raise util.Abort(_("please specify just one revision"))
4244 raise util.Abort(_("please specify just one revision"))
4185
4245
4186 if rev is None or rev == '':
4246 if rev is None or rev == '':
4187 rev = node
4247 rev = node
4188
4248
4189 # if we defined a bookmark, we have to remember the original bookmark name
4249 # if we defined a bookmark, we have to remember the original bookmark name
4190 brev = rev
4250 brev = rev
4191 rev = cmdutil.revsingle(repo, rev, rev).rev()
4251 rev = cmdutil.revsingle(repo, rev, rev).rev()
4192
4252
4193 if check and clean:
4253 if check and clean:
4194 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
4254 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
4195
4255
4196 if check:
4256 if check:
4197 # we could use dirty() but we can ignore merge and branch trivia
4257 # we could use dirty() but we can ignore merge and branch trivia
4198 c = repo[None]
4258 c = repo[None]
4199 if c.modified() or c.added() or c.removed():
4259 if c.modified() or c.added() or c.removed():
4200 raise util.Abort(_("uncommitted local changes"))
4260 raise util.Abort(_("uncommitted local changes"))
4201
4261
4202 if date:
4262 if date:
4203 if rev is not None:
4263 if rev is not None:
4204 raise util.Abort(_("you can't specify a revision and a date"))
4264 raise util.Abort(_("you can't specify a revision and a date"))
4205 rev = cmdutil.finddate(ui, repo, date)
4265 rev = cmdutil.finddate(ui, repo, date)
4206
4266
4207 if clean or check:
4267 if clean or check:
4208 ret = hg.clean(repo, rev)
4268 ret = hg.clean(repo, rev)
4209 else:
4269 else:
4210 ret = hg.update(repo, rev)
4270 ret = hg.update(repo, rev)
4211
4271
4212 if brev in repo._bookmarks:
4272 if brev in repo._bookmarks:
4213 bookmarks.setcurrent(repo, brev)
4273 bookmarks.setcurrent(repo, brev)
4214
4274
4215 return ret
4275 return ret
4216
4276
4217 def verify(ui, repo):
4277 def verify(ui, repo):
4218 """verify the integrity of the repository
4278 """verify the integrity of the repository
4219
4279
4220 Verify the integrity of the current repository.
4280 Verify the integrity of the current repository.
4221
4281
4222 This will perform an extensive check of the repository's
4282 This will perform an extensive check of the repository's
4223 integrity, validating the hashes and checksums of each entry in
4283 integrity, validating the hashes and checksums of each entry in
4224 the changelog, manifest, and tracked files, as well as the
4284 the changelog, manifest, and tracked files, as well as the
4225 integrity of their crosslinks and indices.
4285 integrity of their crosslinks and indices.
4226
4286
4227 Returns 0 on success, 1 if errors are encountered.
4287 Returns 0 on success, 1 if errors are encountered.
4228 """
4288 """
4229 return hg.verify(repo)
4289 return hg.verify(repo)
4230
4290
4231 def version_(ui):
4291 def version_(ui):
4232 """output version and copyright information"""
4292 """output version and copyright information"""
4233 ui.write(_("Mercurial Distributed SCM (version %s)\n")
4293 ui.write(_("Mercurial Distributed SCM (version %s)\n")
4234 % util.version())
4294 % util.version())
4235 ui.status(_(
4295 ui.status(_(
4236 "(see http://mercurial.selenic.com for more information)\n"
4296 "(see http://mercurial.selenic.com for more information)\n"
4237 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
4297 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
4238 "This is free software; see the source for copying conditions. "
4298 "This is free software; see the source for copying conditions. "
4239 "There is NO\nwarranty; "
4299 "There is NO\nwarranty; "
4240 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
4300 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
4241 ))
4301 ))
4242
4302
4243 # Command options and aliases are listed here, alphabetically
4303 # Command options and aliases are listed here, alphabetically
4244
4304
4245 globalopts = [
4305 globalopts = [
4246 ('R', 'repository', '',
4306 ('R', 'repository', '',
4247 _('repository root directory or name of overlay bundle file'),
4307 _('repository root directory or name of overlay bundle file'),
4248 _('REPO')),
4308 _('REPO')),
4249 ('', 'cwd', '',
4309 ('', 'cwd', '',
4250 _('change working directory'), _('DIR')),
4310 _('change working directory'), _('DIR')),
4251 ('y', 'noninteractive', None,
4311 ('y', 'noninteractive', None,
4252 _('do not prompt, assume \'yes\' for any required answers')),
4312 _('do not prompt, assume \'yes\' for any required answers')),
4253 ('q', 'quiet', None, _('suppress output')),
4313 ('q', 'quiet', None, _('suppress output')),
4254 ('v', 'verbose', None, _('enable additional output')),
4314 ('v', 'verbose', None, _('enable additional output')),
4255 ('', 'config', [],
4315 ('', 'config', [],
4256 _('set/override config option (use \'section.name=value\')'),
4316 _('set/override config option (use \'section.name=value\')'),
4257 _('CONFIG')),
4317 _('CONFIG')),
4258 ('', 'debug', None, _('enable debugging output')),
4318 ('', 'debug', None, _('enable debugging output')),
4259 ('', 'debugger', None, _('start debugger')),
4319 ('', 'debugger', None, _('start debugger')),
4260 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
4320 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
4261 _('ENCODE')),
4321 _('ENCODE')),
4262 ('', 'encodingmode', encoding.encodingmode,
4322 ('', 'encodingmode', encoding.encodingmode,
4263 _('set the charset encoding mode'), _('MODE')),
4323 _('set the charset encoding mode'), _('MODE')),
4264 ('', 'traceback', None, _('always print a traceback on exception')),
4324 ('', 'traceback', None, _('always print a traceback on exception')),
4265 ('', 'time', None, _('time how long the command takes')),
4325 ('', 'time', None, _('time how long the command takes')),
4266 ('', 'profile', None, _('print command execution profile')),
4326 ('', 'profile', None, _('print command execution profile')),
4267 ('', 'version', None, _('output version information and exit')),
4327 ('', 'version', None, _('output version information and exit')),
4268 ('h', 'help', None, _('display help and exit')),
4328 ('h', 'help', None, _('display help and exit')),
4269 ]
4329 ]
4270
4330
4271 dryrunopts = [('n', 'dry-run', None,
4331 dryrunopts = [('n', 'dry-run', None,
4272 _('do not perform actions, just print output'))]
4332 _('do not perform actions, just print output'))]
4273
4333
4274 remoteopts = [
4334 remoteopts = [
4275 ('e', 'ssh', '',
4335 ('e', 'ssh', '',
4276 _('specify ssh command to use'), _('CMD')),
4336 _('specify ssh command to use'), _('CMD')),
4277 ('', 'remotecmd', '',
4337 ('', 'remotecmd', '',
4278 _('specify hg command to run on the remote side'), _('CMD')),
4338 _('specify hg command to run on the remote side'), _('CMD')),
4279 ('', 'insecure', None,
4339 ('', 'insecure', None,
4280 _('do not verify server certificate (ignoring web.cacerts config)')),
4340 _('do not verify server certificate (ignoring web.cacerts config)')),
4281 ]
4341 ]
4282
4342
4283 walkopts = [
4343 walkopts = [
4284 ('I', 'include', [],
4344 ('I', 'include', [],
4285 _('include names matching the given patterns'), _('PATTERN')),
4345 _('include names matching the given patterns'), _('PATTERN')),
4286 ('X', 'exclude', [],
4346 ('X', 'exclude', [],
4287 _('exclude names matching the given patterns'), _('PATTERN')),
4347 _('exclude names matching the given patterns'), _('PATTERN')),
4288 ]
4348 ]
4289
4349
4290 commitopts = [
4350 commitopts = [
4291 ('m', 'message', '',
4351 ('m', 'message', '',
4292 _('use text as commit message'), _('TEXT')),
4352 _('use text as commit message'), _('TEXT')),
4293 ('l', 'logfile', '',
4353 ('l', 'logfile', '',
4294 _('read commit message from file'), _('FILE')),
4354 _('read commit message from file'), _('FILE')),
4295 ]
4355 ]
4296
4356
4297 commitopts2 = [
4357 commitopts2 = [
4298 ('d', 'date', '',
4358 ('d', 'date', '',
4299 _('record the specified date as commit date'), _('DATE')),
4359 _('record the specified date as commit date'), _('DATE')),
4300 ('u', 'user', '',
4360 ('u', 'user', '',
4301 _('record the specified user as committer'), _('USER')),
4361 _('record the specified user as committer'), _('USER')),
4302 ]
4362 ]
4303
4363
4304 templateopts = [
4364 templateopts = [
4305 ('', 'style', '',
4365 ('', 'style', '',
4306 _('display using template map file'), _('STYLE')),
4366 _('display using template map file'), _('STYLE')),
4307 ('', 'template', '',
4367 ('', 'template', '',
4308 _('display with template'), _('TEMPLATE')),
4368 _('display with template'), _('TEMPLATE')),
4309 ]
4369 ]
4310
4370
4311 logopts = [
4371 logopts = [
4312 ('p', 'patch', None, _('show patch')),
4372 ('p', 'patch', None, _('show patch')),
4313 ('g', 'git', None, _('use git extended diff format')),
4373 ('g', 'git', None, _('use git extended diff format')),
4314 ('l', 'limit', '',
4374 ('l', 'limit', '',
4315 _('limit number of changes displayed'), _('NUM')),
4375 _('limit number of changes displayed'), _('NUM')),
4316 ('M', 'no-merges', None, _('do not show merges')),
4376 ('M', 'no-merges', None, _('do not show merges')),
4317 ('', 'stat', None, _('output diffstat-style summary of changes')),
4377 ('', 'stat', None, _('output diffstat-style summary of changes')),
4318 ] + templateopts
4378 ] + templateopts
4319
4379
4320 diffopts = [
4380 diffopts = [
4321 ('a', 'text', None, _('treat all files as text')),
4381 ('a', 'text', None, _('treat all files as text')),
4322 ('g', 'git', None, _('use git extended diff format')),
4382 ('g', 'git', None, _('use git extended diff format')),
4323 ('', 'nodates', None, _('omit dates from diff headers'))
4383 ('', 'nodates', None, _('omit dates from diff headers'))
4324 ]
4384 ]
4325
4385
4326 diffopts2 = [
4386 diffopts2 = [
4327 ('p', 'show-function', None, _('show which function each change is in')),
4387 ('p', 'show-function', None, _('show which function each change is in')),
4328 ('', 'reverse', None, _('produce a diff that undoes the changes')),
4388 ('', 'reverse', None, _('produce a diff that undoes the changes')),
4329 ('w', 'ignore-all-space', None,
4389 ('w', 'ignore-all-space', None,
4330 _('ignore white space when comparing lines')),
4390 _('ignore white space when comparing lines')),
4331 ('b', 'ignore-space-change', None,
4391 ('b', 'ignore-space-change', None,
4332 _('ignore changes in the amount of white space')),
4392 _('ignore changes in the amount of white space')),
4333 ('B', 'ignore-blank-lines', None,
4393 ('B', 'ignore-blank-lines', None,
4334 _('ignore changes whose lines are all blank')),
4394 _('ignore changes whose lines are all blank')),
4335 ('U', 'unified', '',
4395 ('U', 'unified', '',
4336 _('number of lines of context to show'), _('NUM')),
4396 _('number of lines of context to show'), _('NUM')),
4337 ('', 'stat', None, _('output diffstat-style summary of changes')),
4397 ('', 'stat', None, _('output diffstat-style summary of changes')),
4338 ]
4398 ]
4339
4399
4340 similarityopts = [
4400 similarityopts = [
4341 ('s', 'similarity', '',
4401 ('s', 'similarity', '',
4342 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
4402 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
4343 ]
4403 ]
4344
4404
4345 subrepoopts = [
4405 subrepoopts = [
4346 ('S', 'subrepos', None,
4406 ('S', 'subrepos', None,
4347 _('recurse into subrepositories'))
4407 _('recurse into subrepositories'))
4348 ]
4408 ]
4349
4409
4350 table = {
4410 table = {
4351 "^add": (add, walkopts + subrepoopts + dryrunopts,
4411 "^add": (add, walkopts + subrepoopts + dryrunopts,
4352 _('[OPTION]... [FILE]...')),
4412 _('[OPTION]... [FILE]...')),
4353 "addremove":
4413 "addremove":
4354 (addremove, similarityopts + walkopts + dryrunopts,
4414 (addremove, similarityopts + walkopts + dryrunopts,
4355 _('[OPTION]... [FILE]...')),
4415 _('[OPTION]... [FILE]...')),
4356 "^annotate|blame":
4416 "^annotate|blame":
4357 (annotate,
4417 (annotate,
4358 [('r', 'rev', '',
4418 [('r', 'rev', '',
4359 _('annotate the specified revision'), _('REV')),
4419 _('annotate the specified revision'), _('REV')),
4360 ('', 'follow', None,
4420 ('', 'follow', None,
4361 _('follow copies/renames and list the filename (DEPRECATED)')),
4421 _('follow copies/renames and list the filename (DEPRECATED)')),
4362 ('', 'no-follow', None, _("don't follow copies and renames")),
4422 ('', 'no-follow', None, _("don't follow copies and renames")),
4363 ('a', 'text', None, _('treat all files as text')),
4423 ('a', 'text', None, _('treat all files as text')),
4364 ('u', 'user', None, _('list the author (long with -v)')),
4424 ('u', 'user', None, _('list the author (long with -v)')),
4365 ('f', 'file', None, _('list the filename')),
4425 ('f', 'file', None, _('list the filename')),
4366 ('d', 'date', None, _('list the date (short with -q)')),
4426 ('d', 'date', None, _('list the date (short with -q)')),
4367 ('n', 'number', None, _('list the revision number (default)')),
4427 ('n', 'number', None, _('list the revision number (default)')),
4368 ('c', 'changeset', None, _('list the changeset')),
4428 ('c', 'changeset', None, _('list the changeset')),
4369 ('l', 'line-number', None,
4429 ('l', 'line-number', None,
4370 _('show line number at the first appearance'))
4430 _('show line number at the first appearance'))
4371 ] + walkopts,
4431 ] + walkopts,
4372 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
4432 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
4373 "archive":
4433 "archive":
4374 (archive,
4434 (archive,
4375 [('', 'no-decode', None, _('do not pass files through decoders')),
4435 [('', 'no-decode', None, _('do not pass files through decoders')),
4376 ('p', 'prefix', '',
4436 ('p', 'prefix', '',
4377 _('directory prefix for files in archive'), _('PREFIX')),
4437 _('directory prefix for files in archive'), _('PREFIX')),
4378 ('r', 'rev', '',
4438 ('r', 'rev', '',
4379 _('revision to distribute'), _('REV')),
4439 _('revision to distribute'), _('REV')),
4380 ('t', 'type', '',
4440 ('t', 'type', '',
4381 _('type of distribution to create'), _('TYPE')),
4441 _('type of distribution to create'), _('TYPE')),
4382 ] + subrepoopts + walkopts,
4442 ] + subrepoopts + walkopts,
4383 _('[OPTION]... DEST')),
4443 _('[OPTION]... DEST')),
4384 "backout":
4444 "backout":
4385 (backout,
4445 (backout,
4386 [('', 'merge', None,
4446 [('', 'merge', None,
4387 _('merge with old dirstate parent after backout')),
4447 _('merge with old dirstate parent after backout')),
4388 ('', 'parent', '',
4448 ('', 'parent', '',
4389 _('parent to choose when backing out merge'), _('REV')),
4449 _('parent to choose when backing out merge'), _('REV')),
4390 ('t', 'tool', '',
4450 ('t', 'tool', '',
4391 _('specify merge tool')),
4451 _('specify merge tool')),
4392 ('r', 'rev', '',
4452 ('r', 'rev', '',
4393 _('revision to backout'), _('REV')),
4453 _('revision to backout'), _('REV')),
4394 ] + walkopts + commitopts + commitopts2,
4454 ] + walkopts + commitopts + commitopts2,
4395 _('[OPTION]... [-r] REV')),
4455 _('[OPTION]... [-r] REV')),
4396 "bisect":
4456 "bisect":
4397 (bisect,
4457 (bisect,
4398 [('r', 'reset', False, _('reset bisect state')),
4458 [('r', 'reset', False, _('reset bisect state')),
4399 ('g', 'good', False, _('mark changeset good')),
4459 ('g', 'good', False, _('mark changeset good')),
4400 ('b', 'bad', False, _('mark changeset bad')),
4460 ('b', 'bad', False, _('mark changeset bad')),
4401 ('s', 'skip', False, _('skip testing changeset')),
4461 ('s', 'skip', False, _('skip testing changeset')),
4402 ('e', 'extend', False, _('extend the bisect range')),
4462 ('e', 'extend', False, _('extend the bisect range')),
4403 ('c', 'command', '',
4463 ('c', 'command', '',
4404 _('use command to check changeset state'), _('CMD')),
4464 _('use command to check changeset state'), _('CMD')),
4405 ('U', 'noupdate', False, _('do not update to target'))],
4465 ('U', 'noupdate', False, _('do not update to target'))],
4406 _("[-gbsr] [-U] [-c CMD] [REV]")),
4466 _("[-gbsr] [-U] [-c CMD] [REV]")),
4407 "bookmarks":
4467 "bookmarks":
4408 (bookmark,
4468 (bookmark,
4409 [('f', 'force', False, _('force')),
4469 [('f', 'force', False, _('force')),
4410 ('r', 'rev', '', _('revision'), _('REV')),
4470 ('r', 'rev', '', _('revision'), _('REV')),
4411 ('d', 'delete', False, _('delete a given bookmark')),
4471 ('d', 'delete', False, _('delete a given bookmark')),
4412 ('m', 'rename', '', _('rename a given bookmark'), _('NAME'))],
4472 ('m', 'rename', '', _('rename a given bookmark'), _('NAME'))],
4413 _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
4473 _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
4414 "branch":
4474 "branch":
4415 (branch,
4475 (branch,
4416 [('f', 'force', None,
4476 [('f', 'force', None,
4417 _('set branch name even if it shadows an existing branch')),
4477 _('set branch name even if it shadows an existing branch')),
4418 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4478 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4419 _('[-fC] [NAME]')),
4479 _('[-fC] [NAME]')),
4420 "branches":
4480 "branches":
4421 (branches,
4481 (branches,
4422 [('a', 'active', False,
4482 [('a', 'active', False,
4423 _('show only branches that have unmerged heads')),
4483 _('show only branches that have unmerged heads')),
4424 ('c', 'closed', False,
4484 ('c', 'closed', False,
4425 _('show normal and closed branches'))],
4485 _('show normal and closed branches'))],
4426 _('[-ac]')),
4486 _('[-ac]')),
4427 "bundle":
4487 "bundle":
4428 (bundle,
4488 (bundle,
4429 [('f', 'force', None,
4489 [('f', 'force', None,
4430 _('run even when the destination is unrelated')),
4490 _('run even when the destination is unrelated')),
4431 ('r', 'rev', [],
4491 ('r', 'rev', [],
4432 _('a changeset intended to be added to the destination'),
4492 _('a changeset intended to be added to the destination'),
4433 _('REV')),
4493 _('REV')),
4434 ('b', 'branch', [],
4494 ('b', 'branch', [],
4435 _('a specific branch you would like to bundle'),
4495 _('a specific branch you would like to bundle'),
4436 _('BRANCH')),
4496 _('BRANCH')),
4437 ('', 'base', [],
4497 ('', 'base', [],
4438 _('a base changeset assumed to be available at the destination'),
4498 _('a base changeset assumed to be available at the destination'),
4439 _('REV')),
4499 _('REV')),
4440 ('a', 'all', None, _('bundle all changesets in the repository')),
4500 ('a', 'all', None, _('bundle all changesets in the repository')),
4441 ('t', 'type', 'bzip2',
4501 ('t', 'type', 'bzip2',
4442 _('bundle compression type to use'), _('TYPE')),
4502 _('bundle compression type to use'), _('TYPE')),
4443 ] + remoteopts,
4503 ] + remoteopts,
4444 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4504 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4445 "cat":
4505 "cat":
4446 (cat,
4506 (cat,
4447 [('o', 'output', '',
4507 [('o', 'output', '',
4448 _('print output to file with formatted name'), _('FORMAT')),
4508 _('print output to file with formatted name'), _('FORMAT')),
4449 ('r', 'rev', '',
4509 ('r', 'rev', '',
4450 _('print the given revision'), _('REV')),
4510 _('print the given revision'), _('REV')),
4451 ('', 'decode', None, _('apply any matching decode filter')),
4511 ('', 'decode', None, _('apply any matching decode filter')),
4452 ] + walkopts,
4512 ] + walkopts,
4453 _('[OPTION]... FILE...')),
4513 _('[OPTION]... FILE...')),
4454 "^clone":
4514 "^clone":
4455 (clone,
4515 (clone,
4456 [('U', 'noupdate', None,
4516 [('U', 'noupdate', None,
4457 _('the clone will include an empty working copy (only a repository)')),
4517 _('the clone will include an empty working copy (only a repository)')),
4458 ('u', 'updaterev', '',
4518 ('u', 'updaterev', '',
4459 _('revision, tag or branch to check out'), _('REV')),
4519 _('revision, tag or branch to check out'), _('REV')),
4460 ('r', 'rev', [],
4520 ('r', 'rev', [],
4461 _('include the specified changeset'), _('REV')),
4521 _('include the specified changeset'), _('REV')),
4462 ('b', 'branch', [],
4522 ('b', 'branch', [],
4463 _('clone only the specified branch'), _('BRANCH')),
4523 _('clone only the specified branch'), _('BRANCH')),
4464 ('', 'pull', None, _('use pull protocol to copy metadata')),
4524 ('', 'pull', None, _('use pull protocol to copy metadata')),
4465 ('', 'uncompressed', None,
4525 ('', 'uncompressed', None,
4466 _('use uncompressed transfer (fast over LAN)')),
4526 _('use uncompressed transfer (fast over LAN)')),
4467 ] + remoteopts,
4527 ] + remoteopts,
4468 _('[OPTION]... SOURCE [DEST]')),
4528 _('[OPTION]... SOURCE [DEST]')),
4469 "^commit|ci":
4529 "^commit|ci":
4470 (commit,
4530 (commit,
4471 [('A', 'addremove', None,
4531 [('A', 'addremove', None,
4472 _('mark new/missing files as added/removed before committing')),
4532 _('mark new/missing files as added/removed before committing')),
4473 ('', 'close-branch', None,
4533 ('', 'close-branch', None,
4474 _('mark a branch as closed, hiding it from the branch list')),
4534 _('mark a branch as closed, hiding it from the branch list')),
4475 ] + walkopts + commitopts + commitopts2,
4535 ] + walkopts + commitopts + commitopts2,
4476 _('[OPTION]... [FILE]...')),
4536 _('[OPTION]... [FILE]...')),
4477 "copy|cp":
4537 "copy|cp":
4478 (copy,
4538 (copy,
4479 [('A', 'after', None, _('record a copy that has already occurred')),
4539 [('A', 'after', None, _('record a copy that has already occurred')),
4480 ('f', 'force', None,
4540 ('f', 'force', None,
4481 _('forcibly copy over an existing managed file')),
4541 _('forcibly copy over an existing managed file')),
4482 ] + walkopts + dryrunopts,
4542 ] + walkopts + dryrunopts,
4483 _('[OPTION]... [SOURCE]... DEST')),
4543 _('[OPTION]... [SOURCE]... DEST')),
4484 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4544 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4485 "debugbuilddag":
4545 "debugbuilddag":
4486 (debugbuilddag,
4546 (debugbuilddag,
4487 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4547 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4488 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4548 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4489 ('n', 'new-file', None, _('add new file at each rev')),
4549 ('n', 'new-file', None, _('add new file at each rev')),
4490 ],
4550 ],
4491 _('[OPTION]... TEXT')),
4551 _('[OPTION]... TEXT')),
4492 "debugbundle":
4552 "debugbundle":
4493 (debugbundle,
4553 (debugbundle,
4494 [('a', 'all', None, _('show all details')),
4554 [('a', 'all', None, _('show all details')),
4495 ],
4555 ],
4496 _('FILE')),
4556 _('FILE')),
4497 "debugcheckstate": (debugcheckstate, [], ''),
4557 "debugcheckstate": (debugcheckstate, [], ''),
4498 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4558 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4499 "debugcomplete":
4559 "debugcomplete":
4500 (debugcomplete,
4560 (debugcomplete,
4501 [('o', 'options', None, _('show the command options'))],
4561 [('o', 'options', None, _('show the command options'))],
4502 _('[-o] CMD')),
4562 _('[-o] CMD')),
4503 "debugdag":
4563 "debugdag":
4504 (debugdag,
4564 (debugdag,
4505 [('t', 'tags', None, _('use tags as labels')),
4565 [('t', 'tags', None, _('use tags as labels')),
4506 ('b', 'branches', None, _('annotate with branch names')),
4566 ('b', 'branches', None, _('annotate with branch names')),
4507 ('', 'dots', None, _('use dots for runs')),
4567 ('', 'dots', None, _('use dots for runs')),
4508 ('s', 'spaces', None, _('separate elements by spaces')),
4568 ('s', 'spaces', None, _('separate elements by spaces')),
4509 ],
4569 ],
4510 _('[OPTION]... [FILE [REV]...]')),
4570 _('[OPTION]... [FILE [REV]...]')),
4511 "debugdate":
4571 "debugdate":
4512 (debugdate,
4572 (debugdate,
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,
4519 [('H', 'head', [], _('id of head node'), _('ID')),
4587 [('H', 'head', [], _('id of head node'), _('ID')),
4520 ('C', 'common', [], _('id of common node'), _('ID')),
4588 ('C', 'common', [], _('id of common node'), _('ID')),
4521 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
4589 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
4522 ],
4590 ],
4523 _('REPO FILE [-H|-C ID]...')),
4591 _('REPO FILE [-H|-C ID]...')),
4524 "debugignore": (debugignore, [], ''),
4592 "debugignore": (debugignore, [], ''),
4525 "debugindex": (debugindex,
4593 "debugindex": (debugindex,
4526 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
4594 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
4527 _('FILE')),
4595 _('FILE')),
4528 "debugindexdot": (debugindexdot, [], _('FILE')),
4596 "debugindexdot": (debugindexdot, [], _('FILE')),
4529 "debuginstall": (debuginstall, [], ''),
4597 "debuginstall": (debuginstall, [], ''),
4530 "debugknown": (debugknown, [], _('REPO ID...')),
4598 "debugknown": (debugknown, [], _('REPO ID...')),
4531 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4599 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4532 "debugrebuildstate":
4600 "debugrebuildstate":
4533 (debugrebuildstate,
4601 (debugrebuildstate,
4534 [('r', 'rev', '',
4602 [('r', 'rev', '',
4535 _('revision to rebuild to'), _('REV'))],
4603 _('revision to rebuild to'), _('REV'))],
4536 _('[-r REV] [REV]')),
4604 _('[-r REV] [REV]')),
4537 "debugrename":
4605 "debugrename":
4538 (debugrename,
4606 (debugrename,
4539 [('r', 'rev', '',
4607 [('r', 'rev', '',
4540 _('revision to debug'), _('REV'))],
4608 _('revision to debug'), _('REV'))],
4541 _('[-r REV] FILE')),
4609 _('[-r REV] FILE')),
4542 "debugrevspec":
4610 "debugrevspec":
4543 (debugrevspec, [], ('REVSPEC')),
4611 (debugrevspec, [], ('REVSPEC')),
4544 "debugsetparents":
4612 "debugsetparents":
4545 (debugsetparents, [], _('REV1 [REV2]')),
4613 (debugsetparents, [], _('REV1 [REV2]')),
4546 "debugstate":
4614 "debugstate":
4547 (debugstate,
4615 (debugstate,
4548 [('', 'nodates', None, _('do not display the saved mtime')),
4616 [('', 'nodates', None, _('do not display the saved mtime')),
4549 ('', 'datesort', None, _('sort by saved mtime'))],
4617 ('', 'datesort', None, _('sort by saved mtime'))],
4550 _('[OPTION]...')),
4618 _('[OPTION]...')),
4551 "debugsub":
4619 "debugsub":
4552 (debugsub,
4620 (debugsub,
4553 [('r', 'rev', '',
4621 [('r', 'rev', '',
4554 _('revision to check'), _('REV'))],
4622 _('revision to check'), _('REV'))],
4555 _('[-r REV] [REV]')),
4623 _('[-r REV] [REV]')),
4556 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4624 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4557 "debugwireargs":
4625 "debugwireargs":
4558 (debugwireargs,
4626 (debugwireargs,
4559 [('', 'three', '', 'three'),
4627 [('', 'three', '', 'three'),
4560 ('', 'four', '', 'four'),
4628 ('', 'four', '', 'four'),
4561 ('', 'five', '', 'five'),
4629 ('', 'five', '', 'five'),
4562 ] + remoteopts,
4630 ] + remoteopts,
4563 _('REPO [OPTIONS]... [ONE [TWO]]')),
4631 _('REPO [OPTIONS]... [ONE [TWO]]')),
4564 "^diff":
4632 "^diff":
4565 (diff,
4633 (diff,
4566 [('r', 'rev', [],
4634 [('r', 'rev', [],
4567 _('revision'), _('REV')),
4635 _('revision'), _('REV')),
4568 ('c', 'change', '',
4636 ('c', 'change', '',
4569 _('change made by revision'), _('REV'))
4637 _('change made by revision'), _('REV'))
4570 ] + diffopts + diffopts2 + walkopts + subrepoopts,
4638 ] + diffopts + diffopts2 + walkopts + subrepoopts,
4571 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4639 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4572 "^export":
4640 "^export":
4573 (export,
4641 (export,
4574 [('o', 'output', '',
4642 [('o', 'output', '',
4575 _('print output to file with formatted name'), _('FORMAT')),
4643 _('print output to file with formatted name'), _('FORMAT')),
4576 ('', 'switch-parent', None, _('diff against the second parent')),
4644 ('', 'switch-parent', None, _('diff against the second parent')),
4577 ('r', 'rev', [],
4645 ('r', 'rev', [],
4578 _('revisions to export'), _('REV')),
4646 _('revisions to export'), _('REV')),
4579 ] + diffopts,
4647 ] + diffopts,
4580 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4648 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4581 "^forget":
4649 "^forget":
4582 (forget,
4650 (forget,
4583 [] + walkopts,
4651 [] + walkopts,
4584 _('[OPTION]... FILE...')),
4652 _('[OPTION]... FILE...')),
4585 "grep":
4653 "grep":
4586 (grep,
4654 (grep,
4587 [('0', 'print0', None, _('end fields with NUL')),
4655 [('0', 'print0', None, _('end fields with NUL')),
4588 ('', 'all', None, _('print all revisions that match')),
4656 ('', 'all', None, _('print all revisions that match')),
4589 ('a', 'text', None, _('treat all files as text')),
4657 ('a', 'text', None, _('treat all files as text')),
4590 ('f', 'follow', None,
4658 ('f', 'follow', None,
4591 _('follow changeset history,'
4659 _('follow changeset history,'
4592 ' or file history across copies and renames')),
4660 ' or file history across copies and renames')),
4593 ('i', 'ignore-case', None, _('ignore case when matching')),
4661 ('i', 'ignore-case', None, _('ignore case when matching')),
4594 ('l', 'files-with-matches', None,
4662 ('l', 'files-with-matches', None,
4595 _('print only filenames and revisions that match')),
4663 _('print only filenames and revisions that match')),
4596 ('n', 'line-number', None, _('print matching line numbers')),
4664 ('n', 'line-number', None, _('print matching line numbers')),
4597 ('r', 'rev', [],
4665 ('r', 'rev', [],
4598 _('only search files changed within revision range'), _('REV')),
4666 _('only search files changed within revision range'), _('REV')),
4599 ('u', 'user', None, _('list the author (long with -v)')),
4667 ('u', 'user', None, _('list the author (long with -v)')),
4600 ('d', 'date', None, _('list the date (short with -q)')),
4668 ('d', 'date', None, _('list the date (short with -q)')),
4601 ] + walkopts,
4669 ] + walkopts,
4602 _('[OPTION]... PATTERN [FILE]...')),
4670 _('[OPTION]... PATTERN [FILE]...')),
4603 "heads":
4671 "heads":
4604 (heads,
4672 (heads,
4605 [('r', 'rev', '',
4673 [('r', 'rev', '',
4606 _('show only heads which are descendants of STARTREV'),
4674 _('show only heads which are descendants of STARTREV'),
4607 _('STARTREV')),
4675 _('STARTREV')),
4608 ('t', 'topo', False, _('show topological heads only')),
4676 ('t', 'topo', False, _('show topological heads only')),
4609 ('a', 'active', False,
4677 ('a', 'active', False,
4610 _('show active branchheads only (DEPRECATED)')),
4678 _('show active branchheads only (DEPRECATED)')),
4611 ('c', 'closed', False,
4679 ('c', 'closed', False,
4612 _('show normal and closed branch heads')),
4680 _('show normal and closed branch heads')),
4613 ] + templateopts,
4681 ] + templateopts,
4614 _('[-ac] [-r STARTREV] [REV]...')),
4682 _('[-ac] [-r STARTREV] [REV]...')),
4615 "help": (help_, [], _('[TOPIC]')),
4683 "help": (help_, [], _('[TOPIC]')),
4616 "identify|id":
4684 "identify|id":
4617 (identify,
4685 (identify,
4618 [('r', 'rev', '',
4686 [('r', 'rev', '',
4619 _('identify the specified revision'), _('REV')),
4687 _('identify the specified revision'), _('REV')),
4620 ('n', 'num', None, _('show local revision number')),
4688 ('n', 'num', None, _('show local revision number')),
4621 ('i', 'id', None, _('show global revision id')),
4689 ('i', 'id', None, _('show global revision id')),
4622 ('b', 'branch', None, _('show branch')),
4690 ('b', 'branch', None, _('show branch')),
4623 ('t', 'tags', None, _('show tags')),
4691 ('t', 'tags', None, _('show tags')),
4624 ('B', 'bookmarks', None, _('show bookmarks'))],
4692 ('B', 'bookmarks', None, _('show bookmarks'))],
4625 _('[-nibtB] [-r REV] [SOURCE]')),
4693 _('[-nibtB] [-r REV] [SOURCE]')),
4626 "import|patch":
4694 "import|patch":
4627 (import_,
4695 (import_,
4628 [('p', 'strip', 1,
4696 [('p', 'strip', 1,
4629 _('directory strip option for patch. This has the same '
4697 _('directory strip option for patch. This has the same '
4630 'meaning as the corresponding patch option'),
4698 'meaning as the corresponding patch option'),
4631 _('NUM')),
4699 _('NUM')),
4632 ('b', 'base', '',
4700 ('b', 'base', '',
4633 _('base path'), _('PATH')),
4701 _('base path'), _('PATH')),
4634 ('f', 'force', None,
4702 ('f', 'force', None,
4635 _('skip check for outstanding uncommitted changes')),
4703 _('skip check for outstanding uncommitted changes')),
4636 ('', 'no-commit', None,
4704 ('', 'no-commit', None,
4637 _("don't commit, just update the working directory")),
4705 _("don't commit, just update the working directory")),
4638 ('', 'exact', None,
4706 ('', 'exact', None,
4639 _('apply patch to the nodes from which it was generated')),
4707 _('apply patch to the nodes from which it was generated')),
4640 ('', 'import-branch', None,
4708 ('', 'import-branch', None,
4641 _('use any branch information in patch (implied by --exact)'))] +
4709 _('use any branch information in patch (implied by --exact)'))] +
4642 commitopts + commitopts2 + similarityopts,
4710 commitopts + commitopts2 + similarityopts,
4643 _('[OPTION]... PATCH...')),
4711 _('[OPTION]... PATCH...')),
4644 "incoming|in":
4712 "incoming|in":
4645 (incoming,
4713 (incoming,
4646 [('f', 'force', None,
4714 [('f', 'force', None,
4647 _('run even if remote repository is unrelated')),
4715 _('run even if remote repository is unrelated')),
4648 ('n', 'newest-first', None, _('show newest record first')),
4716 ('n', 'newest-first', None, _('show newest record first')),
4649 ('', 'bundle', '',
4717 ('', 'bundle', '',
4650 _('file to store the bundles into'), _('FILE')),
4718 _('file to store the bundles into'), _('FILE')),
4651 ('r', 'rev', [],
4719 ('r', 'rev', [],
4652 _('a remote changeset intended to be added'), _('REV')),
4720 _('a remote changeset intended to be added'), _('REV')),
4653 ('B', 'bookmarks', False, _("compare bookmarks")),
4721 ('B', 'bookmarks', False, _("compare bookmarks")),
4654 ('b', 'branch', [],
4722 ('b', 'branch', [],
4655 _('a specific branch you would like to pull'), _('BRANCH')),
4723 _('a specific branch you would like to pull'), _('BRANCH')),
4656 ] + logopts + remoteopts + subrepoopts,
4724 ] + logopts + remoteopts + subrepoopts,
4657 _('[-p] [-n] [-M] [-f] [-r REV]...'
4725 _('[-p] [-n] [-M] [-f] [-r REV]...'
4658 ' [--bundle FILENAME] [SOURCE]')),
4726 ' [--bundle FILENAME] [SOURCE]')),
4659 "^init":
4727 "^init":
4660 (init,
4728 (init,
4661 remoteopts,
4729 remoteopts,
4662 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4730 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4663 "locate":
4731 "locate":
4664 (locate,
4732 (locate,
4665 [('r', 'rev', '',
4733 [('r', 'rev', '',
4666 _('search the repository as it is in REV'), _('REV')),
4734 _('search the repository as it is in REV'), _('REV')),
4667 ('0', 'print0', None,
4735 ('0', 'print0', None,
4668 _('end filenames with NUL, for use with xargs')),
4736 _('end filenames with NUL, for use with xargs')),
4669 ('f', 'fullpath', None,
4737 ('f', 'fullpath', None,
4670 _('print complete paths from the filesystem root')),
4738 _('print complete paths from the filesystem root')),
4671 ] + walkopts,
4739 ] + walkopts,
4672 _('[OPTION]... [PATTERN]...')),
4740 _('[OPTION]... [PATTERN]...')),
4673 "^log|history":
4741 "^log|history":
4674 (log,
4742 (log,
4675 [('f', 'follow', None,
4743 [('f', 'follow', None,
4676 _('follow changeset history,'
4744 _('follow changeset history,'
4677 ' or file history across copies and renames')),
4745 ' or file history across copies and renames')),
4678 ('', 'follow-first', None,
4746 ('', 'follow-first', None,
4679 _('only follow the first parent of merge changesets')),
4747 _('only follow the first parent of merge changesets')),
4680 ('d', 'date', '',
4748 ('d', 'date', '',
4681 _('show revisions matching date spec'), _('DATE')),
4749 _('show revisions matching date spec'), _('DATE')),
4682 ('C', 'copies', None, _('show copied files')),
4750 ('C', 'copies', None, _('show copied files')),
4683 ('k', 'keyword', [],
4751 ('k', 'keyword', [],
4684 _('do case-insensitive search for a given text'), _('TEXT')),
4752 _('do case-insensitive search for a given text'), _('TEXT')),
4685 ('r', 'rev', [],
4753 ('r', 'rev', [],
4686 _('show the specified revision or range'), _('REV')),
4754 _('show the specified revision or range'), _('REV')),
4687 ('', 'removed', None, _('include revisions where files were removed')),
4755 ('', 'removed', None, _('include revisions where files were removed')),
4688 ('m', 'only-merges', None, _('show only merges')),
4756 ('m', 'only-merges', None, _('show only merges')),
4689 ('u', 'user', [],
4757 ('u', 'user', [],
4690 _('revisions committed by user'), _('USER')),
4758 _('revisions committed by user'), _('USER')),
4691 ('', 'only-branch', [],
4759 ('', 'only-branch', [],
4692 _('show only changesets within the given named branch (DEPRECATED)'),
4760 _('show only changesets within the given named branch (DEPRECATED)'),
4693 _('BRANCH')),
4761 _('BRANCH')),
4694 ('b', 'branch', [],
4762 ('b', 'branch', [],
4695 _('show changesets within the given named branch'), _('BRANCH')),
4763 _('show changesets within the given named branch'), _('BRANCH')),
4696 ('P', 'prune', [],
4764 ('P', 'prune', [],
4697 _('do not display revision or any of its ancestors'), _('REV')),
4765 _('do not display revision or any of its ancestors'), _('REV')),
4698 ] + logopts + walkopts,
4766 ] + logopts + walkopts,
4699 _('[OPTION]... [FILE]')),
4767 _('[OPTION]... [FILE]')),
4700 "manifest":
4768 "manifest":
4701 (manifest,
4769 (manifest,
4702 [('r', 'rev', '',
4770 [('r', 'rev', '',
4703 _('revision to display'), _('REV'))],
4771 _('revision to display'), _('REV'))],
4704 _('[-r REV]')),
4772 _('[-r REV]')),
4705 "^merge":
4773 "^merge":
4706 (merge,
4774 (merge,
4707 [('f', 'force', None, _('force a merge with outstanding changes')),
4775 [('f', 'force', None, _('force a merge with outstanding changes')),
4708 ('t', 'tool', '', _('specify merge tool')),
4776 ('t', 'tool', '', _('specify merge tool')),
4709 ('r', 'rev', '',
4777 ('r', 'rev', '',
4710 _('revision to merge'), _('REV')),
4778 _('revision to merge'), _('REV')),
4711 ('P', 'preview', None,
4779 ('P', 'preview', None,
4712 _('review revisions to merge (no merge is performed)'))],
4780 _('review revisions to merge (no merge is performed)'))],
4713 _('[-P] [-f] [[-r] REV]')),
4781 _('[-P] [-f] [[-r] REV]')),
4714 "outgoing|out":
4782 "outgoing|out":
4715 (outgoing,
4783 (outgoing,
4716 [('f', 'force', None,
4784 [('f', 'force', None,
4717 _('run even when the destination is unrelated')),
4785 _('run even when the destination is unrelated')),
4718 ('r', 'rev', [],
4786 ('r', 'rev', [],
4719 _('a changeset intended to be included in the destination'),
4787 _('a changeset intended to be included in the destination'),
4720 _('REV')),
4788 _('REV')),
4721 ('n', 'newest-first', None, _('show newest record first')),
4789 ('n', 'newest-first', None, _('show newest record first')),
4722 ('B', 'bookmarks', False, _("compare bookmarks")),
4790 ('B', 'bookmarks', False, _("compare bookmarks")),
4723 ('b', 'branch', [],
4791 ('b', 'branch', [],
4724 _('a specific branch you would like to push'), _('BRANCH')),
4792 _('a specific branch you would like to push'), _('BRANCH')),
4725 ] + logopts + remoteopts + subrepoopts,
4793 ] + logopts + remoteopts + subrepoopts,
4726 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4794 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4727 "parents":
4795 "parents":
4728 (parents,
4796 (parents,
4729 [('r', 'rev', '',
4797 [('r', 'rev', '',
4730 _('show parents of the specified revision'), _('REV')),
4798 _('show parents of the specified revision'), _('REV')),
4731 ] + templateopts,
4799 ] + templateopts,
4732 _('[-r REV] [FILE]')),
4800 _('[-r REV] [FILE]')),
4733 "paths": (paths, [], _('[NAME]')),
4801 "paths": (paths, [], _('[NAME]')),
4734 "^pull":
4802 "^pull":
4735 (pull,
4803 (pull,
4736 [('u', 'update', None,
4804 [('u', 'update', None,
4737 _('update to new branch head if changesets were pulled')),
4805 _('update to new branch head if changesets were pulled')),
4738 ('f', 'force', None,
4806 ('f', 'force', None,
4739 _('run even when remote repository is unrelated')),
4807 _('run even when remote repository is unrelated')),
4740 ('r', 'rev', [],
4808 ('r', 'rev', [],
4741 _('a remote changeset intended to be added'), _('REV')),
4809 _('a remote changeset intended to be added'), _('REV')),
4742 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4810 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4743 ('b', 'branch', [],
4811 ('b', 'branch', [],
4744 _('a specific branch you would like to pull'), _('BRANCH')),
4812 _('a specific branch you would like to pull'), _('BRANCH')),
4745 ] + remoteopts,
4813 ] + remoteopts,
4746 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4814 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4747 "^push":
4815 "^push":
4748 (push,
4816 (push,
4749 [('f', 'force', None, _('force push')),
4817 [('f', 'force', None, _('force push')),
4750 ('r', 'rev', [],
4818 ('r', 'rev', [],
4751 _('a changeset intended to be included in the destination'),
4819 _('a changeset intended to be included in the destination'),
4752 _('REV')),
4820 _('REV')),
4753 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4821 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4754 ('b', 'branch', [],
4822 ('b', 'branch', [],
4755 _('a specific branch you would like to push'), _('BRANCH')),
4823 _('a specific branch you would like to push'), _('BRANCH')),
4756 ('', 'new-branch', False, _('allow pushing a new branch')),
4824 ('', 'new-branch', False, _('allow pushing a new branch')),
4757 ] + remoteopts,
4825 ] + remoteopts,
4758 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4826 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4759 "recover": (recover, []),
4827 "recover": (recover, []),
4760 "^remove|rm":
4828 "^remove|rm":
4761 (remove,
4829 (remove,
4762 [('A', 'after', None, _('record delete for missing files')),
4830 [('A', 'after', None, _('record delete for missing files')),
4763 ('f', 'force', None,
4831 ('f', 'force', None,
4764 _('remove (and delete) file even if added or modified')),
4832 _('remove (and delete) file even if added or modified')),
4765 ] + walkopts,
4833 ] + walkopts,
4766 _('[OPTION]... FILE...')),
4834 _('[OPTION]... FILE...')),
4767 "rename|move|mv":
4835 "rename|move|mv":
4768 (rename,
4836 (rename,
4769 [('A', 'after', None, _('record a rename that has already occurred')),
4837 [('A', 'after', None, _('record a rename that has already occurred')),
4770 ('f', 'force', None,
4838 ('f', 'force', None,
4771 _('forcibly copy over an existing managed file')),
4839 _('forcibly copy over an existing managed file')),
4772 ] + walkopts + dryrunopts,
4840 ] + walkopts + dryrunopts,
4773 _('[OPTION]... SOURCE... DEST')),
4841 _('[OPTION]... SOURCE... DEST')),
4774 "resolve":
4842 "resolve":
4775 (resolve,
4843 (resolve,
4776 [('a', 'all', None, _('select all unresolved files')),
4844 [('a', 'all', None, _('select all unresolved files')),
4777 ('l', 'list', None, _('list state of files needing merge')),
4845 ('l', 'list', None, _('list state of files needing merge')),
4778 ('m', 'mark', None, _('mark files as resolved')),
4846 ('m', 'mark', None, _('mark files as resolved')),
4779 ('u', 'unmark', None, _('mark files as unresolved')),
4847 ('u', 'unmark', None, _('mark files as unresolved')),
4780 ('t', 'tool', '', _('specify merge tool')),
4848 ('t', 'tool', '', _('specify merge tool')),
4781 ('n', 'no-status', None, _('hide status prefix'))]
4849 ('n', 'no-status', None, _('hide status prefix'))]
4782 + walkopts,
4850 + walkopts,
4783 _('[OPTION]... [FILE]...')),
4851 _('[OPTION]... [FILE]...')),
4784 "revert":
4852 "revert":
4785 (revert,
4853 (revert,
4786 [('a', 'all', None, _('revert all changes when no arguments given')),
4854 [('a', 'all', None, _('revert all changes when no arguments given')),
4787 ('d', 'date', '',
4855 ('d', 'date', '',
4788 _('tipmost revision matching date'), _('DATE')),
4856 _('tipmost revision matching date'), _('DATE')),
4789 ('r', 'rev', '',
4857 ('r', 'rev', '',
4790 _('revert to the specified revision'), _('REV')),
4858 _('revert to the specified revision'), _('REV')),
4791 ('', 'no-backup', None, _('do not save backup copies of files')),
4859 ('', 'no-backup', None, _('do not save backup copies of files')),
4792 ] + walkopts + dryrunopts,
4860 ] + walkopts + dryrunopts,
4793 _('[OPTION]... [-r REV] [NAME]...')),
4861 _('[OPTION]... [-r REV] [NAME]...')),
4794 "rollback": (rollback, dryrunopts),
4862 "rollback": (rollback, dryrunopts),
4795 "root": (root, []),
4863 "root": (root, []),
4796 "^serve":
4864 "^serve":
4797 (serve,
4865 (serve,
4798 [('A', 'accesslog', '',
4866 [('A', 'accesslog', '',
4799 _('name of access log file to write to'), _('FILE')),
4867 _('name of access log file to write to'), _('FILE')),
4800 ('d', 'daemon', None, _('run server in background')),
4868 ('d', 'daemon', None, _('run server in background')),
4801 ('', 'daemon-pipefds', '',
4869 ('', 'daemon-pipefds', '',
4802 _('used internally by daemon mode'), _('NUM')),
4870 _('used internally by daemon mode'), _('NUM')),
4803 ('E', 'errorlog', '',
4871 ('E', 'errorlog', '',
4804 _('name of error log file to write to'), _('FILE')),
4872 _('name of error log file to write to'), _('FILE')),
4805 # use string type, then we can check if something was passed
4873 # use string type, then we can check if something was passed
4806 ('p', 'port', '',
4874 ('p', 'port', '',
4807 _('port to listen on (default: 8000)'), _('PORT')),
4875 _('port to listen on (default: 8000)'), _('PORT')),
4808 ('a', 'address', '',
4876 ('a', 'address', '',
4809 _('address to listen on (default: all interfaces)'), _('ADDR')),
4877 _('address to listen on (default: all interfaces)'), _('ADDR')),
4810 ('', 'prefix', '',
4878 ('', 'prefix', '',
4811 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4879 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4812 ('n', 'name', '',
4880 ('n', 'name', '',
4813 _('name to show in web pages (default: working directory)'),
4881 _('name to show in web pages (default: working directory)'),
4814 _('NAME')),
4882 _('NAME')),
4815 ('', 'web-conf', '',
4883 ('', 'web-conf', '',
4816 _('name of the hgweb config file (see "hg help hgweb")'),
4884 _('name of the hgweb config file (see "hg help hgweb")'),
4817 _('FILE')),
4885 _('FILE')),
4818 ('', 'webdir-conf', '',
4886 ('', 'webdir-conf', '',
4819 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4887 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4820 ('', 'pid-file', '',
4888 ('', 'pid-file', '',
4821 _('name of file to write process ID to'), _('FILE')),
4889 _('name of file to write process ID to'), _('FILE')),
4822 ('', 'stdio', None, _('for remote clients')),
4890 ('', 'stdio', None, _('for remote clients')),
4823 ('t', 'templates', '',
4891 ('t', 'templates', '',
4824 _('web templates to use'), _('TEMPLATE')),
4892 _('web templates to use'), _('TEMPLATE')),
4825 ('', 'style', '',
4893 ('', 'style', '',
4826 _('template style to use'), _('STYLE')),
4894 _('template style to use'), _('STYLE')),
4827 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4895 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4828 ('', 'certificate', '',
4896 ('', 'certificate', '',
4829 _('SSL certificate file'), _('FILE'))],
4897 _('SSL certificate file'), _('FILE'))],
4830 _('[OPTION]...')),
4898 _('[OPTION]...')),
4831 "showconfig|debugconfig":
4899 "showconfig|debugconfig":
4832 (showconfig,
4900 (showconfig,
4833 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4901 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4834 _('[-u] [NAME]...')),
4902 _('[-u] [NAME]...')),
4835 "^summary|sum":
4903 "^summary|sum":
4836 (summary,
4904 (summary,
4837 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4905 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4838 "^status|st":
4906 "^status|st":
4839 (status,
4907 (status,
4840 [('A', 'all', None, _('show status of all files')),
4908 [('A', 'all', None, _('show status of all files')),
4841 ('m', 'modified', None, _('show only modified files')),
4909 ('m', 'modified', None, _('show only modified files')),
4842 ('a', 'added', None, _('show only added files')),
4910 ('a', 'added', None, _('show only added files')),
4843 ('r', 'removed', None, _('show only removed files')),
4911 ('r', 'removed', None, _('show only removed files')),
4844 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4912 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4845 ('c', 'clean', None, _('show only files without changes')),
4913 ('c', 'clean', None, _('show only files without changes')),
4846 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4914 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4847 ('i', 'ignored', None, _('show only ignored files')),
4915 ('i', 'ignored', None, _('show only ignored files')),
4848 ('n', 'no-status', None, _('hide status prefix')),
4916 ('n', 'no-status', None, _('hide status prefix')),
4849 ('C', 'copies', None, _('show source of copied files')),
4917 ('C', 'copies', None, _('show source of copied files')),
4850 ('0', 'print0', None,
4918 ('0', 'print0', None,
4851 _('end filenames with NUL, for use with xargs')),
4919 _('end filenames with NUL, for use with xargs')),
4852 ('', 'rev', [],
4920 ('', 'rev', [],
4853 _('show difference from revision'), _('REV')),
4921 _('show difference from revision'), _('REV')),
4854 ('', 'change', '',
4922 ('', 'change', '',
4855 _('list the changed files of a revision'), _('REV')),
4923 _('list the changed files of a revision'), _('REV')),
4856 ] + walkopts + subrepoopts,
4924 ] + walkopts + subrepoopts,
4857 _('[OPTION]... [FILE]...')),
4925 _('[OPTION]... [FILE]...')),
4858 "tag":
4926 "tag":
4859 (tag,
4927 (tag,
4860 [('f', 'force', None, _('force tag')),
4928 [('f', 'force', None, _('force tag')),
4861 ('l', 'local', None, _('make the tag local')),
4929 ('l', 'local', None, _('make the tag local')),
4862 ('r', 'rev', '',
4930 ('r', 'rev', '',
4863 _('revision to tag'), _('REV')),
4931 _('revision to tag'), _('REV')),
4864 ('', 'remove', None, _('remove a tag')),
4932 ('', 'remove', None, _('remove a tag')),
4865 # -l/--local is already there, commitopts cannot be used
4933 # -l/--local is already there, commitopts cannot be used
4866 ('e', 'edit', None, _('edit commit message')),
4934 ('e', 'edit', None, _('edit commit message')),
4867 ('m', 'message', '',
4935 ('m', 'message', '',
4868 _('use <text> as commit message'), _('TEXT')),
4936 _('use <text> as commit message'), _('TEXT')),
4869 ] + commitopts2,
4937 ] + commitopts2,
4870 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4938 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4871 "tags": (tags, [], ''),
4939 "tags": (tags, [], ''),
4872 "tip":
4940 "tip":
4873 (tip,
4941 (tip,
4874 [('p', 'patch', None, _('show patch')),
4942 [('p', 'patch', None, _('show patch')),
4875 ('g', 'git', None, _('use git extended diff format')),
4943 ('g', 'git', None, _('use git extended diff format')),
4876 ] + templateopts,
4944 ] + templateopts,
4877 _('[-p] [-g]')),
4945 _('[-p] [-g]')),
4878 "unbundle":
4946 "unbundle":
4879 (unbundle,
4947 (unbundle,
4880 [('u', 'update', None,
4948 [('u', 'update', None,
4881 _('update to new branch head if changesets were unbundled'))],
4949 _('update to new branch head if changesets were unbundled'))],
4882 _('[-u] FILE...')),
4950 _('[-u] FILE...')),
4883 "^update|up|checkout|co":
4951 "^update|up|checkout|co":
4884 (update,
4952 (update,
4885 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4953 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4886 ('c', 'check', None,
4954 ('c', 'check', None,
4887 _('update across branches if no uncommitted changes')),
4955 _('update across branches if no uncommitted changes')),
4888 ('d', 'date', '',
4956 ('d', 'date', '',
4889 _('tipmost revision matching date'), _('DATE')),
4957 _('tipmost revision matching date'), _('DATE')),
4890 ('r', 'rev', '',
4958 ('r', 'rev', '',
4891 _('revision'), _('REV'))],
4959 _('revision'), _('REV'))],
4892 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4960 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4893 "verify": (verify, []),
4961 "verify": (verify, []),
4894 "version": (version_, []),
4962 "version": (version_, []),
4895 }
4963 }
4896
4964
4897 norepo = ("clone init version help debugcommands debugcomplete"
4965 norepo = ("clone init version help debugcommands debugcomplete"
4898 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
4966 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
4899 " debugknown debuggetbundle debugbundle")
4967 " debugknown debuggetbundle debugbundle")
4900 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
4968 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
4901 " debugdata debugindex debugindexdot")
4969 " debugdata debugindex debugindexdot")
@@ -1,288 +1,169 b''
1 # discovery.py - protocol changeset discovery functions
1 # discovery.py - protocol changeset discovery functions
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
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
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
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 "anyincoming" is testable as a boolean indicating if any nodes are missing
18 locally. If remote does not support getbundle, this actually is a list of
18 locally. If remote does not support getbundle, this actually is a list of
19 roots of the nodes that would be incoming, to be supplied to
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
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
165 changesets need to be pushed to the remote. Return value depends
48 changesets need to be pushed to the remote. Return value depends
166 on circumstances:
49 on circumstances:
167
50
168 If we are not going to push anything, return a tuple (None,
51 If we are not going to push anything, return a tuple (None,
169 outgoing) where outgoing is 0 if there are no outgoing
52 outgoing) where outgoing is 0 if there are no outgoing
170 changesets and 1 if there are, but we refuse to push them
53 changesets and 1 if there are, but we refuse to push them
171 (e.g. would create new remote heads).
54 (e.g. would create new remote heads).
172
55
173 Otherwise, return a tuple (changegroup, remoteheads), where
56 Otherwise, return a tuple (changegroup, remoteheads), where
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)
183
64
184 if not outg:
65 if not outg:
185 repo.ui.status(_("no changes found\n"))
66 repo.ui.status(_("no changes found\n"))
186 return None, 1
67 return None, 1
187
68
188 if not force and remoteheads != [nullid]:
69 if not force and remoteheads != [nullid]:
189 if remote.capable('branchmap'):
70 if remote.capable('branchmap'):
190 # Check for each named branch if we're creating new remote heads.
71 # Check for each named branch if we're creating new remote heads.
191 # To be a remote head after push, node must be either:
72 # To be a remote head after push, node must be either:
192 # - unknown locally
73 # - unknown locally
193 # - a local outgoing head descended from update
74 # - a local outgoing head descended from update
194 # - a remote head that's known locally and not
75 # - a remote head that's known locally and not
195 # ancestral to an outgoing head
76 # ancestral to an outgoing head
196
77
197 # 1. Create set of branches involved in the push.
78 # 1. Create set of branches involved in the push.
198 branches = set(repo[n].branch() for n in outg)
79 branches = set(repo[n].branch() for n in outg)
199
80
200 # 2. Check for new branches on the remote.
81 # 2. Check for new branches on the remote.
201 remotemap = remote.branchmap()
82 remotemap = remote.branchmap()
202 newbranches = branches - set(remotemap)
83 newbranches = branches - set(remotemap)
203 if newbranches and not newbranch: # new branch requires --new-branch
84 if newbranches and not newbranch: # new branch requires --new-branch
204 branchnames = ', '.join(sorted(newbranches))
85 branchnames = ', '.join(sorted(newbranches))
205 raise util.Abort(_("push creates new remote branches: %s!")
86 raise util.Abort(_("push creates new remote branches: %s!")
206 % branchnames,
87 % branchnames,
207 hint=_("use 'hg push --new-branch' to create"
88 hint=_("use 'hg push --new-branch' to create"
208 " new remote branches"))
89 " new remote branches"))
209 branches.difference_update(newbranches)
90 branches.difference_update(newbranches)
210
91
211 # 3. Construct the initial oldmap and newmap dicts.
92 # 3. Construct the initial oldmap and newmap dicts.
212 # They contain information about the remote heads before and
93 # They contain information about the remote heads before and
213 # after the push, respectively.
94 # after the push, respectively.
214 # Heads not found locally are not included in either dict,
95 # Heads not found locally are not included in either dict,
215 # since they won't be affected by the push.
96 # since they won't be affected by the push.
216 # unsynced contains all branches with incoming changesets.
97 # unsynced contains all branches with incoming changesets.
217 oldmap = {}
98 oldmap = {}
218 newmap = {}
99 newmap = {}
219 unsynced = set()
100 unsynced = set()
220 for branch in branches:
101 for branch in branches:
221 remotebrheads = remotemap[branch]
102 remotebrheads = remotemap[branch]
222 prunedbrheads = [h for h in remotebrheads if h in cl.nodemap]
103 prunedbrheads = [h for h in remotebrheads if h in cl.nodemap]
223 oldmap[branch] = prunedbrheads
104 oldmap[branch] = prunedbrheads
224 newmap[branch] = list(prunedbrheads)
105 newmap[branch] = list(prunedbrheads)
225 if len(remotebrheads) > len(prunedbrheads):
106 if len(remotebrheads) > len(prunedbrheads):
226 unsynced.add(branch)
107 unsynced.add(branch)
227
108
228 # 4. Update newmap with outgoing changes.
109 # 4. Update newmap with outgoing changes.
229 # This will possibly add new heads and remove existing ones.
110 # This will possibly add new heads and remove existing ones.
230 ctxgen = (repo[n] for n in outg)
111 ctxgen = (repo[n] for n in outg)
231 repo._updatebranchcache(newmap, ctxgen)
112 repo._updatebranchcache(newmap, ctxgen)
232
113
233 else:
114 else:
234 # 1-4b. old servers: Check for new topological heads.
115 # 1-4b. old servers: Check for new topological heads.
235 # Construct {old,new}map with branch = None (topological branch).
116 # Construct {old,new}map with branch = None (topological branch).
236 # (code based on _updatebranchcache)
117 # (code based on _updatebranchcache)
237 oldheads = set(h for h in remoteheads if h in cl.nodemap)
118 oldheads = set(h for h in remoteheads if h in cl.nodemap)
238 newheads = oldheads.union(outg)
119 newheads = oldheads.union(outg)
239 if len(newheads) > 1:
120 if len(newheads) > 1:
240 for latest in reversed(outg):
121 for latest in reversed(outg):
241 if latest not in newheads:
122 if latest not in newheads:
242 continue
123 continue
243 minhrev = min(cl.rev(h) for h in newheads)
124 minhrev = min(cl.rev(h) for h in newheads)
244 reachable = cl.reachable(latest, cl.node(minhrev))
125 reachable = cl.reachable(latest, cl.node(minhrev))
245 reachable.remove(latest)
126 reachable.remove(latest)
246 newheads.difference_update(reachable)
127 newheads.difference_update(reachable)
247 branches = set([None])
128 branches = set([None])
248 newmap = {None: newheads}
129 newmap = {None: newheads}
249 oldmap = {None: oldheads}
130 oldmap = {None: oldheads}
250 unsynced = inc and branches or set()
131 unsynced = inc and branches or set()
251
132
252 # 5. Check for new heads.
133 # 5. Check for new heads.
253 # If there are more heads after the push than before, a suitable
134 # If there are more heads after the push than before, a suitable
254 # error message, depending on unsynced status, is displayed.
135 # error message, depending on unsynced status, is displayed.
255 error = None
136 error = None
256 for branch in branches:
137 for branch in branches:
257 newhs = set(newmap[branch])
138 newhs = set(newmap[branch])
258 oldhs = set(oldmap[branch])
139 oldhs = set(oldmap[branch])
259 if len(newhs) > len(oldhs):
140 if len(newhs) > len(oldhs):
260 if error is None:
141 if error is None:
261 if branch:
142 if branch:
262 error = _("push creates new remote heads "
143 error = _("push creates new remote heads "
263 "on branch '%s'!") % branch
144 "on branch '%s'!") % branch
264 else:
145 else:
265 error = _("push creates new remote heads!")
146 error = _("push creates new remote heads!")
266 if branch in unsynced:
147 if branch in unsynced:
267 hint = _("you should pull and merge or "
148 hint = _("you should pull and merge or "
268 "use push -f to force")
149 "use push -f to force")
269 else:
150 else:
270 hint = _("did you forget to merge? "
151 hint = _("did you forget to merge? "
271 "use push -f to force")
152 "use push -f to force")
272 if branch:
153 if branch:
273 repo.ui.debug("new remote heads on branch '%s'\n" % branch)
154 repo.ui.debug("new remote heads on branch '%s'\n" % branch)
274 for h in (newhs - oldhs):
155 for h in (newhs - oldhs):
275 repo.ui.debug("new remote head %s\n" % short(h))
156 repo.ui.debug("new remote head %s\n" % short(h))
276 if error:
157 if error:
277 raise util.Abort(error, hint=hint)
158 raise util.Abort(error, hint=hint)
278
159
279 # 6. Check for unsynced changes on involved branches.
160 # 6. Check for unsynced changes on involved branches.
280 if unsynced:
161 if unsynced:
281 repo.ui.warn(_("note: unsynced remote changes!\n"))
162 repo.ui.warn(_("note: unsynced remote changes!\n"))
282
163
283 if revs is None:
164 if revs is None:
284 # use the fast path, no race possible on push
165 # use the fast path, no race possible on push
285 cg = repo._changegroup(outg, 'push')
166 cg = repo._changegroup(outg, 'push')
286 else:
167 else:
287 cg = repo.getbundle('push', heads=revs, common=common)
168 cg = repo.getbundle('push', heads=revs, common=common)
288 return cg, remoteheads
169 return cg, remoteheads
@@ -1,1265 +1,1270 b''
1 # revlog.py - storage back-end for mercurial
1 # revlog.py - storage back-end for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
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
7
8 """Storage back-end for Mercurial.
8 """Storage back-end for Mercurial.
9
9
10 This provides efficient delta storage with O(1) retrieve and append
10 This provides efficient delta storage with O(1) retrieve and append
11 and O(changes) merge between branches.
11 and O(changes) merge between branches.
12 """
12 """
13
13
14 # import stuff from node for others to import from revlog
14 # import stuff from node for others to import from revlog
15 from node import bin, hex, nullid, nullrev, short #@UnusedImport
15 from node import bin, hex, nullid, nullrev, short #@UnusedImport
16 from i18n import _
16 from i18n import _
17 import ancestor, mdiff, parsers, error, util
17 import ancestor, mdiff, parsers, error, util
18 import struct, zlib, errno
18 import struct, zlib, errno
19
19
20 _pack = struct.pack
20 _pack = struct.pack
21 _unpack = struct.unpack
21 _unpack = struct.unpack
22 _compress = zlib.compress
22 _compress = zlib.compress
23 _decompress = zlib.decompress
23 _decompress = zlib.decompress
24 _sha = util.sha1
24 _sha = util.sha1
25
25
26 # revlog header flags
26 # revlog header flags
27 REVLOGV0 = 0
27 REVLOGV0 = 0
28 REVLOGNG = 1
28 REVLOGNG = 1
29 REVLOGNGINLINEDATA = (1 << 16)
29 REVLOGNGINLINEDATA = (1 << 16)
30 REVLOGSHALLOW = (1 << 17)
30 REVLOGSHALLOW = (1 << 17)
31 REVLOG_DEFAULT_FLAGS = REVLOGNGINLINEDATA
31 REVLOG_DEFAULT_FLAGS = REVLOGNGINLINEDATA
32 REVLOG_DEFAULT_FORMAT = REVLOGNG
32 REVLOG_DEFAULT_FORMAT = REVLOGNG
33 REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
33 REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
34 REVLOGNG_FLAGS = REVLOGNGINLINEDATA | REVLOGSHALLOW
34 REVLOGNG_FLAGS = REVLOGNGINLINEDATA | REVLOGSHALLOW
35
35
36 # revlog index flags
36 # revlog index flags
37 REVIDX_PARENTDELTA = 1
37 REVIDX_PARENTDELTA = 1
38 REVIDX_PUNCHED_FLAG = 2
38 REVIDX_PUNCHED_FLAG = 2
39 REVIDX_KNOWN_FLAGS = REVIDX_PUNCHED_FLAG | REVIDX_PARENTDELTA
39 REVIDX_KNOWN_FLAGS = REVIDX_PUNCHED_FLAG | REVIDX_PARENTDELTA
40
40
41 # max size of revlog with inline data
41 # max size of revlog with inline data
42 _maxinline = 131072
42 _maxinline = 131072
43 _chunksize = 1048576
43 _chunksize = 1048576
44
44
45 RevlogError = error.RevlogError
45 RevlogError = error.RevlogError
46 LookupError = error.LookupError
46 LookupError = error.LookupError
47
47
48 def getoffset(q):
48 def getoffset(q):
49 return int(q >> 16)
49 return int(q >> 16)
50
50
51 def gettype(q):
51 def gettype(q):
52 return int(q & 0xFFFF)
52 return int(q & 0xFFFF)
53
53
54 def offset_type(offset, type):
54 def offset_type(offset, type):
55 return long(long(offset) << 16 | type)
55 return long(long(offset) << 16 | type)
56
56
57 nullhash = _sha(nullid)
57 nullhash = _sha(nullid)
58
58
59 def hash(text, p1, p2):
59 def hash(text, p1, p2):
60 """generate a hash from the given text and its parent hashes
60 """generate a hash from the given text and its parent hashes
61
61
62 This hash combines both the current file contents and its history
62 This hash combines both the current file contents and its history
63 in a manner that makes it easy to distinguish nodes with the same
63 in a manner that makes it easy to distinguish nodes with the same
64 content in the revision graph.
64 content in the revision graph.
65 """
65 """
66 # As of now, if one of the parent node is null, p2 is null
66 # As of now, if one of the parent node is null, p2 is null
67 if p2 == nullid:
67 if p2 == nullid:
68 # deep copy of a hash is faster than creating one
68 # deep copy of a hash is faster than creating one
69 s = nullhash.copy()
69 s = nullhash.copy()
70 s.update(p1)
70 s.update(p1)
71 else:
71 else:
72 # none of the parent nodes are nullid
72 # none of the parent nodes are nullid
73 l = [p1, p2]
73 l = [p1, p2]
74 l.sort()
74 l.sort()
75 s = _sha(l[0])
75 s = _sha(l[0])
76 s.update(l[1])
76 s.update(l[1])
77 s.update(text)
77 s.update(text)
78 return s.digest()
78 return s.digest()
79
79
80 def compress(text):
80 def compress(text):
81 """ generate a possibly-compressed representation of text """
81 """ generate a possibly-compressed representation of text """
82 if not text:
82 if not text:
83 return ("", text)
83 return ("", text)
84 l = len(text)
84 l = len(text)
85 bin = None
85 bin = None
86 if l < 44:
86 if l < 44:
87 pass
87 pass
88 elif l > 1000000:
88 elif l > 1000000:
89 # zlib makes an internal copy, thus doubling memory usage for
89 # zlib makes an internal copy, thus doubling memory usage for
90 # large files, so lets do this in pieces
90 # large files, so lets do this in pieces
91 z = zlib.compressobj()
91 z = zlib.compressobj()
92 p = []
92 p = []
93 pos = 0
93 pos = 0
94 while pos < l:
94 while pos < l:
95 pos2 = pos + 2**20
95 pos2 = pos + 2**20
96 p.append(z.compress(text[pos:pos2]))
96 p.append(z.compress(text[pos:pos2]))
97 pos = pos2
97 pos = pos2
98 p.append(z.flush())
98 p.append(z.flush())
99 if sum(map(len, p)) < l:
99 if sum(map(len, p)) < l:
100 bin = "".join(p)
100 bin = "".join(p)
101 else:
101 else:
102 bin = _compress(text)
102 bin = _compress(text)
103 if bin is None or len(bin) > l:
103 if bin is None or len(bin) > l:
104 if text[0] == '\0':
104 if text[0] == '\0':
105 return ("", text)
105 return ("", text)
106 return ('u', text)
106 return ('u', text)
107 return ("", bin)
107 return ("", bin)
108
108
109 def decompress(bin):
109 def decompress(bin):
110 """ decompress the given input """
110 """ decompress the given input """
111 if not bin:
111 if not bin:
112 return bin
112 return bin
113 t = bin[0]
113 t = bin[0]
114 if t == '\0':
114 if t == '\0':
115 return bin
115 return bin
116 if t == 'x':
116 if t == 'x':
117 return _decompress(bin)
117 return _decompress(bin)
118 if t == 'u':
118 if t == 'u':
119 return bin[1:]
119 return bin[1:]
120 raise RevlogError(_("unknown compression type %r") % t)
120 raise RevlogError(_("unknown compression type %r") % t)
121
121
122 indexformatv0 = ">4l20s20s20s"
122 indexformatv0 = ">4l20s20s20s"
123 v0shaoffset = 56
123 v0shaoffset = 56
124
124
125 class revlogoldio(object):
125 class revlogoldio(object):
126 def __init__(self):
126 def __init__(self):
127 self.size = struct.calcsize(indexformatv0)
127 self.size = struct.calcsize(indexformatv0)
128
128
129 def parseindex(self, data, inline):
129 def parseindex(self, data, inline):
130 s = self.size
130 s = self.size
131 index = []
131 index = []
132 nodemap = {nullid: nullrev}
132 nodemap = {nullid: nullrev}
133 n = off = 0
133 n = off = 0
134 l = len(data)
134 l = len(data)
135 while off + s <= l:
135 while off + s <= l:
136 cur = data[off:off + s]
136 cur = data[off:off + s]
137 off += s
137 off += s
138 e = _unpack(indexformatv0, cur)
138 e = _unpack(indexformatv0, cur)
139 # transform to revlogv1 format
139 # transform to revlogv1 format
140 e2 = (offset_type(e[0], 0), e[1], -1, e[2], e[3],
140 e2 = (offset_type(e[0], 0), e[1], -1, e[2], e[3],
141 nodemap.get(e[4], nullrev), nodemap.get(e[5], nullrev), e[6])
141 nodemap.get(e[4], nullrev), nodemap.get(e[5], nullrev), e[6])
142 index.append(e2)
142 index.append(e2)
143 nodemap[e[6]] = n
143 nodemap[e[6]] = n
144 n += 1
144 n += 1
145
145
146 # add the magic null revision at -1
146 # add the magic null revision at -1
147 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
147 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
148
148
149 return index, nodemap, None
149 return index, nodemap, None
150
150
151 def packentry(self, entry, node, version, rev):
151 def packentry(self, entry, node, version, rev):
152 if gettype(entry[0]):
152 if gettype(entry[0]):
153 raise RevlogError(_("index entry flags need RevlogNG"))
153 raise RevlogError(_("index entry flags need RevlogNG"))
154 e2 = (getoffset(entry[0]), entry[1], entry[3], entry[4],
154 e2 = (getoffset(entry[0]), entry[1], entry[3], entry[4],
155 node(entry[5]), node(entry[6]), entry[7])
155 node(entry[5]), node(entry[6]), entry[7])
156 return _pack(indexformatv0, *e2)
156 return _pack(indexformatv0, *e2)
157
157
158 # index ng:
158 # index ng:
159 # 6 bytes: offset
159 # 6 bytes: offset
160 # 2 bytes: flags
160 # 2 bytes: flags
161 # 4 bytes: compressed length
161 # 4 bytes: compressed length
162 # 4 bytes: uncompressed length
162 # 4 bytes: uncompressed length
163 # 4 bytes: base rev
163 # 4 bytes: base rev
164 # 4 bytes: link rev
164 # 4 bytes: link rev
165 # 4 bytes: parent 1 rev
165 # 4 bytes: parent 1 rev
166 # 4 bytes: parent 2 rev
166 # 4 bytes: parent 2 rev
167 # 32 bytes: nodeid
167 # 32 bytes: nodeid
168 indexformatng = ">Qiiiiii20s12x"
168 indexformatng = ">Qiiiiii20s12x"
169 ngshaoffset = 32
169 ngshaoffset = 32
170 versionformat = ">I"
170 versionformat = ">I"
171
171
172 class revlogio(object):
172 class revlogio(object):
173 def __init__(self):
173 def __init__(self):
174 self.size = struct.calcsize(indexformatng)
174 self.size = struct.calcsize(indexformatng)
175
175
176 def parseindex(self, data, inline):
176 def parseindex(self, data, inline):
177 # call the C implementation to parse the index data
177 # call the C implementation to parse the index data
178 index, cache = parsers.parse_index2(data, inline)
178 index, cache = parsers.parse_index2(data, inline)
179 return index, None, cache
179 return index, None, cache
180
180
181 def packentry(self, entry, node, version, rev):
181 def packentry(self, entry, node, version, rev):
182 p = _pack(indexformatng, *entry)
182 p = _pack(indexformatng, *entry)
183 if rev == 0:
183 if rev == 0:
184 p = _pack(versionformat, version) + p[4:]
184 p = _pack(versionformat, version) + p[4:]
185 return p
185 return p
186
186
187 class revlog(object):
187 class revlog(object):
188 """
188 """
189 the underlying revision storage object
189 the underlying revision storage object
190
190
191 A revlog consists of two parts, an index and the revision data.
191 A revlog consists of two parts, an index and the revision data.
192
192
193 The index is a file with a fixed record size containing
193 The index is a file with a fixed record size containing
194 information on each revision, including its nodeid (hash), the
194 information on each revision, including its nodeid (hash), the
195 nodeids of its parents, the position and offset of its data within
195 nodeids of its parents, the position and offset of its data within
196 the data file, and the revision it's based on. Finally, each entry
196 the data file, and the revision it's based on. Finally, each entry
197 contains a linkrev entry that can serve as a pointer to external
197 contains a linkrev entry that can serve as a pointer to external
198 data.
198 data.
199
199
200 The revision data itself is a linear collection of data chunks.
200 The revision data itself is a linear collection of data chunks.
201 Each chunk represents a revision and is usually represented as a
201 Each chunk represents a revision and is usually represented as a
202 delta against the previous chunk. To bound lookup time, runs of
202 delta against the previous chunk. To bound lookup time, runs of
203 deltas are limited to about 2 times the length of the original
203 deltas are limited to about 2 times the length of the original
204 version data. This makes retrieval of a version proportional to
204 version data. This makes retrieval of a version proportional to
205 its size, or O(1) relative to the number of revisions.
205 its size, or O(1) relative to the number of revisions.
206
206
207 Both pieces of the revlog are written to in an append-only
207 Both pieces of the revlog are written to in an append-only
208 fashion, which means we never need to rewrite a file to insert or
208 fashion, which means we never need to rewrite a file to insert or
209 remove data, and can use some simple techniques to avoid the need
209 remove data, and can use some simple techniques to avoid the need
210 for locking while reading.
210 for locking while reading.
211 """
211 """
212 def __init__(self, opener, indexfile, shallowroot=None):
212 def __init__(self, opener, indexfile, shallowroot=None):
213 """
213 """
214 create a revlog object
214 create a revlog object
215
215
216 opener is a function that abstracts the file opening operation
216 opener is a function that abstracts the file opening operation
217 and can be used to implement COW semantics or the like.
217 and can be used to implement COW semantics or the like.
218 """
218 """
219 self.indexfile = indexfile
219 self.indexfile = indexfile
220 self.datafile = indexfile[:-2] + ".d"
220 self.datafile = indexfile[:-2] + ".d"
221 self.opener = opener
221 self.opener = opener
222 self._cache = None
222 self._cache = None
223 self._chunkcache = (0, '')
223 self._chunkcache = (0, '')
224 self.index = []
224 self.index = []
225 self._shallowroot = shallowroot
225 self._shallowroot = shallowroot
226 self._parentdelta = 0
226 self._parentdelta = 0
227 self._pcache = {}
227 self._pcache = {}
228 self._nodecache = {nullid: nullrev}
228 self._nodecache = {nullid: nullrev}
229 self._nodepos = None
229 self._nodepos = None
230
230
231 v = REVLOG_DEFAULT_VERSION
231 v = REVLOG_DEFAULT_VERSION
232 if hasattr(opener, 'options') and 'defversion' in opener.options:
232 if hasattr(opener, 'options') and 'defversion' in opener.options:
233 v = opener.options['defversion']
233 v = opener.options['defversion']
234 if v & REVLOGNG:
234 if v & REVLOGNG:
235 v |= REVLOGNGINLINEDATA
235 v |= REVLOGNGINLINEDATA
236 if v & REVLOGNG and 'parentdelta' in opener.options:
236 if v & REVLOGNG and 'parentdelta' in opener.options:
237 self._parentdelta = 1
237 self._parentdelta = 1
238
238
239 if shallowroot:
239 if shallowroot:
240 v |= REVLOGSHALLOW
240 v |= REVLOGSHALLOW
241
241
242 i = ''
242 i = ''
243 try:
243 try:
244 f = self.opener(self.indexfile)
244 f = self.opener(self.indexfile)
245 i = f.read()
245 i = f.read()
246 f.close()
246 f.close()
247 if len(i) > 0:
247 if len(i) > 0:
248 v = struct.unpack(versionformat, i[:4])[0]
248 v = struct.unpack(versionformat, i[:4])[0]
249 except IOError, inst:
249 except IOError, inst:
250 if inst.errno != errno.ENOENT:
250 if inst.errno != errno.ENOENT:
251 raise
251 raise
252
252
253 self.version = v
253 self.version = v
254 self._inline = v & REVLOGNGINLINEDATA
254 self._inline = v & REVLOGNGINLINEDATA
255 self._shallow = v & REVLOGSHALLOW
255 self._shallow = v & REVLOGSHALLOW
256 flags = v & ~0xFFFF
256 flags = v & ~0xFFFF
257 fmt = v & 0xFFFF
257 fmt = v & 0xFFFF
258 if fmt == REVLOGV0 and flags:
258 if fmt == REVLOGV0 and flags:
259 raise RevlogError(_("index %s unknown flags %#04x for format v0")
259 raise RevlogError(_("index %s unknown flags %#04x for format v0")
260 % (self.indexfile, flags >> 16))
260 % (self.indexfile, flags >> 16))
261 elif fmt == REVLOGNG and flags & ~REVLOGNG_FLAGS:
261 elif fmt == REVLOGNG and flags & ~REVLOGNG_FLAGS:
262 raise RevlogError(_("index %s unknown flags %#04x for revlogng")
262 raise RevlogError(_("index %s unknown flags %#04x for revlogng")
263 % (self.indexfile, flags >> 16))
263 % (self.indexfile, flags >> 16))
264 elif fmt > REVLOGNG:
264 elif fmt > REVLOGNG:
265 raise RevlogError(_("index %s unknown format %d")
265 raise RevlogError(_("index %s unknown format %d")
266 % (self.indexfile, fmt))
266 % (self.indexfile, fmt))
267
267
268 self._io = revlogio()
268 self._io = revlogio()
269 if self.version == REVLOGV0:
269 if self.version == REVLOGV0:
270 self._io = revlogoldio()
270 self._io = revlogoldio()
271 try:
271 try:
272 d = self._io.parseindex(i, self._inline)
272 d = self._io.parseindex(i, self._inline)
273 except (ValueError, IndexError):
273 except (ValueError, IndexError):
274 raise RevlogError(_("index %s is corrupted") % (self.indexfile))
274 raise RevlogError(_("index %s is corrupted") % (self.indexfile))
275 self.index, nodemap, self._chunkcache = d
275 self.index, nodemap, self._chunkcache = d
276 if nodemap is not None:
276 if nodemap is not None:
277 self.nodemap = self._nodecache = nodemap
277 self.nodemap = self._nodecache = nodemap
278 if not self._chunkcache:
278 if not self._chunkcache:
279 self._chunkclear()
279 self._chunkclear()
280
280
281 def tip(self):
281 def tip(self):
282 return self.node(len(self.index) - 2)
282 return self.node(len(self.index) - 2)
283 def __len__(self):
283 def __len__(self):
284 return len(self.index) - 1
284 return len(self.index) - 1
285 def __iter__(self):
285 def __iter__(self):
286 for i in xrange(len(self)):
286 for i in xrange(len(self)):
287 yield i
287 yield i
288
288
289 @util.propertycache
289 @util.propertycache
290 def nodemap(self):
290 def nodemap(self):
291 self.rev(self.node(0))
291 self.rev(self.node(0))
292 return self._nodecache
292 return self._nodecache
293
293
294 def rev(self, node):
294 def rev(self, node):
295 try:
295 try:
296 return self._nodecache[node]
296 return self._nodecache[node]
297 except KeyError:
297 except KeyError:
298 n = self._nodecache
298 n = self._nodecache
299 i = self.index
299 i = self.index
300 p = self._nodepos
300 p = self._nodepos
301 if p is None:
301 if p is None:
302 p = len(i) - 2
302 p = len(i) - 2
303 for r in xrange(p, -1, -1):
303 for r in xrange(p, -1, -1):
304 v = i[r][7]
304 v = i[r][7]
305 n[v] = r
305 n[v] = r
306 if v == node:
306 if v == node:
307 self._nodepos = r - 1
307 self._nodepos = r - 1
308 return r
308 return r
309 raise LookupError(node, self.indexfile, _('no node'))
309 raise LookupError(node, self.indexfile, _('no node'))
310
310
311 def node(self, rev):
311 def node(self, rev):
312 return self.index[rev][7]
312 return self.index[rev][7]
313 def linkrev(self, rev):
313 def linkrev(self, rev):
314 return self.index[rev][4]
314 return self.index[rev][4]
315 def parents(self, node):
315 def parents(self, node):
316 i = self.index
316 i = self.index
317 d = i[self.rev(node)]
317 d = i[self.rev(node)]
318 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
318 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
319 def parentrevs(self, rev):
319 def parentrevs(self, rev):
320 return self.index[rev][5:7]
320 return self.index[rev][5:7]
321 def start(self, rev):
321 def start(self, rev):
322 return int(self.index[rev][0] >> 16)
322 return int(self.index[rev][0] >> 16)
323 def end(self, rev):
323 def end(self, rev):
324 return self.start(rev) + self.length(rev)
324 return self.start(rev) + self.length(rev)
325 def length(self, rev):
325 def length(self, rev):
326 return self.index[rev][1]
326 return self.index[rev][1]
327 def base(self, rev):
327 def base(self, rev):
328 return self.index[rev][3]
328 return self.index[rev][3]
329 def flags(self, rev):
329 def flags(self, rev):
330 return self.index[rev][0] & 0xFFFF
330 return self.index[rev][0] & 0xFFFF
331 def rawsize(self, rev):
331 def rawsize(self, rev):
332 """return the length of the uncompressed text for a given revision"""
332 """return the length of the uncompressed text for a given revision"""
333 l = self.index[rev][2]
333 l = self.index[rev][2]
334 if l >= 0:
334 if l >= 0:
335 return l
335 return l
336
336
337 t = self.revision(self.node(rev))
337 t = self.revision(self.node(rev))
338 return len(t)
338 return len(t)
339 size = rawsize
339 size = rawsize
340
340
341 def reachable(self, node, stop=None):
341 def reachable(self, node, stop=None):
342 """return the set of all nodes ancestral to a given node, including
342 """return the set of all nodes ancestral to a given node, including
343 the node itself, stopping when stop is matched"""
343 the node itself, stopping when stop is matched"""
344 reachable = set((node,))
344 reachable = set((node,))
345 visit = [node]
345 visit = [node]
346 if stop:
346 if stop:
347 stopn = self.rev(stop)
347 stopn = self.rev(stop)
348 else:
348 else:
349 stopn = 0
349 stopn = 0
350 while visit:
350 while visit:
351 n = visit.pop(0)
351 n = visit.pop(0)
352 if n == stop:
352 if n == stop:
353 continue
353 continue
354 if n == nullid:
354 if n == nullid:
355 continue
355 continue
356 for p in self.parents(n):
356 for p in self.parents(n):
357 if self.rev(p) < stopn:
357 if self.rev(p) < stopn:
358 continue
358 continue
359 if p not in reachable:
359 if p not in reachable:
360 reachable.add(p)
360 reachable.add(p)
361 visit.append(p)
361 visit.append(p)
362 return reachable
362 return reachable
363
363
364 def ancestors(self, *revs):
364 def ancestors(self, *revs):
365 """Generate the ancestors of 'revs' in reverse topological order.
365 """Generate the ancestors of 'revs' in reverse topological order.
366
366
367 Yield a sequence of revision numbers starting with the parents
367 Yield a sequence of revision numbers starting with the parents
368 of each revision in revs, i.e., each revision is *not* considered
368 of each revision in revs, i.e., each revision is *not* considered
369 an ancestor of itself. Results are in breadth-first order:
369 an ancestor of itself. Results are in breadth-first order:
370 parents of each rev in revs, then parents of those, etc. Result
370 parents of each rev in revs, then parents of those, etc. Result
371 does not include the null revision."""
371 does not include the null revision."""
372 visit = list(revs)
372 visit = list(revs)
373 seen = set([nullrev])
373 seen = set([nullrev])
374 while visit:
374 while visit:
375 for parent in self.parentrevs(visit.pop(0)):
375 for parent in self.parentrevs(visit.pop(0)):
376 if parent not in seen:
376 if parent not in seen:
377 visit.append(parent)
377 visit.append(parent)
378 seen.add(parent)
378 seen.add(parent)
379 yield parent
379 yield parent
380
380
381 def descendants(self, *revs):
381 def descendants(self, *revs):
382 """Generate the descendants of 'revs' in revision order.
382 """Generate the descendants of 'revs' in revision order.
383
383
384 Yield a sequence of revision numbers starting with a child of
384 Yield a sequence of revision numbers starting with a child of
385 some rev in revs, i.e., each revision is *not* considered a
385 some rev in revs, i.e., each revision is *not* considered a
386 descendant of itself. Results are ordered by revision number (a
386 descendant of itself. Results are ordered by revision number (a
387 topological sort)."""
387 topological sort)."""
388 first = min(revs)
388 first = min(revs)
389 if first == nullrev:
389 if first == nullrev:
390 for i in self:
390 for i in self:
391 yield i
391 yield i
392 return
392 return
393
393
394 seen = set(revs)
394 seen = set(revs)
395 for i in xrange(first + 1, len(self)):
395 for i in xrange(first + 1, len(self)):
396 for x in self.parentrevs(i):
396 for x in self.parentrevs(i):
397 if x != nullrev and x in seen:
397 if x != nullrev and x in seen:
398 seen.add(i)
398 seen.add(i)
399 yield i
399 yield i
400 break
400 break
401
401
402 def findcommonmissing(self, common=None, heads=None):
402 def findcommonmissing(self, common=None, heads=None):
403 """Return a tuple of the ancestors of common and the ancestors of heads
403 """Return a tuple of the ancestors of common and the ancestors of heads
404 that are not ancestors of common.
404 that are not ancestors of common.
405
405
406 More specifically, the second element is a list of nodes N such that
406 More specifically, the second element is a list of nodes N such that
407 every N satisfies the following constraints:
407 every N satisfies the following constraints:
408
408
409 1. N is an ancestor of some node in 'heads'
409 1. N is an ancestor of some node in 'heads'
410 2. N is not an ancestor of any node in 'common'
410 2. N is not an ancestor of any node in 'common'
411
411
412 The list is sorted by revision number, meaning it is
412 The list is sorted by revision number, meaning it is
413 topologically sorted.
413 topologically sorted.
414
414
415 'heads' and 'common' are both lists of node IDs. If heads is
415 'heads' and 'common' are both lists of node IDs. If heads is
416 not supplied, uses all of the revlog's heads. If common is not
416 not supplied, uses all of the revlog's heads. If common is not
417 supplied, uses nullid."""
417 supplied, uses nullid."""
418 if common is None:
418 if common is None:
419 common = [nullid]
419 common = [nullid]
420 if heads is None:
420 if heads is None:
421 heads = self.heads()
421 heads = self.heads()
422
422
423 common = [self.rev(n) for n in common]
423 common = [self.rev(n) for n in common]
424 heads = [self.rev(n) for n in heads]
424 heads = [self.rev(n) for n in heads]
425
425
426 # we want the ancestors, but inclusive
426 # we want the ancestors, but inclusive
427 has = set(self.ancestors(*common))
427 has = set(self.ancestors(*common))
428 has.add(nullrev)
428 has.add(nullrev)
429 has.update(common)
429 has.update(common)
430
430
431 # take all ancestors from heads that aren't in has
431 # take all ancestors from heads that aren't in has
432 missing = set()
432 missing = set()
433 visit = [r for r in heads if r not in has]
433 visit = [r for r in heads if r not in has]
434 while visit:
434 while visit:
435 r = visit.pop(0)
435 r = visit.pop(0)
436 if r in missing:
436 if r in missing:
437 continue
437 continue
438 else:
438 else:
439 missing.add(r)
439 missing.add(r)
440 for p in self.parentrevs(r):
440 for p in self.parentrevs(r):
441 if p not in has:
441 if p not in has:
442 visit.append(p)
442 visit.append(p)
443 missing = list(missing)
443 missing = list(missing)
444 missing.sort()
444 missing.sort()
445 return has, [self.node(r) for r in missing]
445 return has, [self.node(r) for r in missing]
446
446
447 def findmissing(self, common=None, heads=None):
447 def findmissing(self, common=None, heads=None):
448 """Return the ancestors of heads that are not ancestors of common.
448 """Return the ancestors of heads that are not ancestors of common.
449
449
450 More specifically, return a list of nodes N such that every N
450 More specifically, return a list of nodes N such that every N
451 satisfies the following constraints:
451 satisfies the following constraints:
452
452
453 1. N is an ancestor of some node in 'heads'
453 1. N is an ancestor of some node in 'heads'
454 2. N is not an ancestor of any node in 'common'
454 2. N is not an ancestor of any node in 'common'
455
455
456 The list is sorted by revision number, meaning it is
456 The list is sorted by revision number, meaning it is
457 topologically sorted.
457 topologically sorted.
458
458
459 'heads' and 'common' are both lists of node IDs. If heads is
459 'heads' and 'common' are both lists of node IDs. If heads is
460 not supplied, uses all of the revlog's heads. If common is not
460 not supplied, uses all of the revlog's heads. If common is not
461 supplied, uses nullid."""
461 supplied, uses nullid."""
462 _common, missing = self.findcommonmissing(common, heads)
462 _common, missing = self.findcommonmissing(common, heads)
463 return missing
463 return missing
464
464
465 def nodesbetween(self, roots=None, heads=None):
465 def nodesbetween(self, roots=None, heads=None):
466 """Return a topological path from 'roots' to 'heads'.
466 """Return a topological path from 'roots' to 'heads'.
467
467
468 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
468 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
469 topologically sorted list of all nodes N that satisfy both of
469 topologically sorted list of all nodes N that satisfy both of
470 these constraints:
470 these constraints:
471
471
472 1. N is a descendant of some node in 'roots'
472 1. N is a descendant of some node in 'roots'
473 2. N is an ancestor of some node in 'heads'
473 2. N is an ancestor of some node in 'heads'
474
474
475 Every node is considered to be both a descendant and an ancestor
475 Every node is considered to be both a descendant and an ancestor
476 of itself, so every reachable node in 'roots' and 'heads' will be
476 of itself, so every reachable node in 'roots' and 'heads' will be
477 included in 'nodes'.
477 included in 'nodes'.
478
478
479 'outroots' is the list of reachable nodes in 'roots', i.e., the
479 'outroots' is the list of reachable nodes in 'roots', i.e., the
480 subset of 'roots' that is returned in 'nodes'. Likewise,
480 subset of 'roots' that is returned in 'nodes'. Likewise,
481 'outheads' is the subset of 'heads' that is also in 'nodes'.
481 'outheads' is the subset of 'heads' that is also in 'nodes'.
482
482
483 'roots' and 'heads' are both lists of node IDs. If 'roots' is
483 'roots' and 'heads' are both lists of node IDs. If 'roots' is
484 unspecified, uses nullid as the only root. If 'heads' is
484 unspecified, uses nullid as the only root. If 'heads' is
485 unspecified, uses list of all of the revlog's heads."""
485 unspecified, uses list of all of the revlog's heads."""
486 nonodes = ([], [], [])
486 nonodes = ([], [], [])
487 if roots is not None:
487 if roots is not None:
488 roots = list(roots)
488 roots = list(roots)
489 if not roots:
489 if not roots:
490 return nonodes
490 return nonodes
491 lowestrev = min([self.rev(n) for n in roots])
491 lowestrev = min([self.rev(n) for n in roots])
492 else:
492 else:
493 roots = [nullid] # Everybody's a descendent of nullid
493 roots = [nullid] # Everybody's a descendent of nullid
494 lowestrev = nullrev
494 lowestrev = nullrev
495 if (lowestrev == nullrev) and (heads is None):
495 if (lowestrev == nullrev) and (heads is None):
496 # We want _all_ the nodes!
496 # We want _all_ the nodes!
497 return ([self.node(r) for r in self], [nullid], list(self.heads()))
497 return ([self.node(r) for r in self], [nullid], list(self.heads()))
498 if heads is None:
498 if heads is None:
499 # All nodes are ancestors, so the latest ancestor is the last
499 # All nodes are ancestors, so the latest ancestor is the last
500 # node.
500 # node.
501 highestrev = len(self) - 1
501 highestrev = len(self) - 1
502 # Set ancestors to None to signal that every node is an ancestor.
502 # Set ancestors to None to signal that every node is an ancestor.
503 ancestors = None
503 ancestors = None
504 # Set heads to an empty dictionary for later discovery of heads
504 # Set heads to an empty dictionary for later discovery of heads
505 heads = {}
505 heads = {}
506 else:
506 else:
507 heads = list(heads)
507 heads = list(heads)
508 if not heads:
508 if not heads:
509 return nonodes
509 return nonodes
510 ancestors = set()
510 ancestors = set()
511 # Turn heads into a dictionary so we can remove 'fake' heads.
511 # Turn heads into a dictionary so we can remove 'fake' heads.
512 # Also, later we will be using it to filter out the heads we can't
512 # Also, later we will be using it to filter out the heads we can't
513 # find from roots.
513 # find from roots.
514 heads = dict.fromkeys(heads, 0)
514 heads = dict.fromkeys(heads, 0)
515 # Start at the top and keep marking parents until we're done.
515 # Start at the top and keep marking parents until we're done.
516 nodestotag = set(heads)
516 nodestotag = set(heads)
517 # Remember where the top was so we can use it as a limit later.
517 # Remember where the top was so we can use it as a limit later.
518 highestrev = max([self.rev(n) for n in nodestotag])
518 highestrev = max([self.rev(n) for n in nodestotag])
519 while nodestotag:
519 while nodestotag:
520 # grab a node to tag
520 # grab a node to tag
521 n = nodestotag.pop()
521 n = nodestotag.pop()
522 # Never tag nullid
522 # Never tag nullid
523 if n == nullid:
523 if n == nullid:
524 continue
524 continue
525 # A node's revision number represents its place in a
525 # A node's revision number represents its place in a
526 # topologically sorted list of nodes.
526 # topologically sorted list of nodes.
527 r = self.rev(n)
527 r = self.rev(n)
528 if r >= lowestrev:
528 if r >= lowestrev:
529 if n not in ancestors:
529 if n not in ancestors:
530 # If we are possibly a descendent of one of the roots
530 # If we are possibly a descendent of one of the roots
531 # and we haven't already been marked as an ancestor
531 # and we haven't already been marked as an ancestor
532 ancestors.add(n) # Mark as ancestor
532 ancestors.add(n) # Mark as ancestor
533 # Add non-nullid parents to list of nodes to tag.
533 # Add non-nullid parents to list of nodes to tag.
534 nodestotag.update([p for p in self.parents(n) if
534 nodestotag.update([p for p in self.parents(n) if
535 p != nullid])
535 p != nullid])
536 elif n in heads: # We've seen it before, is it a fake head?
536 elif n in heads: # We've seen it before, is it a fake head?
537 # So it is, real heads should not be the ancestors of
537 # So it is, real heads should not be the ancestors of
538 # any other heads.
538 # any other heads.
539 heads.pop(n)
539 heads.pop(n)
540 if not ancestors:
540 if not ancestors:
541 return nonodes
541 return nonodes
542 # Now that we have our set of ancestors, we want to remove any
542 # Now that we have our set of ancestors, we want to remove any
543 # roots that are not ancestors.
543 # roots that are not ancestors.
544
544
545 # If one of the roots was nullid, everything is included anyway.
545 # If one of the roots was nullid, everything is included anyway.
546 if lowestrev > nullrev:
546 if lowestrev > nullrev:
547 # But, since we weren't, let's recompute the lowest rev to not
547 # But, since we weren't, let's recompute the lowest rev to not
548 # include roots that aren't ancestors.
548 # include roots that aren't ancestors.
549
549
550 # Filter out roots that aren't ancestors of heads
550 # Filter out roots that aren't ancestors of heads
551 roots = [n for n in roots if n in ancestors]
551 roots = [n for n in roots if n in ancestors]
552 # Recompute the lowest revision
552 # Recompute the lowest revision
553 if roots:
553 if roots:
554 lowestrev = min([self.rev(n) for n in roots])
554 lowestrev = min([self.rev(n) for n in roots])
555 else:
555 else:
556 # No more roots? Return empty list
556 # No more roots? Return empty list
557 return nonodes
557 return nonodes
558 else:
558 else:
559 # We are descending from nullid, and don't need to care about
559 # We are descending from nullid, and don't need to care about
560 # any other roots.
560 # any other roots.
561 lowestrev = nullrev
561 lowestrev = nullrev
562 roots = [nullid]
562 roots = [nullid]
563 # Transform our roots list into a set.
563 # Transform our roots list into a set.
564 descendents = set(roots)
564 descendents = set(roots)
565 # Also, keep the original roots so we can filter out roots that aren't
565 # Also, keep the original roots so we can filter out roots that aren't
566 # 'real' roots (i.e. are descended from other roots).
566 # 'real' roots (i.e. are descended from other roots).
567 roots = descendents.copy()
567 roots = descendents.copy()
568 # Our topologically sorted list of output nodes.
568 # Our topologically sorted list of output nodes.
569 orderedout = []
569 orderedout = []
570 # Don't start at nullid since we don't want nullid in our output list,
570 # Don't start at nullid since we don't want nullid in our output list,
571 # and if nullid shows up in descedents, empty parents will look like
571 # and if nullid shows up in descedents, empty parents will look like
572 # they're descendents.
572 # they're descendents.
573 for r in xrange(max(lowestrev, 0), highestrev + 1):
573 for r in xrange(max(lowestrev, 0), highestrev + 1):
574 n = self.node(r)
574 n = self.node(r)
575 isdescendent = False
575 isdescendent = False
576 if lowestrev == nullrev: # Everybody is a descendent of nullid
576 if lowestrev == nullrev: # Everybody is a descendent of nullid
577 isdescendent = True
577 isdescendent = True
578 elif n in descendents:
578 elif n in descendents:
579 # n is already a descendent
579 # n is already a descendent
580 isdescendent = True
580 isdescendent = True
581 # This check only needs to be done here because all the roots
581 # This check only needs to be done here because all the roots
582 # will start being marked is descendents before the loop.
582 # will start being marked is descendents before the loop.
583 if n in roots:
583 if n in roots:
584 # If n was a root, check if it's a 'real' root.
584 # If n was a root, check if it's a 'real' root.
585 p = tuple(self.parents(n))
585 p = tuple(self.parents(n))
586 # If any of its parents are descendents, it's not a root.
586 # If any of its parents are descendents, it's not a root.
587 if (p[0] in descendents) or (p[1] in descendents):
587 if (p[0] in descendents) or (p[1] in descendents):
588 roots.remove(n)
588 roots.remove(n)
589 else:
589 else:
590 p = tuple(self.parents(n))
590 p = tuple(self.parents(n))
591 # A node is a descendent if either of its parents are
591 # A node is a descendent if either of its parents are
592 # descendents. (We seeded the dependents list with the roots
592 # descendents. (We seeded the dependents list with the roots
593 # up there, remember?)
593 # up there, remember?)
594 if (p[0] in descendents) or (p[1] in descendents):
594 if (p[0] in descendents) or (p[1] in descendents):
595 descendents.add(n)
595 descendents.add(n)
596 isdescendent = True
596 isdescendent = True
597 if isdescendent and ((ancestors is None) or (n in ancestors)):
597 if isdescendent and ((ancestors is None) or (n in ancestors)):
598 # Only include nodes that are both descendents and ancestors.
598 # Only include nodes that are both descendents and ancestors.
599 orderedout.append(n)
599 orderedout.append(n)
600 if (ancestors is not None) and (n in heads):
600 if (ancestors is not None) and (n in heads):
601 # We're trying to figure out which heads are reachable
601 # We're trying to figure out which heads are reachable
602 # from roots.
602 # from roots.
603 # Mark this head as having been reached
603 # Mark this head as having been reached
604 heads[n] = 1
604 heads[n] = 1
605 elif ancestors is None:
605 elif ancestors is None:
606 # Otherwise, we're trying to discover the heads.
606 # Otherwise, we're trying to discover the heads.
607 # Assume this is a head because if it isn't, the next step
607 # Assume this is a head because if it isn't, the next step
608 # will eventually remove it.
608 # will eventually remove it.
609 heads[n] = 1
609 heads[n] = 1
610 # But, obviously its parents aren't.
610 # But, obviously its parents aren't.
611 for p in self.parents(n):
611 for p in self.parents(n):
612 heads.pop(p, None)
612 heads.pop(p, None)
613 heads = [n for n in heads.iterkeys() if heads[n] != 0]
613 heads = [n for n in heads.iterkeys() if heads[n] != 0]
614 roots = list(roots)
614 roots = list(roots)
615 assert orderedout
615 assert orderedout
616 assert roots
616 assert roots
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
623 if start is specified, only heads that are descendants of
634 if start is specified, only heads that are descendants of
624 start will be returned
635 start will be returned
625 if stop is specified, it will consider all the revs from stop
636 if stop is specified, it will consider all the revs from stop
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
641 if stop is None:
646 if stop is None:
642 stop = []
647 stop = []
643 stoprevs = set([self.rev(n) for n in stop])
648 stoprevs = set([self.rev(n) for n in stop])
644 startrev = self.rev(start)
649 startrev = self.rev(start)
645 reachable = set((startrev,))
650 reachable = set((startrev,))
646 heads = set((startrev,))
651 heads = set((startrev,))
647
652
648 parentrevs = self.parentrevs
653 parentrevs = self.parentrevs
649 for r in xrange(startrev + 1, len(self)):
654 for r in xrange(startrev + 1, len(self)):
650 for p in parentrevs(r):
655 for p in parentrevs(r):
651 if p in reachable:
656 if p in reachable:
652 if r not in stoprevs:
657 if r not in stoprevs:
653 reachable.add(r)
658 reachable.add(r)
654 heads.add(r)
659 heads.add(r)
655 if p in heads and p not in stoprevs:
660 if p in heads and p not in stoprevs:
656 heads.remove(p)
661 heads.remove(p)
657
662
658 return [self.node(r) for r in heads]
663 return [self.node(r) for r in heads]
659
664
660 def children(self, node):
665 def children(self, node):
661 """find the children of a given node"""
666 """find the children of a given node"""
662 c = []
667 c = []
663 p = self.rev(node)
668 p = self.rev(node)
664 for r in range(p + 1, len(self)):
669 for r in range(p + 1, len(self)):
665 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
670 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
666 if prevs:
671 if prevs:
667 for pr in prevs:
672 for pr in prevs:
668 if pr == p:
673 if pr == p:
669 c.append(self.node(r))
674 c.append(self.node(r))
670 elif p == nullrev:
675 elif p == nullrev:
671 c.append(self.node(r))
676 c.append(self.node(r))
672 return c
677 return c
673
678
674 def descendant(self, start, end):
679 def descendant(self, start, end):
675 if start == nullrev:
680 if start == nullrev:
676 return True
681 return True
677 for i in self.descendants(start):
682 for i in self.descendants(start):
678 if i == end:
683 if i == end:
679 return True
684 return True
680 elif i > end:
685 elif i > end:
681 break
686 break
682 return False
687 return False
683
688
684 def ancestor(self, a, b):
689 def ancestor(self, a, b):
685 """calculate the least common ancestor of nodes a and b"""
690 """calculate the least common ancestor of nodes a and b"""
686
691
687 # fast path, check if it is a descendant
692 # fast path, check if it is a descendant
688 a, b = self.rev(a), self.rev(b)
693 a, b = self.rev(a), self.rev(b)
689 start, end = sorted((a, b))
694 start, end = sorted((a, b))
690 if self.descendant(start, end):
695 if self.descendant(start, end):
691 return self.node(start)
696 return self.node(start)
692
697
693 def parents(rev):
698 def parents(rev):
694 return [p for p in self.parentrevs(rev) if p != nullrev]
699 return [p for p in self.parentrevs(rev) if p != nullrev]
695
700
696 c = ancestor.ancestor(a, b, parents)
701 c = ancestor.ancestor(a, b, parents)
697 if c is None:
702 if c is None:
698 return nullid
703 return nullid
699
704
700 return self.node(c)
705 return self.node(c)
701
706
702 def _match(self, id):
707 def _match(self, id):
703 if isinstance(id, (long, int)):
708 if isinstance(id, (long, int)):
704 # rev
709 # rev
705 return self.node(id)
710 return self.node(id)
706 if len(id) == 20:
711 if len(id) == 20:
707 # possibly a binary node
712 # possibly a binary node
708 # odds of a binary node being all hex in ASCII are 1 in 10**25
713 # odds of a binary node being all hex in ASCII are 1 in 10**25
709 try:
714 try:
710 node = id
715 node = id
711 self.rev(node) # quick search the index
716 self.rev(node) # quick search the index
712 return node
717 return node
713 except LookupError:
718 except LookupError:
714 pass # may be partial hex id
719 pass # may be partial hex id
715 try:
720 try:
716 # str(rev)
721 # str(rev)
717 rev = int(id)
722 rev = int(id)
718 if str(rev) != id:
723 if str(rev) != id:
719 raise ValueError
724 raise ValueError
720 if rev < 0:
725 if rev < 0:
721 rev = len(self) + rev
726 rev = len(self) + rev
722 if rev < 0 or rev >= len(self):
727 if rev < 0 or rev >= len(self):
723 raise ValueError
728 raise ValueError
724 return self.node(rev)
729 return self.node(rev)
725 except (ValueError, OverflowError):
730 except (ValueError, OverflowError):
726 pass
731 pass
727 if len(id) == 40:
732 if len(id) == 40:
728 try:
733 try:
729 # a full hex nodeid?
734 # a full hex nodeid?
730 node = bin(id)
735 node = bin(id)
731 self.rev(node)
736 self.rev(node)
732 return node
737 return node
733 except (TypeError, LookupError):
738 except (TypeError, LookupError):
734 pass
739 pass
735
740
736 def _partialmatch(self, id):
741 def _partialmatch(self, id):
737 if id in self._pcache:
742 if id in self._pcache:
738 return self._pcache[id]
743 return self._pcache[id]
739
744
740 if len(id) < 40:
745 if len(id) < 40:
741 try:
746 try:
742 # hex(node)[:...]
747 # hex(node)[:...]
743 l = len(id) // 2 # grab an even number of digits
748 l = len(id) // 2 # grab an even number of digits
744 prefix = bin(id[:l * 2])
749 prefix = bin(id[:l * 2])
745 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
750 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
746 nl = [n for n in nl if hex(n).startswith(id)]
751 nl = [n for n in nl if hex(n).startswith(id)]
747 if len(nl) > 0:
752 if len(nl) > 0:
748 if len(nl) == 1:
753 if len(nl) == 1:
749 self._pcache[id] = nl[0]
754 self._pcache[id] = nl[0]
750 return nl[0]
755 return nl[0]
751 raise LookupError(id, self.indexfile,
756 raise LookupError(id, self.indexfile,
752 _('ambiguous identifier'))
757 _('ambiguous identifier'))
753 return None
758 return None
754 except TypeError:
759 except TypeError:
755 pass
760 pass
756
761
757 def lookup(self, id):
762 def lookup(self, id):
758 """locate a node based on:
763 """locate a node based on:
759 - revision number or str(revision number)
764 - revision number or str(revision number)
760 - nodeid or subset of hex nodeid
765 - nodeid or subset of hex nodeid
761 """
766 """
762 n = self._match(id)
767 n = self._match(id)
763 if n is not None:
768 if n is not None:
764 return n
769 return n
765 n = self._partialmatch(id)
770 n = self._partialmatch(id)
766 if n:
771 if n:
767 return n
772 return n
768
773
769 raise LookupError(id, self.indexfile, _('no match found'))
774 raise LookupError(id, self.indexfile, _('no match found'))
770
775
771 def cmp(self, node, text):
776 def cmp(self, node, text):
772 """compare text with a given file revision
777 """compare text with a given file revision
773
778
774 returns True if text is different than what is stored.
779 returns True if text is different than what is stored.
775 """
780 """
776 p1, p2 = self.parents(node)
781 p1, p2 = self.parents(node)
777 return hash(text, p1, p2) != node
782 return hash(text, p1, p2) != node
778
783
779 def _addchunk(self, offset, data):
784 def _addchunk(self, offset, data):
780 o, d = self._chunkcache
785 o, d = self._chunkcache
781 # try to add to existing cache
786 # try to add to existing cache
782 if o + len(d) == offset and len(d) + len(data) < _chunksize:
787 if o + len(d) == offset and len(d) + len(data) < _chunksize:
783 self._chunkcache = o, d + data
788 self._chunkcache = o, d + data
784 else:
789 else:
785 self._chunkcache = offset, data
790 self._chunkcache = offset, data
786
791
787 def _loadchunk(self, offset, length):
792 def _loadchunk(self, offset, length):
788 if self._inline:
793 if self._inline:
789 df = self.opener(self.indexfile)
794 df = self.opener(self.indexfile)
790 else:
795 else:
791 df = self.opener(self.datafile)
796 df = self.opener(self.datafile)
792
797
793 readahead = max(65536, length)
798 readahead = max(65536, length)
794 df.seek(offset)
799 df.seek(offset)
795 d = df.read(readahead)
800 d = df.read(readahead)
796 self._addchunk(offset, d)
801 self._addchunk(offset, d)
797 if readahead > length:
802 if readahead > length:
798 return d[:length]
803 return d[:length]
799 return d
804 return d
800
805
801 def _getchunk(self, offset, length):
806 def _getchunk(self, offset, length):
802 o, d = self._chunkcache
807 o, d = self._chunkcache
803 l = len(d)
808 l = len(d)
804
809
805 # is it in the cache?
810 # is it in the cache?
806 cachestart = offset - o
811 cachestart = offset - o
807 cacheend = cachestart + length
812 cacheend = cachestart + length
808 if cachestart >= 0 and cacheend <= l:
813 if cachestart >= 0 and cacheend <= l:
809 if cachestart == 0 and cacheend == l:
814 if cachestart == 0 and cacheend == l:
810 return d # avoid a copy
815 return d # avoid a copy
811 return d[cachestart:cacheend]
816 return d[cachestart:cacheend]
812
817
813 return self._loadchunk(offset, length)
818 return self._loadchunk(offset, length)
814
819
815 def _chunkraw(self, startrev, endrev):
820 def _chunkraw(self, startrev, endrev):
816 start = self.start(startrev)
821 start = self.start(startrev)
817 length = self.end(endrev) - start
822 length = self.end(endrev) - start
818 if self._inline:
823 if self._inline:
819 start += (startrev + 1) * self._io.size
824 start += (startrev + 1) * self._io.size
820 return self._getchunk(start, length)
825 return self._getchunk(start, length)
821
826
822 def _chunk(self, rev):
827 def _chunk(self, rev):
823 return decompress(self._chunkraw(rev, rev))
828 return decompress(self._chunkraw(rev, rev))
824
829
825 def _chunkbase(self, rev):
830 def _chunkbase(self, rev):
826 return self._chunk(rev)
831 return self._chunk(rev)
827
832
828 def _chunkclear(self):
833 def _chunkclear(self):
829 self._chunkcache = (0, '')
834 self._chunkcache = (0, '')
830
835
831 def deltaparent(self, rev):
836 def deltaparent(self, rev):
832 """return previous revision or parentrev according to flags"""
837 """return previous revision or parentrev according to flags"""
833 if self.flags(rev) & REVIDX_PARENTDELTA:
838 if self.flags(rev) & REVIDX_PARENTDELTA:
834 return self.parentrevs(rev)[0]
839 return self.parentrevs(rev)[0]
835 else:
840 else:
836 return rev - 1
841 return rev - 1
837
842
838 def revdiff(self, rev1, rev2):
843 def revdiff(self, rev1, rev2):
839 """return or calculate a delta between two revisions"""
844 """return or calculate a delta between two revisions"""
840 if self.base(rev2) != rev2 and self.deltaparent(rev2) == rev1:
845 if self.base(rev2) != rev2 and self.deltaparent(rev2) == rev1:
841 return self._chunk(rev2)
846 return self._chunk(rev2)
842
847
843 return mdiff.textdiff(self.revision(self.node(rev1)),
848 return mdiff.textdiff(self.revision(self.node(rev1)),
844 self.revision(self.node(rev2)))
849 self.revision(self.node(rev2)))
845
850
846 def revision(self, node):
851 def revision(self, node):
847 """return an uncompressed revision of a given node"""
852 """return an uncompressed revision of a given node"""
848 cachedrev = None
853 cachedrev = None
849 if node == nullid:
854 if node == nullid:
850 return ""
855 return ""
851 if self._cache:
856 if self._cache:
852 if self._cache[0] == node:
857 if self._cache[0] == node:
853 return self._cache[2]
858 return self._cache[2]
854 cachedrev = self._cache[1]
859 cachedrev = self._cache[1]
855
860
856 # look up what we need to read
861 # look up what we need to read
857 text = None
862 text = None
858 rev = self.rev(node)
863 rev = self.rev(node)
859 base = self.base(rev)
864 base = self.base(rev)
860
865
861 # check rev flags
866 # check rev flags
862 if self.flags(rev) & ~REVIDX_KNOWN_FLAGS:
867 if self.flags(rev) & ~REVIDX_KNOWN_FLAGS:
863 raise RevlogError(_('incompatible revision flag %x') %
868 raise RevlogError(_('incompatible revision flag %x') %
864 (self.flags(rev) & ~REVIDX_KNOWN_FLAGS))
869 (self.flags(rev) & ~REVIDX_KNOWN_FLAGS))
865
870
866 # build delta chain
871 # build delta chain
867 chain = []
872 chain = []
868 index = self.index # for performance
873 index = self.index # for performance
869 iterrev = rev
874 iterrev = rev
870 e = index[iterrev]
875 e = index[iterrev]
871 while iterrev != base and iterrev != cachedrev:
876 while iterrev != base and iterrev != cachedrev:
872 chain.append(iterrev)
877 chain.append(iterrev)
873 if e[0] & REVIDX_PARENTDELTA:
878 if e[0] & REVIDX_PARENTDELTA:
874 iterrev = e[5]
879 iterrev = e[5]
875 else:
880 else:
876 iterrev -= 1
881 iterrev -= 1
877 e = index[iterrev]
882 e = index[iterrev]
878 chain.reverse()
883 chain.reverse()
879 base = iterrev
884 base = iterrev
880
885
881 if iterrev == cachedrev:
886 if iterrev == cachedrev:
882 # cache hit
887 # cache hit
883 text = self._cache[2]
888 text = self._cache[2]
884
889
885 # drop cache to save memory
890 # drop cache to save memory
886 self._cache = None
891 self._cache = None
887
892
888 self._chunkraw(base, rev)
893 self._chunkraw(base, rev)
889 if text is None:
894 if text is None:
890 text = self._chunkbase(base)
895 text = self._chunkbase(base)
891
896
892 bins = [self._chunk(r) for r in chain]
897 bins = [self._chunk(r) for r in chain]
893 text = mdiff.patches(text, bins)
898 text = mdiff.patches(text, bins)
894
899
895 text = self._checkhash(text, node, rev)
900 text = self._checkhash(text, node, rev)
896
901
897 self._cache = (node, rev, text)
902 self._cache = (node, rev, text)
898 return text
903 return text
899
904
900 def _checkhash(self, text, node, rev):
905 def _checkhash(self, text, node, rev):
901 p1, p2 = self.parents(node)
906 p1, p2 = self.parents(node)
902 if (node != hash(text, p1, p2) and
907 if (node != hash(text, p1, p2) and
903 not (self.flags(rev) & REVIDX_PUNCHED_FLAG)):
908 not (self.flags(rev) & REVIDX_PUNCHED_FLAG)):
904 raise RevlogError(_("integrity check failed on %s:%d")
909 raise RevlogError(_("integrity check failed on %s:%d")
905 % (self.indexfile, rev))
910 % (self.indexfile, rev))
906 return text
911 return text
907
912
908 def checkinlinesize(self, tr, fp=None):
913 def checkinlinesize(self, tr, fp=None):
909 if not self._inline or (self.start(-2) + self.length(-2)) < _maxinline:
914 if not self._inline or (self.start(-2) + self.length(-2)) < _maxinline:
910 return
915 return
911
916
912 trinfo = tr.find(self.indexfile)
917 trinfo = tr.find(self.indexfile)
913 if trinfo is None:
918 if trinfo is None:
914 raise RevlogError(_("%s not found in the transaction")
919 raise RevlogError(_("%s not found in the transaction")
915 % self.indexfile)
920 % self.indexfile)
916
921
917 trindex = trinfo[2]
922 trindex = trinfo[2]
918 dataoff = self.start(trindex)
923 dataoff = self.start(trindex)
919
924
920 tr.add(self.datafile, dataoff)
925 tr.add(self.datafile, dataoff)
921
926
922 if fp:
927 if fp:
923 fp.flush()
928 fp.flush()
924 fp.close()
929 fp.close()
925
930
926 df = self.opener(self.datafile, 'w')
931 df = self.opener(self.datafile, 'w')
927 try:
932 try:
928 for r in self:
933 for r in self:
929 df.write(self._chunkraw(r, r))
934 df.write(self._chunkraw(r, r))
930 finally:
935 finally:
931 df.close()
936 df.close()
932
937
933 fp = self.opener(self.indexfile, 'w', atomictemp=True)
938 fp = self.opener(self.indexfile, 'w', atomictemp=True)
934 self.version &= ~(REVLOGNGINLINEDATA)
939 self.version &= ~(REVLOGNGINLINEDATA)
935 self._inline = False
940 self._inline = False
936 for i in self:
941 for i in self:
937 e = self._io.packentry(self.index[i], self.node, self.version, i)
942 e = self._io.packentry(self.index[i], self.node, self.version, i)
938 fp.write(e)
943 fp.write(e)
939
944
940 # if we don't call rename, the temp file will never replace the
945 # if we don't call rename, the temp file will never replace the
941 # real index
946 # real index
942 fp.rename()
947 fp.rename()
943
948
944 tr.replace(self.indexfile, trindex * self._io.size)
949 tr.replace(self.indexfile, trindex * self._io.size)
945 self._chunkclear()
950 self._chunkclear()
946
951
947 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None):
952 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None):
948 """add a revision to the log
953 """add a revision to the log
949
954
950 text - the revision data to add
955 text - the revision data to add
951 transaction - the transaction object used for rollback
956 transaction - the transaction object used for rollback
952 link - the linkrev data to add
957 link - the linkrev data to add
953 p1, p2 - the parent nodeids of the revision
958 p1, p2 - the parent nodeids of the revision
954 cachedelta - an optional precomputed delta
959 cachedelta - an optional precomputed delta
955 """
960 """
956 node = hash(text, p1, p2)
961 node = hash(text, p1, p2)
957 if (node in self.nodemap and
962 if (node in self.nodemap and
958 (not self.flags(self.rev(node)) & REVIDX_PUNCHED_FLAG)):
963 (not self.flags(self.rev(node)) & REVIDX_PUNCHED_FLAG)):
959 return node
964 return node
960
965
961 dfh = None
966 dfh = None
962 if not self._inline:
967 if not self._inline:
963 dfh = self.opener(self.datafile, "a")
968 dfh = self.opener(self.datafile, "a")
964 ifh = self.opener(self.indexfile, "a+")
969 ifh = self.opener(self.indexfile, "a+")
965 try:
970 try:
966 return self._addrevision(node, text, transaction, link, p1, p2,
971 return self._addrevision(node, text, transaction, link, p1, p2,
967 cachedelta, ifh, dfh)
972 cachedelta, ifh, dfh)
968 finally:
973 finally:
969 if dfh:
974 if dfh:
970 dfh.close()
975 dfh.close()
971 ifh.close()
976 ifh.close()
972
977
973 def _addrevision(self, node, text, transaction, link, p1, p2,
978 def _addrevision(self, node, text, transaction, link, p1, p2,
974 cachedelta, ifh, dfh):
979 cachedelta, ifh, dfh):
975
980
976 btext = [text]
981 btext = [text]
977 def buildtext():
982 def buildtext():
978 if btext[0] is not None:
983 if btext[0] is not None:
979 return btext[0]
984 return btext[0]
980 # flush any pending writes here so we can read it in revision
985 # flush any pending writes here so we can read it in revision
981 if dfh:
986 if dfh:
982 dfh.flush()
987 dfh.flush()
983 ifh.flush()
988 ifh.flush()
984 basetext = self.revision(self.node(cachedelta[0]))
989 basetext = self.revision(self.node(cachedelta[0]))
985 btext[0] = mdiff.patch(basetext, cachedelta[1])
990 btext[0] = mdiff.patch(basetext, cachedelta[1])
986 chk = hash(btext[0], p1, p2)
991 chk = hash(btext[0], p1, p2)
987 if chk != node:
992 if chk != node:
988 raise RevlogError(_("consistency error in delta"))
993 raise RevlogError(_("consistency error in delta"))
989 return btext[0]
994 return btext[0]
990
995
991 def builddelta(rev):
996 def builddelta(rev):
992 # can we use the cached delta?
997 # can we use the cached delta?
993 if cachedelta and cachedelta[0] == rev:
998 if cachedelta and cachedelta[0] == rev:
994 delta = cachedelta[1]
999 delta = cachedelta[1]
995 else:
1000 else:
996 t = buildtext()
1001 t = buildtext()
997 ptext = self.revision(self.node(rev))
1002 ptext = self.revision(self.node(rev))
998 delta = mdiff.textdiff(ptext, t)
1003 delta = mdiff.textdiff(ptext, t)
999 data = compress(delta)
1004 data = compress(delta)
1000 l = len(data[1]) + len(data[0])
1005 l = len(data[1]) + len(data[0])
1001 base = self.base(rev)
1006 base = self.base(rev)
1002 dist = l + offset - self.start(base)
1007 dist = l + offset - self.start(base)
1003 return dist, l, data, base
1008 return dist, l, data, base
1004
1009
1005 curr = len(self)
1010 curr = len(self)
1006 prev = curr - 1
1011 prev = curr - 1
1007 base = curr
1012 base = curr
1008 offset = self.end(prev)
1013 offset = self.end(prev)
1009 flags = 0
1014 flags = 0
1010 d = None
1015 d = None
1011 p1r, p2r = self.rev(p1), self.rev(p2)
1016 p1r, p2r = self.rev(p1), self.rev(p2)
1012
1017
1013 # should we try to build a delta?
1018 # should we try to build a delta?
1014 if prev != nullrev:
1019 if prev != nullrev:
1015 d = builddelta(prev)
1020 d = builddelta(prev)
1016 if self._parentdelta and prev != p1r:
1021 if self._parentdelta and prev != p1r:
1017 d2 = builddelta(p1r)
1022 d2 = builddelta(p1r)
1018 if d2 < d:
1023 if d2 < d:
1019 d = d2
1024 d = d2
1020 flags = REVIDX_PARENTDELTA
1025 flags = REVIDX_PARENTDELTA
1021 dist, l, data, base = d
1026 dist, l, data, base = d
1022
1027
1023 # full versions are inserted when the needed deltas
1028 # full versions are inserted when the needed deltas
1024 # become comparable to the uncompressed text
1029 # become comparable to the uncompressed text
1025 # or the base revision is punched
1030 # or the base revision is punched
1026 if text is None:
1031 if text is None:
1027 textlen = mdiff.patchedsize(self.rawsize(cachedelta[0]),
1032 textlen = mdiff.patchedsize(self.rawsize(cachedelta[0]),
1028 cachedelta[1])
1033 cachedelta[1])
1029 else:
1034 else:
1030 textlen = len(text)
1035 textlen = len(text)
1031 if (d is None or dist > textlen * 2 or
1036 if (d is None or dist > textlen * 2 or
1032 (self.flags(base) & REVIDX_PUNCHED_FLAG)):
1037 (self.flags(base) & REVIDX_PUNCHED_FLAG)):
1033 text = buildtext()
1038 text = buildtext()
1034 data = compress(text)
1039 data = compress(text)
1035 l = len(data[1]) + len(data[0])
1040 l = len(data[1]) + len(data[0])
1036 base = curr
1041 base = curr
1037
1042
1038 e = (offset_type(offset, flags), l, textlen,
1043 e = (offset_type(offset, flags), l, textlen,
1039 base, link, p1r, p2r, node)
1044 base, link, p1r, p2r, node)
1040 self.index.insert(-1, e)
1045 self.index.insert(-1, e)
1041 self.nodemap[node] = curr
1046 self.nodemap[node] = curr
1042
1047
1043 entry = self._io.packentry(e, self.node, self.version, curr)
1048 entry = self._io.packentry(e, self.node, self.version, curr)
1044 if not self._inline:
1049 if not self._inline:
1045 transaction.add(self.datafile, offset)
1050 transaction.add(self.datafile, offset)
1046 transaction.add(self.indexfile, curr * len(entry))
1051 transaction.add(self.indexfile, curr * len(entry))
1047 if data[0]:
1052 if data[0]:
1048 dfh.write(data[0])
1053 dfh.write(data[0])
1049 dfh.write(data[1])
1054 dfh.write(data[1])
1050 dfh.flush()
1055 dfh.flush()
1051 ifh.write(entry)
1056 ifh.write(entry)
1052 else:
1057 else:
1053 offset += curr * self._io.size
1058 offset += curr * self._io.size
1054 transaction.add(self.indexfile, offset, curr)
1059 transaction.add(self.indexfile, offset, curr)
1055 ifh.write(entry)
1060 ifh.write(entry)
1056 ifh.write(data[0])
1061 ifh.write(data[0])
1057 ifh.write(data[1])
1062 ifh.write(data[1])
1058 self.checkinlinesize(transaction, ifh)
1063 self.checkinlinesize(transaction, ifh)
1059
1064
1060 if type(text) == str: # only accept immutable objects
1065 if type(text) == str: # only accept immutable objects
1061 self._cache = (node, curr, text)
1066 self._cache = (node, curr, text)
1062 return node
1067 return node
1063
1068
1064 def group(self, nodelist, bundler):
1069 def group(self, nodelist, bundler):
1065 """Calculate a delta group, yielding a sequence of changegroup chunks
1070 """Calculate a delta group, yielding a sequence of changegroup chunks
1066 (strings).
1071 (strings).
1067
1072
1068 Given a list of changeset revs, return a set of deltas and
1073 Given a list of changeset revs, return a set of deltas and
1069 metadata corresponding to nodes. The first delta is
1074 metadata corresponding to nodes. The first delta is
1070 first parent(nodelist[0]) -> nodelist[0], the receiver is
1075 first parent(nodelist[0]) -> nodelist[0], the receiver is
1071 guaranteed to have this parent as it has all history before
1076 guaranteed to have this parent as it has all history before
1072 these changesets. In the case firstparent is nullrev the
1077 these changesets. In the case firstparent is nullrev the
1073 changegroup starts with a full revision.
1078 changegroup starts with a full revision.
1074 """
1079 """
1075
1080
1076 revs = sorted([self.rev(n) for n in nodelist])
1081 revs = sorted([self.rev(n) for n in nodelist])
1077
1082
1078 # if we don't have any revisions touched by these changesets, bail
1083 # if we don't have any revisions touched by these changesets, bail
1079 if not revs:
1084 if not revs:
1080 yield bundler.close()
1085 yield bundler.close()
1081 return
1086 return
1082
1087
1083 # add the parent of the first rev
1088 # add the parent of the first rev
1084 p = self.parentrevs(revs[0])[0]
1089 p = self.parentrevs(revs[0])[0]
1085 revs.insert(0, p)
1090 revs.insert(0, p)
1086
1091
1087 # build deltas
1092 # build deltas
1088 for r in xrange(len(revs) - 1):
1093 for r in xrange(len(revs) - 1):
1089 prev, curr = revs[r], revs[r + 1]
1094 prev, curr = revs[r], revs[r + 1]
1090 for c in bundler.revchunk(self, curr, prev):
1095 for c in bundler.revchunk(self, curr, prev):
1091 yield c
1096 yield c
1092
1097
1093 yield bundler.close()
1098 yield bundler.close()
1094
1099
1095 def addgroup(self, bundle, linkmapper, transaction):
1100 def addgroup(self, bundle, linkmapper, transaction):
1096 """
1101 """
1097 add a delta group
1102 add a delta group
1098
1103
1099 given a set of deltas, add them to the revision log. the
1104 given a set of deltas, add them to the revision log. the
1100 first delta is against its parent, which should be in our
1105 first delta is against its parent, which should be in our
1101 log, the rest are against the previous delta.
1106 log, the rest are against the previous delta.
1102 """
1107 """
1103
1108
1104 # track the base of the current delta log
1109 # track the base of the current delta log
1105 node = None
1110 node = None
1106
1111
1107 r = len(self)
1112 r = len(self)
1108 end = 0
1113 end = 0
1109 if r:
1114 if r:
1110 end = self.end(r - 1)
1115 end = self.end(r - 1)
1111 ifh = self.opener(self.indexfile, "a+")
1116 ifh = self.opener(self.indexfile, "a+")
1112 isize = r * self._io.size
1117 isize = r * self._io.size
1113 if self._inline:
1118 if self._inline:
1114 transaction.add(self.indexfile, end + isize, r)
1119 transaction.add(self.indexfile, end + isize, r)
1115 dfh = None
1120 dfh = None
1116 else:
1121 else:
1117 transaction.add(self.indexfile, isize, r)
1122 transaction.add(self.indexfile, isize, r)
1118 transaction.add(self.datafile, end)
1123 transaction.add(self.datafile, end)
1119 dfh = self.opener(self.datafile, "a")
1124 dfh = self.opener(self.datafile, "a")
1120
1125
1121 try:
1126 try:
1122 # loop through our set of deltas
1127 # loop through our set of deltas
1123 chain = None
1128 chain = None
1124 while 1:
1129 while 1:
1125 chunkdata = bundle.deltachunk(chain)
1130 chunkdata = bundle.deltachunk(chain)
1126 if not chunkdata:
1131 if not chunkdata:
1127 break
1132 break
1128 node = chunkdata['node']
1133 node = chunkdata['node']
1129 p1 = chunkdata['p1']
1134 p1 = chunkdata['p1']
1130 p2 = chunkdata['p2']
1135 p2 = chunkdata['p2']
1131 cs = chunkdata['cs']
1136 cs = chunkdata['cs']
1132 deltabase = chunkdata['deltabase']
1137 deltabase = chunkdata['deltabase']
1133 delta = chunkdata['delta']
1138 delta = chunkdata['delta']
1134
1139
1135 link = linkmapper(cs)
1140 link = linkmapper(cs)
1136 if (node in self.nodemap and
1141 if (node in self.nodemap and
1137 (not self.flags(self.rev(node)) & REVIDX_PUNCHED_FLAG)):
1142 (not self.flags(self.rev(node)) & REVIDX_PUNCHED_FLAG)):
1138 # this can happen if two branches make the same change
1143 # this can happen if two branches make the same change
1139 chain = node
1144 chain = node
1140 continue
1145 continue
1141
1146
1142 for p in (p1, p2):
1147 for p in (p1, p2):
1143 if not p in self.nodemap:
1148 if not p in self.nodemap:
1144 if self._shallow:
1149 if self._shallow:
1145 # add null entries for missing parents
1150 # add null entries for missing parents
1146 # XXX FIXME
1151 # XXX FIXME
1147 #if base == nullrev:
1152 #if base == nullrev:
1148 # base = len(self)
1153 # base = len(self)
1149 #e = (offset_type(end, REVIDX_PUNCHED_FLAG),
1154 #e = (offset_type(end, REVIDX_PUNCHED_FLAG),
1150 # 0, 0, base, nullrev, nullrev, nullrev, p)
1155 # 0, 0, base, nullrev, nullrev, nullrev, p)
1151 #self.index.insert(-1, e)
1156 #self.index.insert(-1, e)
1152 #self.nodemap[p] = r
1157 #self.nodemap[p] = r
1153 #entry = self._io.packentry(e, self.node,
1158 #entry = self._io.packentry(e, self.node,
1154 # self.version, r)
1159 # self.version, r)
1155 #ifh.write(entry)
1160 #ifh.write(entry)
1156 #t, r = r, r + 1
1161 #t, r = r, r + 1
1157 raise LookupError(p, self.indexfile,
1162 raise LookupError(p, self.indexfile,
1158 _('unknown parent'))
1163 _('unknown parent'))
1159 else:
1164 else:
1160 raise LookupError(p, self.indexfile,
1165 raise LookupError(p, self.indexfile,
1161 _('unknown parent'))
1166 _('unknown parent'))
1162
1167
1163 if deltabase not in self.nodemap:
1168 if deltabase not in self.nodemap:
1164 raise LookupError(deltabase, self.indexfile,
1169 raise LookupError(deltabase, self.indexfile,
1165 _('unknown delta base'))
1170 _('unknown delta base'))
1166
1171
1167 baserev = self.rev(deltabase)
1172 baserev = self.rev(deltabase)
1168 chain = self._addrevision(node, None, transaction, link,
1173 chain = self._addrevision(node, None, transaction, link,
1169 p1, p2, (baserev, delta), ifh, dfh)
1174 p1, p2, (baserev, delta), ifh, dfh)
1170 if not dfh and not self._inline:
1175 if not dfh and not self._inline:
1171 # addrevision switched from inline to conventional
1176 # addrevision switched from inline to conventional
1172 # reopen the index
1177 # reopen the index
1173 ifh.close()
1178 ifh.close()
1174 dfh = self.opener(self.datafile, "a")
1179 dfh = self.opener(self.datafile, "a")
1175 ifh = self.opener(self.indexfile, "a")
1180 ifh = self.opener(self.indexfile, "a")
1176 finally:
1181 finally:
1177 if dfh:
1182 if dfh:
1178 dfh.close()
1183 dfh.close()
1179 ifh.close()
1184 ifh.close()
1180
1185
1181 return node
1186 return node
1182
1187
1183 def strip(self, minlink, transaction):
1188 def strip(self, minlink, transaction):
1184 """truncate the revlog on the first revision with a linkrev >= minlink
1189 """truncate the revlog on the first revision with a linkrev >= minlink
1185
1190
1186 This function is called when we're stripping revision minlink and
1191 This function is called when we're stripping revision minlink and
1187 its descendants from the repository.
1192 its descendants from the repository.
1188
1193
1189 We have to remove all revisions with linkrev >= minlink, because
1194 We have to remove all revisions with linkrev >= minlink, because
1190 the equivalent changelog revisions will be renumbered after the
1195 the equivalent changelog revisions will be renumbered after the
1191 strip.
1196 strip.
1192
1197
1193 So we truncate the revlog on the first of these revisions, and
1198 So we truncate the revlog on the first of these revisions, and
1194 trust that the caller has saved the revisions that shouldn't be
1199 trust that the caller has saved the revisions that shouldn't be
1195 removed and that it'll readd them after this truncation.
1200 removed and that it'll readd them after this truncation.
1196 """
1201 """
1197 if len(self) == 0:
1202 if len(self) == 0:
1198 return
1203 return
1199
1204
1200 for rev in self:
1205 for rev in self:
1201 if self.index[rev][4] >= minlink:
1206 if self.index[rev][4] >= minlink:
1202 break
1207 break
1203 else:
1208 else:
1204 return
1209 return
1205
1210
1206 # first truncate the files on disk
1211 # first truncate the files on disk
1207 end = self.start(rev)
1212 end = self.start(rev)
1208 if not self._inline:
1213 if not self._inline:
1209 transaction.add(self.datafile, end)
1214 transaction.add(self.datafile, end)
1210 end = rev * self._io.size
1215 end = rev * self._io.size
1211 else:
1216 else:
1212 end += rev * self._io.size
1217 end += rev * self._io.size
1213
1218
1214 transaction.add(self.indexfile, end)
1219 transaction.add(self.indexfile, end)
1215
1220
1216 # then reset internal state in memory to forget those revisions
1221 # then reset internal state in memory to forget those revisions
1217 self._cache = None
1222 self._cache = None
1218 self._chunkclear()
1223 self._chunkclear()
1219 for x in xrange(rev, len(self)):
1224 for x in xrange(rev, len(self)):
1220 del self.nodemap[self.node(x)]
1225 del self.nodemap[self.node(x)]
1221
1226
1222 del self.index[rev:-1]
1227 del self.index[rev:-1]
1223
1228
1224 def checksize(self):
1229 def checksize(self):
1225 expected = 0
1230 expected = 0
1226 if len(self):
1231 if len(self):
1227 expected = max(0, self.end(len(self) - 1))
1232 expected = max(0, self.end(len(self) - 1))
1228
1233
1229 try:
1234 try:
1230 f = self.opener(self.datafile)
1235 f = self.opener(self.datafile)
1231 f.seek(0, 2)
1236 f.seek(0, 2)
1232 actual = f.tell()
1237 actual = f.tell()
1233 f.close()
1238 f.close()
1234 dd = actual - expected
1239 dd = actual - expected
1235 except IOError, inst:
1240 except IOError, inst:
1236 if inst.errno != errno.ENOENT:
1241 if inst.errno != errno.ENOENT:
1237 raise
1242 raise
1238 dd = 0
1243 dd = 0
1239
1244
1240 try:
1245 try:
1241 f = self.opener(self.indexfile)
1246 f = self.opener(self.indexfile)
1242 f.seek(0, 2)
1247 f.seek(0, 2)
1243 actual = f.tell()
1248 actual = f.tell()
1244 f.close()
1249 f.close()
1245 s = self._io.size
1250 s = self._io.size
1246 i = max(0, actual // s)
1251 i = max(0, actual // s)
1247 di = actual - (i * s)
1252 di = actual - (i * s)
1248 if self._inline:
1253 if self._inline:
1249 databytes = 0
1254 databytes = 0
1250 for r in self:
1255 for r in self:
1251 databytes += max(0, self.length(r))
1256 databytes += max(0, self.length(r))
1252 dd = 0
1257 dd = 0
1253 di = actual - len(self) * s - databytes
1258 di = actual - len(self) * s - databytes
1254 except IOError, inst:
1259 except IOError, inst:
1255 if inst.errno != errno.ENOENT:
1260 if inst.errno != errno.ENOENT:
1256 raise
1261 raise
1257 di = 0
1262 di = 0
1258
1263
1259 return (dd, di)
1264 return (dd, di)
1260
1265
1261 def files(self):
1266 def files(self):
1262 res = [self.indexfile]
1267 res = [self.indexfile]
1263 if not self._inline:
1268 if not self._inline:
1264 res.append(self.datafile)
1269 res.append(self.datafile)
1265 return res
1270 return res
@@ -1,288 +1,151 b''
1 # discovery.py - protocol changeset discovery functions
1 # discovery.py - protocol changeset discovery functions
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
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
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
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
25 m = repo.changelog.nodemap
22 m = repo.changelog.nodemap
26 search = []
23 search = []
27 fetch = set()
24 fetch = set()
28 seen = set()
25 seen = set()
29 seenbranch = set()
26 seenbranch = set()
30 base = set()
27 base = set()
31
28
32 if not heads:
29 if not heads:
33 heads = remote.heads()
30 heads = remote.heads()
34
31
35 if repo.changelog.tip() == nullid:
32 if repo.changelog.tip() == nullid:
36 base.add(nullid)
33 base.add(nullid)
37 if heads != [nullid]:
34 if heads != [nullid]:
38 return [nullid], [nullid], list(heads)
35 return [nullid], [nullid], list(heads)
39 return [nullid], [], []
36 return [nullid], [], []
40
37
41 # assume we're closer to the tip than the root
38 # assume we're closer to the tip than the root
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:
55 unknown.append(h)
45 unknown.append(h)
56 else:
46 else:
57 base.add(h)
47 base.add(h)
58
48
59 heads = unknown
49 heads = unknown
60 if not unknown:
50 if not unknown:
61 return list(base), [], []
51 return list(base), [], []
62
52
63 req = set(unknown)
53 req = set(unknown)
64 reqcnt = 0
54 reqcnt = 0
65
55
66 # search through remote branches
56 # search through remote branches
67 # a 'branch' here is a linear segment of history, with four parts:
57 # a 'branch' here is a linear segment of history, with four parts:
68 # head, root, first parent, second parent
58 # head, root, first parent, second parent
69 # (a branch always has two parents (or none) by definition)
59 # (a branch always has two parents (or none) by definition)
70 unknown = remote.branches(unknown)
60 unknown = remote.branches(unknown)
71 while unknown:
61 while unknown:
72 r = []
62 r = []
73 while unknown:
63 while unknown:
74 n = unknown.pop(0)
64 n = unknown.pop(0)
75 if n[0] in seen:
65 if n[0] in seen:
76 continue
66 continue
77
67
78 repo.ui.debug("examining %s:%s\n"
68 repo.ui.debug("examining %s:%s\n"
79 % (short(n[0]), short(n[1])))
69 % (short(n[0]), short(n[1])))
80 if n[0] == nullid: # found the end of the branch
70 if n[0] == nullid: # found the end of the branch
81 pass
71 pass
82 elif n in seenbranch:
72 elif n in seenbranch:
83 repo.ui.debug("branch already found\n")
73 repo.ui.debug("branch already found\n")
84 continue
74 continue
85 elif n[1] and n[1] in m: # do we know the base?
75 elif n[1] and n[1] in m: # do we know the base?
86 repo.ui.debug("found incomplete branch %s:%s\n"
76 repo.ui.debug("found incomplete branch %s:%s\n"
87 % (short(n[0]), short(n[1])))
77 % (short(n[0]), short(n[1])))
88 search.append(n[0:2]) # schedule branch range for scanning
78 search.append(n[0:2]) # schedule branch range for scanning
89 seenbranch.add(n)
79 seenbranch.add(n)
90 else:
80 else:
91 if n[1] not in seen and n[1] not in fetch:
81 if n[1] not in seen and n[1] not in fetch:
92 if n[2] in m and n[3] in m:
82 if n[2] in m and n[3] in m:
93 repo.ui.debug("found new changeset %s\n" %
83 repo.ui.debug("found new changeset %s\n" %
94 short(n[1]))
84 short(n[1]))
95 fetch.add(n[1]) # earliest unknown
85 fetch.add(n[1]) # earliest unknown
96 for p in n[2:4]:
86 for p in n[2:4]:
97 if p in m:
87 if p in m:
98 base.add(p) # latest known
88 base.add(p) # latest known
99
89
100 for p in n[2:4]:
90 for p in n[2:4]:
101 if p not in req and p not in m:
91 if p not in req and p not in m:
102 r.append(p)
92 r.append(p)
103 req.add(p)
93 req.add(p)
104 seen.add(n[0])
94 seen.add(n[0])
105
95
106 if r:
96 if r:
107 reqcnt += 1
97 reqcnt += 1
108 repo.ui.progress(_('searching'), reqcnt, unit=_('queries'))
98 repo.ui.progress(_('searching'), reqcnt, unit=_('queries'))
109 repo.ui.debug("request %d: %s\n" %
99 repo.ui.debug("request %d: %s\n" %
110 (reqcnt, " ".join(map(short, r))))
100 (reqcnt, " ".join(map(short, r))))
111 for p in xrange(0, len(r), 10):
101 for p in xrange(0, len(r), 10):
112 for b in remote.branches(r[p:p + 10]):
102 for b in remote.branches(r[p:p + 10]):
113 repo.ui.debug("received %s:%s\n" %
103 repo.ui.debug("received %s:%s\n" %
114 (short(b[0]), short(b[1])))
104 (short(b[0]), short(b[1])))
115 unknown.append(b)
105 unknown.append(b)
116
106
117 # do binary search on the branches we found
107 # do binary search on the branches we found
118 while search:
108 while search:
119 newsearch = []
109 newsearch = []
120 reqcnt += 1
110 reqcnt += 1
121 repo.ui.progress(_('searching'), reqcnt, unit=_('queries'))
111 repo.ui.progress(_('searching'), reqcnt, unit=_('queries'))
122 for n, l in zip(search, remote.between(search)):
112 for n, l in zip(search, remote.between(search)):
123 l.append(n[1])
113 l.append(n[1])
124 p = n[0]
114 p = n[0]
125 f = 1
115 f = 1
126 for i in l:
116 for i in l:
127 repo.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
117 repo.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
128 if i in m:
118 if i in m:
129 if f <= 2:
119 if f <= 2:
130 repo.ui.debug("found new branch changeset %s\n" %
120 repo.ui.debug("found new branch changeset %s\n" %
131 short(p))
121 short(p))
132 fetch.add(p)
122 fetch.add(p)
133 base.add(i)
123 base.add(i)
134 else:
124 else:
135 repo.ui.debug("narrowed branch search to %s:%s\n"
125 repo.ui.debug("narrowed branch search to %s:%s\n"
136 % (short(p), short(i)))
126 % (short(p), short(i)))
137 newsearch.append((p, i))
127 newsearch.append((p, i))
138 break
128 break
139 p, f = i, f * 2
129 p, f = i, f * 2
140 search = newsearch
130 search = newsearch
141
131
142 # sanity check our fetch list
132 # sanity check our fetch list
143 for f in fetch:
133 for f in fetch:
144 if f in m:
134 if f in m:
145 raise error.RepoError(_("already have changeset ")
135 raise error.RepoError(_("already have changeset ")
146 + short(f[:4]))
136 + short(f[:4]))
147
137
148 base = list(base)
138 base = list(base)
149 if base == [nullid]:
139 if base == [nullid]:
150 if force:
140 if force:
151 repo.ui.warn(_("warning: repository is unrelated\n"))
141 repo.ui.warn(_("warning: repository is unrelated\n"))
152 else:
142 else:
153 raise util.Abort(_("repository is unrelated"))
143 raise util.Abort(_("repository is unrelated"))
154
144
155 repo.ui.debug("found new changesets starting at " +
145 repo.ui.debug("found new changesets starting at " +
156 " ".join([short(f) for f in fetch]) + "\n")
146 " ".join([short(f) for f in fetch]) + "\n")
157
147
158 repo.ui.progress(_('searching'), None)
148 repo.ui.progress(_('searching'), None)
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
@@ -1,1870 +1,1920 b''
1 > do_push()
1 > do_push()
2 > {
2 > {
3 > user=$1
3 > user=$1
4 > shift
4 > shift
5 > echo "Pushing as user $user"
5 > echo "Pushing as user $user"
6 > echo 'hgrc = """'
6 > echo 'hgrc = """'
7 > sed -e 1,2d b/.hg/hgrc | grep -v fakegroups.py
7 > sed -e 1,2d b/.hg/hgrc | grep -v fakegroups.py
8 > echo '"""'
8 > echo '"""'
9 > if test -f acl.config; then
9 > if test -f acl.config; then
10 > echo 'acl.config = """'
10 > echo 'acl.config = """'
11 > cat acl.config
11 > cat acl.config
12 > echo '"""'
12 > echo '"""'
13 > fi
13 > fi
14 > # On AIX /etc/profile sets LOGNAME read-only. So
14 > # On AIX /etc/profile sets LOGNAME read-only. So
15 > # LOGNAME=$user hg --cws a --debug push ../b
15 > # LOGNAME=$user hg --cws a --debug push ../b
16 > # fails with "This variable is read only."
16 > # fails with "This variable is read only."
17 > # Use env to work around this.
17 > # Use env to work around this.
18 > env LOGNAME=$user hg --cwd a --debug push ../b
18 > env LOGNAME=$user hg --cwd a --debug push ../b
19 > hg --cwd b rollback
19 > hg --cwd b rollback
20 > hg --cwd b --quiet tip
20 > hg --cwd b --quiet tip
21 > echo
21 > echo
22 > }
22 > }
23
23
24 > init_config()
24 > init_config()
25 > {
25 > {
26 > cat > fakegroups.py <<EOF
26 > cat > fakegroups.py <<EOF
27 > from hgext import acl
27 > from hgext import acl
28 > def fakegetusers(ui, group):
28 > def fakegetusers(ui, group):
29 > try:
29 > try:
30 > return acl._getusersorig(ui, group)
30 > return acl._getusersorig(ui, group)
31 > except:
31 > except:
32 > return ["fred", "betty"]
32 > return ["fred", "betty"]
33 > acl._getusersorig = acl._getusers
33 > acl._getusersorig = acl._getusers
34 > acl._getusers = fakegetusers
34 > acl._getusers = fakegetusers
35 > EOF
35 > EOF
36 > rm -f acl.config
36 > rm -f acl.config
37 > cat > $config <<EOF
37 > cat > $config <<EOF
38 > [hooks]
38 > [hooks]
39 > pretxnchangegroup.acl = python:hgext.acl.hook
39 > pretxnchangegroup.acl = python:hgext.acl.hook
40 > [acl]
40 > [acl]
41 > sources = push
41 > sources = push
42 > [extensions]
42 > [extensions]
43 > f=`pwd`/fakegroups.py
43 > f=`pwd`/fakegroups.py
44 > EOF
44 > EOF
45 > }
45 > }
46
46
47 $ hg init a
47 $ hg init a
48 $ cd a
48 $ cd a
49 $ mkdir foo foo/Bar quux
49 $ mkdir foo foo/Bar quux
50 $ echo 'in foo' > foo/file.txt
50 $ echo 'in foo' > foo/file.txt
51 $ echo 'in foo/Bar' > foo/Bar/file.txt
51 $ echo 'in foo/Bar' > foo/Bar/file.txt
52 $ echo 'in quux' > quux/file.py
52 $ echo 'in quux' > quux/file.py
53 $ hg add -q
53 $ hg add -q
54 $ hg ci -m 'add files' -d '1000000 0'
54 $ hg ci -m 'add files' -d '1000000 0'
55 $ echo >> foo/file.txt
55 $ echo >> foo/file.txt
56 $ hg ci -m 'change foo/file' -d '1000001 0'
56 $ hg ci -m 'change foo/file' -d '1000001 0'
57 $ echo >> foo/Bar/file.txt
57 $ echo >> foo/Bar/file.txt
58 $ hg ci -m 'change foo/Bar/file' -d '1000002 0'
58 $ hg ci -m 'change foo/Bar/file' -d '1000002 0'
59 $ echo >> quux/file.py
59 $ echo >> quux/file.py
60 $ hg ci -m 'change quux/file' -d '1000003 0'
60 $ hg ci -m 'change quux/file' -d '1000003 0'
61 $ hg tip --quiet
61 $ hg tip --quiet
62 3:911600dab2ae
62 3:911600dab2ae
63
63
64 $ cd ..
64 $ cd ..
65 $ hg clone -r 0 a b
65 $ hg clone -r 0 a b
66 adding changesets
66 adding changesets
67 adding manifests
67 adding manifests
68 adding file changes
68 adding file changes
69 added 1 changesets with 3 changes to 3 files
69 added 1 changesets with 3 changes to 3 files
70 updating to branch default
70 updating to branch default
71 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
71 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
72
72
73 $ echo '[extensions]' >> $HGRCPATH
73 $ echo '[extensions]' >> $HGRCPATH
74 $ echo 'acl =' >> $HGRCPATH
74 $ echo 'acl =' >> $HGRCPATH
75
75
76 $ config=b/.hg/hgrc
76 $ config=b/.hg/hgrc
77
77
78 Extension disabled for lack of a hook
78 Extension disabled for lack of a hook
79
79
80 $ do_push fred
80 $ do_push fred
81 Pushing as user fred
81 Pushing as user fred
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
89 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
91 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
90 911600dab2ae7a9baff75958b84fe606851ce955
92 911600dab2ae7a9baff75958b84fe606851ce955
91 adding changesets
93 adding changesets
92 bundling: 1 changesets
94 bundling: 1 changesets
93 bundling: 2 changesets
95 bundling: 2 changesets
94 bundling: 3 changesets
96 bundling: 3 changesets
95 bundling: 1/3 manifests (33.33%)
97 bundling: 1/3 manifests (33.33%)
96 bundling: 2/3 manifests (66.67%)
98 bundling: 2/3 manifests (66.67%)
97 bundling: 3/3 manifests (100.00%)
99 bundling: 3/3 manifests (100.00%)
98 bundling: foo/Bar/file.txt 0/3 files (0.00%)
100 bundling: foo/Bar/file.txt 0/3 files (0.00%)
99 bundling: foo/file.txt 1/3 files (33.33%)
101 bundling: foo/file.txt 1/3 files (33.33%)
100 bundling: quux/file.py 2/3 files (66.67%)
102 bundling: quux/file.py 2/3 files (66.67%)
101 changesets: 1 chunks
103 changesets: 1 chunks
102 add changeset ef1ea85a6374
104 add changeset ef1ea85a6374
103 changesets: 2 chunks
105 changesets: 2 chunks
104 add changeset f9cafe1212c8
106 add changeset f9cafe1212c8
105 changesets: 3 chunks
107 changesets: 3 chunks
106 add changeset 911600dab2ae
108 add changeset 911600dab2ae
107 adding manifests
109 adding manifests
108 manifests: 1/3 chunks (33.33%)
110 manifests: 1/3 chunks (33.33%)
109 manifests: 2/3 chunks (66.67%)
111 manifests: 2/3 chunks (66.67%)
110 manifests: 3/3 chunks (100.00%)
112 manifests: 3/3 chunks (100.00%)
111 adding file changes
113 adding file changes
112 adding foo/Bar/file.txt revisions
114 adding foo/Bar/file.txt revisions
113 files: 1/3 chunks (33.33%)
115 files: 1/3 chunks (33.33%)
114 adding foo/file.txt revisions
116 adding foo/file.txt revisions
115 files: 2/3 chunks (66.67%)
117 files: 2/3 chunks (66.67%)
116 adding quux/file.py revisions
118 adding quux/file.py revisions
117 files: 3/3 chunks (100.00%)
119 files: 3/3 chunks (100.00%)
118 added 3 changesets with 3 changes to 3 files
120 added 3 changesets with 3 changes to 3 files
119 updating the branch cache
121 updating the branch cache
120 checking for updated bookmarks
122 checking for updated bookmarks
121 repository tip rolled back to revision 0 (undo push)
123 repository tip rolled back to revision 0 (undo push)
122 working directory now based on revision 0
124 working directory now based on revision 0
123 0:6675d58eff77
125 0:6675d58eff77
124
126
125
127
126 $ echo '[hooks]' >> $config
128 $ echo '[hooks]' >> $config
127 $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
129 $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
128
130
129 Extension disabled for lack of acl.sources
131 Extension disabled for lack of acl.sources
130
132
131 $ do_push fred
133 $ do_push fred
132 Pushing as user fred
134 Pushing as user fred
133 hgrc = """
135 hgrc = """
134 [hooks]
136 [hooks]
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:
142 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
146 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
143 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
147 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
144 911600dab2ae7a9baff75958b84fe606851ce955
148 911600dab2ae7a9baff75958b84fe606851ce955
145 adding changesets
149 adding changesets
146 bundling: 1 changesets
150 bundling: 1 changesets
147 bundling: 2 changesets
151 bundling: 2 changesets
148 bundling: 3 changesets
152 bundling: 3 changesets
149 bundling: 1/3 manifests (33.33%)
153 bundling: 1/3 manifests (33.33%)
150 bundling: 2/3 manifests (66.67%)
154 bundling: 2/3 manifests (66.67%)
151 bundling: 3/3 manifests (100.00%)
155 bundling: 3/3 manifests (100.00%)
152 bundling: foo/Bar/file.txt 0/3 files (0.00%)
156 bundling: foo/Bar/file.txt 0/3 files (0.00%)
153 bundling: foo/file.txt 1/3 files (33.33%)
157 bundling: foo/file.txt 1/3 files (33.33%)
154 bundling: quux/file.py 2/3 files (66.67%)
158 bundling: quux/file.py 2/3 files (66.67%)
155 changesets: 1 chunks
159 changesets: 1 chunks
156 add changeset ef1ea85a6374
160 add changeset ef1ea85a6374
157 changesets: 2 chunks
161 changesets: 2 chunks
158 add changeset f9cafe1212c8
162 add changeset f9cafe1212c8
159 changesets: 3 chunks
163 changesets: 3 chunks
160 add changeset 911600dab2ae
164 add changeset 911600dab2ae
161 adding manifests
165 adding manifests
162 manifests: 1/3 chunks (33.33%)
166 manifests: 1/3 chunks (33.33%)
163 manifests: 2/3 chunks (66.67%)
167 manifests: 2/3 chunks (66.67%)
164 manifests: 3/3 chunks (100.00%)
168 manifests: 3/3 chunks (100.00%)
165 adding file changes
169 adding file changes
166 adding foo/Bar/file.txt revisions
170 adding foo/Bar/file.txt revisions
167 files: 1/3 chunks (33.33%)
171 files: 1/3 chunks (33.33%)
168 adding foo/file.txt revisions
172 adding foo/file.txt revisions
169 files: 2/3 chunks (66.67%)
173 files: 2/3 chunks (66.67%)
170 adding quux/file.py revisions
174 adding quux/file.py revisions
171 files: 3/3 chunks (100.00%)
175 files: 3/3 chunks (100.00%)
172 added 3 changesets with 3 changes to 3 files
176 added 3 changesets with 3 changes to 3 files
173 calling hook pretxnchangegroup.acl: hgext.acl.hook
177 calling hook pretxnchangegroup.acl: hgext.acl.hook
174 acl: changes have source "push" - skipping
178 acl: changes have source "push" - skipping
175 updating the branch cache
179 updating the branch cache
176 checking for updated bookmarks
180 checking for updated bookmarks
177 repository tip rolled back to revision 0 (undo push)
181 repository tip rolled back to revision 0 (undo push)
178 working directory now based on revision 0
182 working directory now based on revision 0
179 0:6675d58eff77
183 0:6675d58eff77
180
184
181
185
182 No [acl.allow]/[acl.deny]
186 No [acl.allow]/[acl.deny]
183
187
184 $ echo '[acl]' >> $config
188 $ echo '[acl]' >> $config
185 $ echo 'sources = push' >> $config
189 $ echo 'sources = push' >> $config
186 $ do_push fred
190 $ do_push fred
187 Pushing as user fred
191 Pushing as user fred
188 hgrc = """
192 hgrc = """
189 [hooks]
193 [hooks]
190 pretxnchangegroup.acl = python:hgext.acl.hook
194 pretxnchangegroup.acl = python:hgext.acl.hook
191 [acl]
195 [acl]
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:
199 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
205 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
200 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
206 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
201 911600dab2ae7a9baff75958b84fe606851ce955
207 911600dab2ae7a9baff75958b84fe606851ce955
202 adding changesets
208 adding changesets
203 bundling: 1 changesets
209 bundling: 1 changesets
204 bundling: 2 changesets
210 bundling: 2 changesets
205 bundling: 3 changesets
211 bundling: 3 changesets
206 bundling: 1/3 manifests (33.33%)
212 bundling: 1/3 manifests (33.33%)
207 bundling: 2/3 manifests (66.67%)
213 bundling: 2/3 manifests (66.67%)
208 bundling: 3/3 manifests (100.00%)
214 bundling: 3/3 manifests (100.00%)
209 bundling: foo/Bar/file.txt 0/3 files (0.00%)
215 bundling: foo/Bar/file.txt 0/3 files (0.00%)
210 bundling: foo/file.txt 1/3 files (33.33%)
216 bundling: foo/file.txt 1/3 files (33.33%)
211 bundling: quux/file.py 2/3 files (66.67%)
217 bundling: quux/file.py 2/3 files (66.67%)
212 changesets: 1 chunks
218 changesets: 1 chunks
213 add changeset ef1ea85a6374
219 add changeset ef1ea85a6374
214 changesets: 2 chunks
220 changesets: 2 chunks
215 add changeset f9cafe1212c8
221 add changeset f9cafe1212c8
216 changesets: 3 chunks
222 changesets: 3 chunks
217 add changeset 911600dab2ae
223 add changeset 911600dab2ae
218 adding manifests
224 adding manifests
219 manifests: 1/3 chunks (33.33%)
225 manifests: 1/3 chunks (33.33%)
220 manifests: 2/3 chunks (66.67%)
226 manifests: 2/3 chunks (66.67%)
221 manifests: 3/3 chunks (100.00%)
227 manifests: 3/3 chunks (100.00%)
222 adding file changes
228 adding file changes
223 adding foo/Bar/file.txt revisions
229 adding foo/Bar/file.txt revisions
224 files: 1/3 chunks (33.33%)
230 files: 1/3 chunks (33.33%)
225 adding foo/file.txt revisions
231 adding foo/file.txt revisions
226 files: 2/3 chunks (66.67%)
232 files: 2/3 chunks (66.67%)
227 adding quux/file.py revisions
233 adding quux/file.py revisions
228 files: 3/3 chunks (100.00%)
234 files: 3/3 chunks (100.00%)
229 added 3 changesets with 3 changes to 3 files
235 added 3 changesets with 3 changes to 3 files
230 calling hook pretxnchangegroup.acl: hgext.acl.hook
236 calling hook pretxnchangegroup.acl: hgext.acl.hook
231 acl: acl.allow.branches not enabled
237 acl: acl.allow.branches not enabled
232 acl: acl.deny.branches not enabled
238 acl: acl.deny.branches not enabled
233 acl: acl.allow not enabled
239 acl: acl.allow not enabled
234 acl: acl.deny not enabled
240 acl: acl.deny not enabled
235 acl: branch access granted: "ef1ea85a6374" on branch "default"
241 acl: branch access granted: "ef1ea85a6374" on branch "default"
236 acl: allowing changeset ef1ea85a6374
242 acl: allowing changeset ef1ea85a6374
237 acl: branch access granted: "f9cafe1212c8" on branch "default"
243 acl: branch access granted: "f9cafe1212c8" on branch "default"
238 acl: allowing changeset f9cafe1212c8
244 acl: allowing changeset f9cafe1212c8
239 acl: branch access granted: "911600dab2ae" on branch "default"
245 acl: branch access granted: "911600dab2ae" on branch "default"
240 acl: allowing changeset 911600dab2ae
246 acl: allowing changeset 911600dab2ae
241 updating the branch cache
247 updating the branch cache
242 checking for updated bookmarks
248 checking for updated bookmarks
243 repository tip rolled back to revision 0 (undo push)
249 repository tip rolled back to revision 0 (undo push)
244 working directory now based on revision 0
250 working directory now based on revision 0
245 0:6675d58eff77
251 0:6675d58eff77
246
252
247
253
248 Empty [acl.allow]
254 Empty [acl.allow]
249
255
250 $ echo '[acl.allow]' >> $config
256 $ echo '[acl.allow]' >> $config
251 $ do_push fred
257 $ do_push fred
252 Pushing as user fred
258 Pushing as user fred
253 hgrc = """
259 hgrc = """
254 [hooks]
260 [hooks]
255 pretxnchangegroup.acl = python:hgext.acl.hook
261 pretxnchangegroup.acl = python:hgext.acl.hook
256 [acl]
262 [acl]
257 sources = push
263 sources = push
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:
265 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
273 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
266 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
274 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
267 911600dab2ae7a9baff75958b84fe606851ce955
275 911600dab2ae7a9baff75958b84fe606851ce955
268 adding changesets
276 adding changesets
269 bundling: 1 changesets
277 bundling: 1 changesets
270 bundling: 2 changesets
278 bundling: 2 changesets
271 bundling: 3 changesets
279 bundling: 3 changesets
272 bundling: 1/3 manifests (33.33%)
280 bundling: 1/3 manifests (33.33%)
273 bundling: 2/3 manifests (66.67%)
281 bundling: 2/3 manifests (66.67%)
274 bundling: 3/3 manifests (100.00%)
282 bundling: 3/3 manifests (100.00%)
275 bundling: foo/Bar/file.txt 0/3 files (0.00%)
283 bundling: foo/Bar/file.txt 0/3 files (0.00%)
276 bundling: foo/file.txt 1/3 files (33.33%)
284 bundling: foo/file.txt 1/3 files (33.33%)
277 bundling: quux/file.py 2/3 files (66.67%)
285 bundling: quux/file.py 2/3 files (66.67%)
278 changesets: 1 chunks
286 changesets: 1 chunks
279 add changeset ef1ea85a6374
287 add changeset ef1ea85a6374
280 changesets: 2 chunks
288 changesets: 2 chunks
281 add changeset f9cafe1212c8
289 add changeset f9cafe1212c8
282 changesets: 3 chunks
290 changesets: 3 chunks
283 add changeset 911600dab2ae
291 add changeset 911600dab2ae
284 adding manifests
292 adding manifests
285 manifests: 1/3 chunks (33.33%)
293 manifests: 1/3 chunks (33.33%)
286 manifests: 2/3 chunks (66.67%)
294 manifests: 2/3 chunks (66.67%)
287 manifests: 3/3 chunks (100.00%)
295 manifests: 3/3 chunks (100.00%)
288 adding file changes
296 adding file changes
289 adding foo/Bar/file.txt revisions
297 adding foo/Bar/file.txt revisions
290 files: 1/3 chunks (33.33%)
298 files: 1/3 chunks (33.33%)
291 adding foo/file.txt revisions
299 adding foo/file.txt revisions
292 files: 2/3 chunks (66.67%)
300 files: 2/3 chunks (66.67%)
293 adding quux/file.py revisions
301 adding quux/file.py revisions
294 files: 3/3 chunks (100.00%)
302 files: 3/3 chunks (100.00%)
295 added 3 changesets with 3 changes to 3 files
303 added 3 changesets with 3 changes to 3 files
296 calling hook pretxnchangegroup.acl: hgext.acl.hook
304 calling hook pretxnchangegroup.acl: hgext.acl.hook
297 acl: acl.allow.branches not enabled
305 acl: acl.allow.branches not enabled
298 acl: acl.deny.branches not enabled
306 acl: acl.deny.branches not enabled
299 acl: acl.allow enabled, 0 entries for user fred
307 acl: acl.allow enabled, 0 entries for user fred
300 acl: acl.deny not enabled
308 acl: acl.deny not enabled
301 acl: branch access granted: "ef1ea85a6374" on branch "default"
309 acl: branch access granted: "ef1ea85a6374" on branch "default"
302 acl: user fred not allowed on foo/file.txt
310 acl: user fred not allowed on foo/file.txt
303 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374
311 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374
304 transaction abort!
312 transaction abort!
305 rollback completed
313 rollback completed
306 abort: acl: access denied for changeset ef1ea85a6374
314 abort: acl: access denied for changeset ef1ea85a6374
307 no rollback information available
315 no rollback information available
308 0:6675d58eff77
316 0:6675d58eff77
309
317
310
318
311 fred is allowed inside foo/
319 fred is allowed inside foo/
312
320
313 $ echo 'foo/** = fred' >> $config
321 $ echo 'foo/** = fred' >> $config
314 $ do_push fred
322 $ do_push fred
315 Pushing as user fred
323 Pushing as user fred
316 hgrc = """
324 hgrc = """
317 [hooks]
325 [hooks]
318 pretxnchangegroup.acl = python:hgext.acl.hook
326 pretxnchangegroup.acl = python:hgext.acl.hook
319 [acl]
327 [acl]
320 sources = push
328 sources = push
321 [acl.allow]
329 [acl.allow]
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
329 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
339 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
330 911600dab2ae7a9baff75958b84fe606851ce955
340 911600dab2ae7a9baff75958b84fe606851ce955
331 adding changesets
341 adding changesets
332 bundling: 1 changesets
342 bundling: 1 changesets
333 bundling: 2 changesets
343 bundling: 2 changesets
334 bundling: 3 changesets
344 bundling: 3 changesets
335 bundling: 1/3 manifests (33.33%)
345 bundling: 1/3 manifests (33.33%)
336 bundling: 2/3 manifests (66.67%)
346 bundling: 2/3 manifests (66.67%)
337 bundling: 3/3 manifests (100.00%)
347 bundling: 3/3 manifests (100.00%)
338 bundling: foo/Bar/file.txt 0/3 files (0.00%)
348 bundling: foo/Bar/file.txt 0/3 files (0.00%)
339 bundling: foo/file.txt 1/3 files (33.33%)
349 bundling: foo/file.txt 1/3 files (33.33%)
340 bundling: quux/file.py 2/3 files (66.67%)
350 bundling: quux/file.py 2/3 files (66.67%)
341 changesets: 1 chunks
351 changesets: 1 chunks
342 add changeset ef1ea85a6374
352 add changeset ef1ea85a6374
343 changesets: 2 chunks
353 changesets: 2 chunks
344 add changeset f9cafe1212c8
354 add changeset f9cafe1212c8
345 changesets: 3 chunks
355 changesets: 3 chunks
346 add changeset 911600dab2ae
356 add changeset 911600dab2ae
347 adding manifests
357 adding manifests
348 manifests: 1/3 chunks (33.33%)
358 manifests: 1/3 chunks (33.33%)
349 manifests: 2/3 chunks (66.67%)
359 manifests: 2/3 chunks (66.67%)
350 manifests: 3/3 chunks (100.00%)
360 manifests: 3/3 chunks (100.00%)
351 adding file changes
361 adding file changes
352 adding foo/Bar/file.txt revisions
362 adding foo/Bar/file.txt revisions
353 files: 1/3 chunks (33.33%)
363 files: 1/3 chunks (33.33%)
354 adding foo/file.txt revisions
364 adding foo/file.txt revisions
355 files: 2/3 chunks (66.67%)
365 files: 2/3 chunks (66.67%)
356 adding quux/file.py revisions
366 adding quux/file.py revisions
357 files: 3/3 chunks (100.00%)
367 files: 3/3 chunks (100.00%)
358 added 3 changesets with 3 changes to 3 files
368 added 3 changesets with 3 changes to 3 files
359 calling hook pretxnchangegroup.acl: hgext.acl.hook
369 calling hook pretxnchangegroup.acl: hgext.acl.hook
360 acl: acl.allow.branches not enabled
370 acl: acl.allow.branches not enabled
361 acl: acl.deny.branches not enabled
371 acl: acl.deny.branches not enabled
362 acl: acl.allow enabled, 1 entries for user fred
372 acl: acl.allow enabled, 1 entries for user fred
363 acl: acl.deny not enabled
373 acl: acl.deny not enabled
364 acl: branch access granted: "ef1ea85a6374" on branch "default"
374 acl: branch access granted: "ef1ea85a6374" on branch "default"
365 acl: allowing changeset ef1ea85a6374
375 acl: allowing changeset ef1ea85a6374
366 acl: branch access granted: "f9cafe1212c8" on branch "default"
376 acl: branch access granted: "f9cafe1212c8" on branch "default"
367 acl: allowing changeset f9cafe1212c8
377 acl: allowing changeset f9cafe1212c8
368 acl: branch access granted: "911600dab2ae" on branch "default"
378 acl: branch access granted: "911600dab2ae" on branch "default"
369 acl: user fred not allowed on quux/file.py
379 acl: user fred not allowed on quux/file.py
370 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
380 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
371 transaction abort!
381 transaction abort!
372 rollback completed
382 rollback completed
373 abort: acl: access denied for changeset 911600dab2ae
383 abort: acl: access denied for changeset 911600dab2ae
374 no rollback information available
384 no rollback information available
375 0:6675d58eff77
385 0:6675d58eff77
376
386
377
387
378 Empty [acl.deny]
388 Empty [acl.deny]
379
389
380 $ echo '[acl.deny]' >> $config
390 $ echo '[acl.deny]' >> $config
381 $ do_push barney
391 $ do_push barney
382 Pushing as user barney
392 Pushing as user barney
383 hgrc = """
393 hgrc = """
384 [hooks]
394 [hooks]
385 pretxnchangegroup.acl = python:hgext.acl.hook
395 pretxnchangegroup.acl = python:hgext.acl.hook
386 [acl]
396 [acl]
387 sources = push
397 sources = push
388 [acl.allow]
398 [acl.allow]
389 foo/** = fred
399 foo/** = fred
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
397 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
409 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
398 911600dab2ae7a9baff75958b84fe606851ce955
410 911600dab2ae7a9baff75958b84fe606851ce955
399 adding changesets
411 adding changesets
400 bundling: 1 changesets
412 bundling: 1 changesets
401 bundling: 2 changesets
413 bundling: 2 changesets
402 bundling: 3 changesets
414 bundling: 3 changesets
403 bundling: 1/3 manifests (33.33%)
415 bundling: 1/3 manifests (33.33%)
404 bundling: 2/3 manifests (66.67%)
416 bundling: 2/3 manifests (66.67%)
405 bundling: 3/3 manifests (100.00%)
417 bundling: 3/3 manifests (100.00%)
406 bundling: foo/Bar/file.txt 0/3 files (0.00%)
418 bundling: foo/Bar/file.txt 0/3 files (0.00%)
407 bundling: foo/file.txt 1/3 files (33.33%)
419 bundling: foo/file.txt 1/3 files (33.33%)
408 bundling: quux/file.py 2/3 files (66.67%)
420 bundling: quux/file.py 2/3 files (66.67%)
409 changesets: 1 chunks
421 changesets: 1 chunks
410 add changeset ef1ea85a6374
422 add changeset ef1ea85a6374
411 changesets: 2 chunks
423 changesets: 2 chunks
412 add changeset f9cafe1212c8
424 add changeset f9cafe1212c8
413 changesets: 3 chunks
425 changesets: 3 chunks
414 add changeset 911600dab2ae
426 add changeset 911600dab2ae
415 adding manifests
427 adding manifests
416 manifests: 1/3 chunks (33.33%)
428 manifests: 1/3 chunks (33.33%)
417 manifests: 2/3 chunks (66.67%)
429 manifests: 2/3 chunks (66.67%)
418 manifests: 3/3 chunks (100.00%)
430 manifests: 3/3 chunks (100.00%)
419 adding file changes
431 adding file changes
420 adding foo/Bar/file.txt revisions
432 adding foo/Bar/file.txt revisions
421 files: 1/3 chunks (33.33%)
433 files: 1/3 chunks (33.33%)
422 adding foo/file.txt revisions
434 adding foo/file.txt revisions
423 files: 2/3 chunks (66.67%)
435 files: 2/3 chunks (66.67%)
424 adding quux/file.py revisions
436 adding quux/file.py revisions
425 files: 3/3 chunks (100.00%)
437 files: 3/3 chunks (100.00%)
426 added 3 changesets with 3 changes to 3 files
438 added 3 changesets with 3 changes to 3 files
427 calling hook pretxnchangegroup.acl: hgext.acl.hook
439 calling hook pretxnchangegroup.acl: hgext.acl.hook
428 acl: acl.allow.branches not enabled
440 acl: acl.allow.branches not enabled
429 acl: acl.deny.branches not enabled
441 acl: acl.deny.branches not enabled
430 acl: acl.allow enabled, 0 entries for user barney
442 acl: acl.allow enabled, 0 entries for user barney
431 acl: acl.deny enabled, 0 entries for user barney
443 acl: acl.deny enabled, 0 entries for user barney
432 acl: branch access granted: "ef1ea85a6374" on branch "default"
444 acl: branch access granted: "ef1ea85a6374" on branch "default"
433 acl: user barney not allowed on foo/file.txt
445 acl: user barney not allowed on foo/file.txt
434 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374
446 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374
435 transaction abort!
447 transaction abort!
436 rollback completed
448 rollback completed
437 abort: acl: access denied for changeset ef1ea85a6374
449 abort: acl: access denied for changeset ef1ea85a6374
438 no rollback information available
450 no rollback information available
439 0:6675d58eff77
451 0:6675d58eff77
440
452
441
453
442 fred is allowed inside foo/, but not foo/bar/ (case matters)
454 fred is allowed inside foo/, but not foo/bar/ (case matters)
443
455
444 $ echo 'foo/bar/** = fred' >> $config
456 $ echo 'foo/bar/** = fred' >> $config
445 $ do_push fred
457 $ do_push fred
446 Pushing as user fred
458 Pushing as user fred
447 hgrc = """
459 hgrc = """
448 [hooks]
460 [hooks]
449 pretxnchangegroup.acl = python:hgext.acl.hook
461 pretxnchangegroup.acl = python:hgext.acl.hook
450 [acl]
462 [acl]
451 sources = push
463 sources = push
452 [acl.allow]
464 [acl.allow]
453 foo/** = fred
465 foo/** = fred
454 [acl.deny]
466 [acl.deny]
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
462 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
476 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
463 911600dab2ae7a9baff75958b84fe606851ce955
477 911600dab2ae7a9baff75958b84fe606851ce955
464 adding changesets
478 adding changesets
465 bundling: 1 changesets
479 bundling: 1 changesets
466 bundling: 2 changesets
480 bundling: 2 changesets
467 bundling: 3 changesets
481 bundling: 3 changesets
468 bundling: 1/3 manifests (33.33%)
482 bundling: 1/3 manifests (33.33%)
469 bundling: 2/3 manifests (66.67%)
483 bundling: 2/3 manifests (66.67%)
470 bundling: 3/3 manifests (100.00%)
484 bundling: 3/3 manifests (100.00%)
471 bundling: foo/Bar/file.txt 0/3 files (0.00%)
485 bundling: foo/Bar/file.txt 0/3 files (0.00%)
472 bundling: foo/file.txt 1/3 files (33.33%)
486 bundling: foo/file.txt 1/3 files (33.33%)
473 bundling: quux/file.py 2/3 files (66.67%)
487 bundling: quux/file.py 2/3 files (66.67%)
474 changesets: 1 chunks
488 changesets: 1 chunks
475 add changeset ef1ea85a6374
489 add changeset ef1ea85a6374
476 changesets: 2 chunks
490 changesets: 2 chunks
477 add changeset f9cafe1212c8
491 add changeset f9cafe1212c8
478 changesets: 3 chunks
492 changesets: 3 chunks
479 add changeset 911600dab2ae
493 add changeset 911600dab2ae
480 adding manifests
494 adding manifests
481 manifests: 1/3 chunks (33.33%)
495 manifests: 1/3 chunks (33.33%)
482 manifests: 2/3 chunks (66.67%)
496 manifests: 2/3 chunks (66.67%)
483 manifests: 3/3 chunks (100.00%)
497 manifests: 3/3 chunks (100.00%)
484 adding file changes
498 adding file changes
485 adding foo/Bar/file.txt revisions
499 adding foo/Bar/file.txt revisions
486 files: 1/3 chunks (33.33%)
500 files: 1/3 chunks (33.33%)
487 adding foo/file.txt revisions
501 adding foo/file.txt revisions
488 files: 2/3 chunks (66.67%)
502 files: 2/3 chunks (66.67%)
489 adding quux/file.py revisions
503 adding quux/file.py revisions
490 files: 3/3 chunks (100.00%)
504 files: 3/3 chunks (100.00%)
491 added 3 changesets with 3 changes to 3 files
505 added 3 changesets with 3 changes to 3 files
492 calling hook pretxnchangegroup.acl: hgext.acl.hook
506 calling hook pretxnchangegroup.acl: hgext.acl.hook
493 acl: acl.allow.branches not enabled
507 acl: acl.allow.branches not enabled
494 acl: acl.deny.branches not enabled
508 acl: acl.deny.branches not enabled
495 acl: acl.allow enabled, 1 entries for user fred
509 acl: acl.allow enabled, 1 entries for user fred
496 acl: acl.deny enabled, 1 entries for user fred
510 acl: acl.deny enabled, 1 entries for user fred
497 acl: branch access granted: "ef1ea85a6374" on branch "default"
511 acl: branch access granted: "ef1ea85a6374" on branch "default"
498 acl: allowing changeset ef1ea85a6374
512 acl: allowing changeset ef1ea85a6374
499 acl: branch access granted: "f9cafe1212c8" on branch "default"
513 acl: branch access granted: "f9cafe1212c8" on branch "default"
500 acl: allowing changeset f9cafe1212c8
514 acl: allowing changeset f9cafe1212c8
501 acl: branch access granted: "911600dab2ae" on branch "default"
515 acl: branch access granted: "911600dab2ae" on branch "default"
502 acl: user fred not allowed on quux/file.py
516 acl: user fred not allowed on quux/file.py
503 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
517 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
504 transaction abort!
518 transaction abort!
505 rollback completed
519 rollback completed
506 abort: acl: access denied for changeset 911600dab2ae
520 abort: acl: access denied for changeset 911600dab2ae
507 no rollback information available
521 no rollback information available
508 0:6675d58eff77
522 0:6675d58eff77
509
523
510
524
511 fred is allowed inside foo/, but not foo/Bar/
525 fred is allowed inside foo/, but not foo/Bar/
512
526
513 $ echo 'foo/Bar/** = fred' >> $config
527 $ echo 'foo/Bar/** = fred' >> $config
514 $ do_push fred
528 $ do_push fred
515 Pushing as user fred
529 Pushing as user fred
516 hgrc = """
530 hgrc = """
517 [hooks]
531 [hooks]
518 pretxnchangegroup.acl = python:hgext.acl.hook
532 pretxnchangegroup.acl = python:hgext.acl.hook
519 [acl]
533 [acl]
520 sources = push
534 sources = push
521 [acl.allow]
535 [acl.allow]
522 foo/** = fred
536 foo/** = fred
523 [acl.deny]
537 [acl.deny]
524 foo/bar/** = fred
538 foo/bar/** = fred
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
532 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
548 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
533 911600dab2ae7a9baff75958b84fe606851ce955
549 911600dab2ae7a9baff75958b84fe606851ce955
534 adding changesets
550 adding changesets
535 bundling: 1 changesets
551 bundling: 1 changesets
536 bundling: 2 changesets
552 bundling: 2 changesets
537 bundling: 3 changesets
553 bundling: 3 changesets
538 bundling: 1/3 manifests (33.33%)
554 bundling: 1/3 manifests (33.33%)
539 bundling: 2/3 manifests (66.67%)
555 bundling: 2/3 manifests (66.67%)
540 bundling: 3/3 manifests (100.00%)
556 bundling: 3/3 manifests (100.00%)
541 bundling: foo/Bar/file.txt 0/3 files (0.00%)
557 bundling: foo/Bar/file.txt 0/3 files (0.00%)
542 bundling: foo/file.txt 1/3 files (33.33%)
558 bundling: foo/file.txt 1/3 files (33.33%)
543 bundling: quux/file.py 2/3 files (66.67%)
559 bundling: quux/file.py 2/3 files (66.67%)
544 changesets: 1 chunks
560 changesets: 1 chunks
545 add changeset ef1ea85a6374
561 add changeset ef1ea85a6374
546 changesets: 2 chunks
562 changesets: 2 chunks
547 add changeset f9cafe1212c8
563 add changeset f9cafe1212c8
548 changesets: 3 chunks
564 changesets: 3 chunks
549 add changeset 911600dab2ae
565 add changeset 911600dab2ae
550 adding manifests
566 adding manifests
551 manifests: 1/3 chunks (33.33%)
567 manifests: 1/3 chunks (33.33%)
552 manifests: 2/3 chunks (66.67%)
568 manifests: 2/3 chunks (66.67%)
553 manifests: 3/3 chunks (100.00%)
569 manifests: 3/3 chunks (100.00%)
554 adding file changes
570 adding file changes
555 adding foo/Bar/file.txt revisions
571 adding foo/Bar/file.txt revisions
556 files: 1/3 chunks (33.33%)
572 files: 1/3 chunks (33.33%)
557 adding foo/file.txt revisions
573 adding foo/file.txt revisions
558 files: 2/3 chunks (66.67%)
574 files: 2/3 chunks (66.67%)
559 adding quux/file.py revisions
575 adding quux/file.py revisions
560 files: 3/3 chunks (100.00%)
576 files: 3/3 chunks (100.00%)
561 added 3 changesets with 3 changes to 3 files
577 added 3 changesets with 3 changes to 3 files
562 calling hook pretxnchangegroup.acl: hgext.acl.hook
578 calling hook pretxnchangegroup.acl: hgext.acl.hook
563 acl: acl.allow.branches not enabled
579 acl: acl.allow.branches not enabled
564 acl: acl.deny.branches not enabled
580 acl: acl.deny.branches not enabled
565 acl: acl.allow enabled, 1 entries for user fred
581 acl: acl.allow enabled, 1 entries for user fred
566 acl: acl.deny enabled, 2 entries for user fred
582 acl: acl.deny enabled, 2 entries for user fred
567 acl: branch access granted: "ef1ea85a6374" on branch "default"
583 acl: branch access granted: "ef1ea85a6374" on branch "default"
568 acl: allowing changeset ef1ea85a6374
584 acl: allowing changeset ef1ea85a6374
569 acl: branch access granted: "f9cafe1212c8" on branch "default"
585 acl: branch access granted: "f9cafe1212c8" on branch "default"
570 acl: user fred denied on foo/Bar/file.txt
586 acl: user fred denied on foo/Bar/file.txt
571 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8
587 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8
572 transaction abort!
588 transaction abort!
573 rollback completed
589 rollback completed
574 abort: acl: access denied for changeset f9cafe1212c8
590 abort: acl: access denied for changeset f9cafe1212c8
575 no rollback information available
591 no rollback information available
576 0:6675d58eff77
592 0:6675d58eff77
577
593
578
594
579 $ echo 'barney is not mentioned => not allowed anywhere'
595 $ echo 'barney is not mentioned => not allowed anywhere'
580 barney is not mentioned => not allowed anywhere
596 barney is not mentioned => not allowed anywhere
581 $ do_push barney
597 $ do_push barney
582 Pushing as user barney
598 Pushing as user barney
583 hgrc = """
599 hgrc = """
584 [hooks]
600 [hooks]
585 pretxnchangegroup.acl = python:hgext.acl.hook
601 pretxnchangegroup.acl = python:hgext.acl.hook
586 [acl]
602 [acl]
587 sources = push
603 sources = push
588 [acl.allow]
604 [acl.allow]
589 foo/** = fred
605 foo/** = fred
590 [acl.deny]
606 [acl.deny]
591 foo/bar/** = fred
607 foo/bar/** = fred
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
599 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
617 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
600 911600dab2ae7a9baff75958b84fe606851ce955
618 911600dab2ae7a9baff75958b84fe606851ce955
601 adding changesets
619 adding changesets
602 bundling: 1 changesets
620 bundling: 1 changesets
603 bundling: 2 changesets
621 bundling: 2 changesets
604 bundling: 3 changesets
622 bundling: 3 changesets
605 bundling: 1/3 manifests (33.33%)
623 bundling: 1/3 manifests (33.33%)
606 bundling: 2/3 manifests (66.67%)
624 bundling: 2/3 manifests (66.67%)
607 bundling: 3/3 manifests (100.00%)
625 bundling: 3/3 manifests (100.00%)
608 bundling: foo/Bar/file.txt 0/3 files (0.00%)
626 bundling: foo/Bar/file.txt 0/3 files (0.00%)
609 bundling: foo/file.txt 1/3 files (33.33%)
627 bundling: foo/file.txt 1/3 files (33.33%)
610 bundling: quux/file.py 2/3 files (66.67%)
628 bundling: quux/file.py 2/3 files (66.67%)
611 changesets: 1 chunks
629 changesets: 1 chunks
612 add changeset ef1ea85a6374
630 add changeset ef1ea85a6374
613 changesets: 2 chunks
631 changesets: 2 chunks
614 add changeset f9cafe1212c8
632 add changeset f9cafe1212c8
615 changesets: 3 chunks
633 changesets: 3 chunks
616 add changeset 911600dab2ae
634 add changeset 911600dab2ae
617 adding manifests
635 adding manifests
618 manifests: 1/3 chunks (33.33%)
636 manifests: 1/3 chunks (33.33%)
619 manifests: 2/3 chunks (66.67%)
637 manifests: 2/3 chunks (66.67%)
620 manifests: 3/3 chunks (100.00%)
638 manifests: 3/3 chunks (100.00%)
621 adding file changes
639 adding file changes
622 adding foo/Bar/file.txt revisions
640 adding foo/Bar/file.txt revisions
623 files: 1/3 chunks (33.33%)
641 files: 1/3 chunks (33.33%)
624 adding foo/file.txt revisions
642 adding foo/file.txt revisions
625 files: 2/3 chunks (66.67%)
643 files: 2/3 chunks (66.67%)
626 adding quux/file.py revisions
644 adding quux/file.py revisions
627 files: 3/3 chunks (100.00%)
645 files: 3/3 chunks (100.00%)
628 added 3 changesets with 3 changes to 3 files
646 added 3 changesets with 3 changes to 3 files
629 calling hook pretxnchangegroup.acl: hgext.acl.hook
647 calling hook pretxnchangegroup.acl: hgext.acl.hook
630 acl: acl.allow.branches not enabled
648 acl: acl.allow.branches not enabled
631 acl: acl.deny.branches not enabled
649 acl: acl.deny.branches not enabled
632 acl: acl.allow enabled, 0 entries for user barney
650 acl: acl.allow enabled, 0 entries for user barney
633 acl: acl.deny enabled, 0 entries for user barney
651 acl: acl.deny enabled, 0 entries for user barney
634 acl: branch access granted: "ef1ea85a6374" on branch "default"
652 acl: branch access granted: "ef1ea85a6374" on branch "default"
635 acl: user barney not allowed on foo/file.txt
653 acl: user barney not allowed on foo/file.txt
636 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374
654 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset ef1ea85a6374
637 transaction abort!
655 transaction abort!
638 rollback completed
656 rollback completed
639 abort: acl: access denied for changeset ef1ea85a6374
657 abort: acl: access denied for changeset ef1ea85a6374
640 no rollback information available
658 no rollback information available
641 0:6675d58eff77
659 0:6675d58eff77
642
660
643
661
644 barney is allowed everywhere
662 barney is allowed everywhere
645
663
646 $ echo '[acl.allow]' >> $config
664 $ echo '[acl.allow]' >> $config
647 $ echo '** = barney' >> $config
665 $ echo '** = barney' >> $config
648 $ do_push barney
666 $ do_push barney
649 Pushing as user barney
667 Pushing as user barney
650 hgrc = """
668 hgrc = """
651 [hooks]
669 [hooks]
652 pretxnchangegroup.acl = python:hgext.acl.hook
670 pretxnchangegroup.acl = python:hgext.acl.hook
653 [acl]
671 [acl]
654 sources = push
672 sources = push
655 [acl.allow]
673 [acl.allow]
656 foo/** = fred
674 foo/** = fred
657 [acl.deny]
675 [acl.deny]
658 foo/bar/** = fred
676 foo/bar/** = fred
659 foo/Bar/** = fred
677 foo/Bar/** = fred
660 [acl.allow]
678 [acl.allow]
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
668 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
688 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
669 911600dab2ae7a9baff75958b84fe606851ce955
689 911600dab2ae7a9baff75958b84fe606851ce955
670 adding changesets
690 adding changesets
671 bundling: 1 changesets
691 bundling: 1 changesets
672 bundling: 2 changesets
692 bundling: 2 changesets
673 bundling: 3 changesets
693 bundling: 3 changesets
674 bundling: 1/3 manifests (33.33%)
694 bundling: 1/3 manifests (33.33%)
675 bundling: 2/3 manifests (66.67%)
695 bundling: 2/3 manifests (66.67%)
676 bundling: 3/3 manifests (100.00%)
696 bundling: 3/3 manifests (100.00%)
677 bundling: foo/Bar/file.txt 0/3 files (0.00%)
697 bundling: foo/Bar/file.txt 0/3 files (0.00%)
678 bundling: foo/file.txt 1/3 files (33.33%)
698 bundling: foo/file.txt 1/3 files (33.33%)
679 bundling: quux/file.py 2/3 files (66.67%)
699 bundling: quux/file.py 2/3 files (66.67%)
680 changesets: 1 chunks
700 changesets: 1 chunks
681 add changeset ef1ea85a6374
701 add changeset ef1ea85a6374
682 changesets: 2 chunks
702 changesets: 2 chunks
683 add changeset f9cafe1212c8
703 add changeset f9cafe1212c8
684 changesets: 3 chunks
704 changesets: 3 chunks
685 add changeset 911600dab2ae
705 add changeset 911600dab2ae
686 adding manifests
706 adding manifests
687 manifests: 1/3 chunks (33.33%)
707 manifests: 1/3 chunks (33.33%)
688 manifests: 2/3 chunks (66.67%)
708 manifests: 2/3 chunks (66.67%)
689 manifests: 3/3 chunks (100.00%)
709 manifests: 3/3 chunks (100.00%)
690 adding file changes
710 adding file changes
691 adding foo/Bar/file.txt revisions
711 adding foo/Bar/file.txt revisions
692 files: 1/3 chunks (33.33%)
712 files: 1/3 chunks (33.33%)
693 adding foo/file.txt revisions
713 adding foo/file.txt revisions
694 files: 2/3 chunks (66.67%)
714 files: 2/3 chunks (66.67%)
695 adding quux/file.py revisions
715 adding quux/file.py revisions
696 files: 3/3 chunks (100.00%)
716 files: 3/3 chunks (100.00%)
697 added 3 changesets with 3 changes to 3 files
717 added 3 changesets with 3 changes to 3 files
698 calling hook pretxnchangegroup.acl: hgext.acl.hook
718 calling hook pretxnchangegroup.acl: hgext.acl.hook
699 acl: acl.allow.branches not enabled
719 acl: acl.allow.branches not enabled
700 acl: acl.deny.branches not enabled
720 acl: acl.deny.branches not enabled
701 acl: acl.allow enabled, 1 entries for user barney
721 acl: acl.allow enabled, 1 entries for user barney
702 acl: acl.deny enabled, 0 entries for user barney
722 acl: acl.deny enabled, 0 entries for user barney
703 acl: branch access granted: "ef1ea85a6374" on branch "default"
723 acl: branch access granted: "ef1ea85a6374" on branch "default"
704 acl: allowing changeset ef1ea85a6374
724 acl: allowing changeset ef1ea85a6374
705 acl: branch access granted: "f9cafe1212c8" on branch "default"
725 acl: branch access granted: "f9cafe1212c8" on branch "default"
706 acl: allowing changeset f9cafe1212c8
726 acl: allowing changeset f9cafe1212c8
707 acl: branch access granted: "911600dab2ae" on branch "default"
727 acl: branch access granted: "911600dab2ae" on branch "default"
708 acl: allowing changeset 911600dab2ae
728 acl: allowing changeset 911600dab2ae
709 updating the branch cache
729 updating the branch cache
710 checking for updated bookmarks
730 checking for updated bookmarks
711 repository tip rolled back to revision 0 (undo push)
731 repository tip rolled back to revision 0 (undo push)
712 working directory now based on revision 0
732 working directory now based on revision 0
713 0:6675d58eff77
733 0:6675d58eff77
714
734
715
735
716 wilma can change files with a .txt extension
736 wilma can change files with a .txt extension
717
737
718 $ echo '**/*.txt = wilma' >> $config
738 $ echo '**/*.txt = wilma' >> $config
719 $ do_push wilma
739 $ do_push wilma
720 Pushing as user wilma
740 Pushing as user wilma
721 hgrc = """
741 hgrc = """
722 [hooks]
742 [hooks]
723 pretxnchangegroup.acl = python:hgext.acl.hook
743 pretxnchangegroup.acl = python:hgext.acl.hook
724 [acl]
744 [acl]
725 sources = push
745 sources = push
726 [acl.allow]
746 [acl.allow]
727 foo/** = fred
747 foo/** = fred
728 [acl.deny]
748 [acl.deny]
729 foo/bar/** = fred
749 foo/bar/** = fred
730 foo/Bar/** = fred
750 foo/Bar/** = fred
731 [acl.allow]
751 [acl.allow]
732 ** = barney
752 ** = barney
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:
740 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
762 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
741 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
763 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
742 911600dab2ae7a9baff75958b84fe606851ce955
764 911600dab2ae7a9baff75958b84fe606851ce955
743 adding changesets
765 adding changesets
744 bundling: 1 changesets
766 bundling: 1 changesets
745 bundling: 2 changesets
767 bundling: 2 changesets
746 bundling: 3 changesets
768 bundling: 3 changesets
747 bundling: 1/3 manifests (33.33%)
769 bundling: 1/3 manifests (33.33%)
748 bundling: 2/3 manifests (66.67%)
770 bundling: 2/3 manifests (66.67%)
749 bundling: 3/3 manifests (100.00%)
771 bundling: 3/3 manifests (100.00%)
750 bundling: foo/Bar/file.txt 0/3 files (0.00%)
772 bundling: foo/Bar/file.txt 0/3 files (0.00%)
751 bundling: foo/file.txt 1/3 files (33.33%)
773 bundling: foo/file.txt 1/3 files (33.33%)
752 bundling: quux/file.py 2/3 files (66.67%)
774 bundling: quux/file.py 2/3 files (66.67%)
753 changesets: 1 chunks
775 changesets: 1 chunks
754 add changeset ef1ea85a6374
776 add changeset ef1ea85a6374
755 changesets: 2 chunks
777 changesets: 2 chunks
756 add changeset f9cafe1212c8
778 add changeset f9cafe1212c8
757 changesets: 3 chunks
779 changesets: 3 chunks
758 add changeset 911600dab2ae
780 add changeset 911600dab2ae
759 adding manifests
781 adding manifests
760 manifests: 1/3 chunks (33.33%)
782 manifests: 1/3 chunks (33.33%)
761 manifests: 2/3 chunks (66.67%)
783 manifests: 2/3 chunks (66.67%)
762 manifests: 3/3 chunks (100.00%)
784 manifests: 3/3 chunks (100.00%)
763 adding file changes
785 adding file changes
764 adding foo/Bar/file.txt revisions
786 adding foo/Bar/file.txt revisions
765 files: 1/3 chunks (33.33%)
787 files: 1/3 chunks (33.33%)
766 adding foo/file.txt revisions
788 adding foo/file.txt revisions
767 files: 2/3 chunks (66.67%)
789 files: 2/3 chunks (66.67%)
768 adding quux/file.py revisions
790 adding quux/file.py revisions
769 files: 3/3 chunks (100.00%)
791 files: 3/3 chunks (100.00%)
770 added 3 changesets with 3 changes to 3 files
792 added 3 changesets with 3 changes to 3 files
771 calling hook pretxnchangegroup.acl: hgext.acl.hook
793 calling hook pretxnchangegroup.acl: hgext.acl.hook
772 acl: acl.allow.branches not enabled
794 acl: acl.allow.branches not enabled
773 acl: acl.deny.branches not enabled
795 acl: acl.deny.branches not enabled
774 acl: acl.allow enabled, 1 entries for user wilma
796 acl: acl.allow enabled, 1 entries for user wilma
775 acl: acl.deny enabled, 0 entries for user wilma
797 acl: acl.deny enabled, 0 entries for user wilma
776 acl: branch access granted: "ef1ea85a6374" on branch "default"
798 acl: branch access granted: "ef1ea85a6374" on branch "default"
777 acl: allowing changeset ef1ea85a6374
799 acl: allowing changeset ef1ea85a6374
778 acl: branch access granted: "f9cafe1212c8" on branch "default"
800 acl: branch access granted: "f9cafe1212c8" on branch "default"
779 acl: allowing changeset f9cafe1212c8
801 acl: allowing changeset f9cafe1212c8
780 acl: branch access granted: "911600dab2ae" on branch "default"
802 acl: branch access granted: "911600dab2ae" on branch "default"
781 acl: user wilma not allowed on quux/file.py
803 acl: user wilma not allowed on quux/file.py
782 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
804 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
783 transaction abort!
805 transaction abort!
784 rollback completed
806 rollback completed
785 abort: acl: access denied for changeset 911600dab2ae
807 abort: acl: access denied for changeset 911600dab2ae
786 no rollback information available
808 no rollback information available
787 0:6675d58eff77
809 0:6675d58eff77
788
810
789
811
790 file specified by acl.config does not exist
812 file specified by acl.config does not exist
791
813
792 $ echo '[acl]' >> $config
814 $ echo '[acl]' >> $config
793 $ echo 'config = ../acl.config' >> $config
815 $ echo 'config = ../acl.config' >> $config
794 $ do_push barney
816 $ do_push barney
795 Pushing as user barney
817 Pushing as user barney
796 hgrc = """
818 hgrc = """
797 [hooks]
819 [hooks]
798 pretxnchangegroup.acl = python:hgext.acl.hook
820 pretxnchangegroup.acl = python:hgext.acl.hook
799 [acl]
821 [acl]
800 sources = push
822 sources = push
801 [acl.allow]
823 [acl.allow]
802 foo/** = fred
824 foo/** = fred
803 [acl.deny]
825 [acl.deny]
804 foo/bar/** = fred
826 foo/bar/** = fred
805 foo/Bar/** = fred
827 foo/Bar/** = fred
806 [acl.allow]
828 [acl.allow]
807 ** = barney
829 ** = barney
808 **/*.txt = wilma
830 **/*.txt = wilma
809 [acl]
831 [acl]
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
817 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
841 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
818 911600dab2ae7a9baff75958b84fe606851ce955
842 911600dab2ae7a9baff75958b84fe606851ce955
819 adding changesets
843 adding changesets
820 bundling: 1 changesets
844 bundling: 1 changesets
821 bundling: 2 changesets
845 bundling: 2 changesets
822 bundling: 3 changesets
846 bundling: 3 changesets
823 bundling: 1/3 manifests (33.33%)
847 bundling: 1/3 manifests (33.33%)
824 bundling: 2/3 manifests (66.67%)
848 bundling: 2/3 manifests (66.67%)
825 bundling: 3/3 manifests (100.00%)
849 bundling: 3/3 manifests (100.00%)
826 bundling: foo/Bar/file.txt 0/3 files (0.00%)
850 bundling: foo/Bar/file.txt 0/3 files (0.00%)
827 bundling: foo/file.txt 1/3 files (33.33%)
851 bundling: foo/file.txt 1/3 files (33.33%)
828 bundling: quux/file.py 2/3 files (66.67%)
852 bundling: quux/file.py 2/3 files (66.67%)
829 changesets: 1 chunks
853 changesets: 1 chunks
830 add changeset ef1ea85a6374
854 add changeset ef1ea85a6374
831 changesets: 2 chunks
855 changesets: 2 chunks
832 add changeset f9cafe1212c8
856 add changeset f9cafe1212c8
833 changesets: 3 chunks
857 changesets: 3 chunks
834 add changeset 911600dab2ae
858 add changeset 911600dab2ae
835 adding manifests
859 adding manifests
836 manifests: 1/3 chunks (33.33%)
860 manifests: 1/3 chunks (33.33%)
837 manifests: 2/3 chunks (66.67%)
861 manifests: 2/3 chunks (66.67%)
838 manifests: 3/3 chunks (100.00%)
862 manifests: 3/3 chunks (100.00%)
839 adding file changes
863 adding file changes
840 adding foo/Bar/file.txt revisions
864 adding foo/Bar/file.txt revisions
841 files: 1/3 chunks (33.33%)
865 files: 1/3 chunks (33.33%)
842 adding foo/file.txt revisions
866 adding foo/file.txt revisions
843 files: 2/3 chunks (66.67%)
867 files: 2/3 chunks (66.67%)
844 adding quux/file.py revisions
868 adding quux/file.py revisions
845 files: 3/3 chunks (100.00%)
869 files: 3/3 chunks (100.00%)
846 added 3 changesets with 3 changes to 3 files
870 added 3 changesets with 3 changes to 3 files
847 calling hook pretxnchangegroup.acl: hgext.acl.hook
871 calling hook pretxnchangegroup.acl: hgext.acl.hook
848 error: pretxnchangegroup.acl hook raised an exception: [Errno 2] No such file or directory: '../acl.config'
872 error: pretxnchangegroup.acl hook raised an exception: [Errno 2] No such file or directory: '../acl.config'
849 transaction abort!
873 transaction abort!
850 rollback completed
874 rollback completed
851 abort: No such file or directory: ../acl.config
875 abort: No such file or directory: ../acl.config
852 no rollback information available
876 no rollback information available
853 0:6675d58eff77
877 0:6675d58eff77
854
878
855
879
856 betty is allowed inside foo/ by a acl.config file
880 betty is allowed inside foo/ by a acl.config file
857
881
858 $ echo '[acl.allow]' >> acl.config
882 $ echo '[acl.allow]' >> acl.config
859 $ echo 'foo/** = betty' >> acl.config
883 $ echo 'foo/** = betty' >> acl.config
860 $ do_push betty
884 $ do_push betty
861 Pushing as user betty
885 Pushing as user betty
862 hgrc = """
886 hgrc = """
863 [hooks]
887 [hooks]
864 pretxnchangegroup.acl = python:hgext.acl.hook
888 pretxnchangegroup.acl = python:hgext.acl.hook
865 [acl]
889 [acl]
866 sources = push
890 sources = push
867 [acl.allow]
891 [acl.allow]
868 foo/** = fred
892 foo/** = fred
869 [acl.deny]
893 [acl.deny]
870 foo/bar/** = fred
894 foo/bar/** = fred
871 foo/Bar/** = fred
895 foo/Bar/** = fred
872 [acl.allow]
896 [acl.allow]
873 ** = barney
897 ** = barney
874 **/*.txt = wilma
898 **/*.txt = wilma
875 [acl]
899 [acl]
876 config = ../acl.config
900 config = ../acl.config
877 """
901 """
878 acl.config = """
902 acl.config = """
879 [acl.allow]
903 [acl.allow]
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
887 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
913 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
888 911600dab2ae7a9baff75958b84fe606851ce955
914 911600dab2ae7a9baff75958b84fe606851ce955
889 adding changesets
915 adding changesets
890 bundling: 1 changesets
916 bundling: 1 changesets
891 bundling: 2 changesets
917 bundling: 2 changesets
892 bundling: 3 changesets
918 bundling: 3 changesets
893 bundling: 1/3 manifests (33.33%)
919 bundling: 1/3 manifests (33.33%)
894 bundling: 2/3 manifests (66.67%)
920 bundling: 2/3 manifests (66.67%)
895 bundling: 3/3 manifests (100.00%)
921 bundling: 3/3 manifests (100.00%)
896 bundling: foo/Bar/file.txt 0/3 files (0.00%)
922 bundling: foo/Bar/file.txt 0/3 files (0.00%)
897 bundling: foo/file.txt 1/3 files (33.33%)
923 bundling: foo/file.txt 1/3 files (33.33%)
898 bundling: quux/file.py 2/3 files (66.67%)
924 bundling: quux/file.py 2/3 files (66.67%)
899 changesets: 1 chunks
925 changesets: 1 chunks
900 add changeset ef1ea85a6374
926 add changeset ef1ea85a6374
901 changesets: 2 chunks
927 changesets: 2 chunks
902 add changeset f9cafe1212c8
928 add changeset f9cafe1212c8
903 changesets: 3 chunks
929 changesets: 3 chunks
904 add changeset 911600dab2ae
930 add changeset 911600dab2ae
905 adding manifests
931 adding manifests
906 manifests: 1/3 chunks (33.33%)
932 manifests: 1/3 chunks (33.33%)
907 manifests: 2/3 chunks (66.67%)
933 manifests: 2/3 chunks (66.67%)
908 manifests: 3/3 chunks (100.00%)
934 manifests: 3/3 chunks (100.00%)
909 adding file changes
935 adding file changes
910 adding foo/Bar/file.txt revisions
936 adding foo/Bar/file.txt revisions
911 files: 1/3 chunks (33.33%)
937 files: 1/3 chunks (33.33%)
912 adding foo/file.txt revisions
938 adding foo/file.txt revisions
913 files: 2/3 chunks (66.67%)
939 files: 2/3 chunks (66.67%)
914 adding quux/file.py revisions
940 adding quux/file.py revisions
915 files: 3/3 chunks (100.00%)
941 files: 3/3 chunks (100.00%)
916 added 3 changesets with 3 changes to 3 files
942 added 3 changesets with 3 changes to 3 files
917 calling hook pretxnchangegroup.acl: hgext.acl.hook
943 calling hook pretxnchangegroup.acl: hgext.acl.hook
918 acl: acl.allow.branches not enabled
944 acl: acl.allow.branches not enabled
919 acl: acl.deny.branches not enabled
945 acl: acl.deny.branches not enabled
920 acl: acl.allow enabled, 1 entries for user betty
946 acl: acl.allow enabled, 1 entries for user betty
921 acl: acl.deny enabled, 0 entries for user betty
947 acl: acl.deny enabled, 0 entries for user betty
922 acl: branch access granted: "ef1ea85a6374" on branch "default"
948 acl: branch access granted: "ef1ea85a6374" on branch "default"
923 acl: allowing changeset ef1ea85a6374
949 acl: allowing changeset ef1ea85a6374
924 acl: branch access granted: "f9cafe1212c8" on branch "default"
950 acl: branch access granted: "f9cafe1212c8" on branch "default"
925 acl: allowing changeset f9cafe1212c8
951 acl: allowing changeset f9cafe1212c8
926 acl: branch access granted: "911600dab2ae" on branch "default"
952 acl: branch access granted: "911600dab2ae" on branch "default"
927 acl: user betty not allowed on quux/file.py
953 acl: user betty not allowed on quux/file.py
928 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
954 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset 911600dab2ae
929 transaction abort!
955 transaction abort!
930 rollback completed
956 rollback completed
931 abort: acl: access denied for changeset 911600dab2ae
957 abort: acl: access denied for changeset 911600dab2ae
932 no rollback information available
958 no rollback information available
933 0:6675d58eff77
959 0:6675d58eff77
934
960
935
961
936 acl.config can set only [acl.allow]/[acl.deny]
962 acl.config can set only [acl.allow]/[acl.deny]
937
963
938 $ echo '[hooks]' >> acl.config
964 $ echo '[hooks]' >> acl.config
939 $ echo 'changegroup.acl = false' >> acl.config
965 $ echo 'changegroup.acl = false' >> acl.config
940 $ do_push barney
966 $ do_push barney
941 Pushing as user barney
967 Pushing as user barney
942 hgrc = """
968 hgrc = """
943 [hooks]
969 [hooks]
944 pretxnchangegroup.acl = python:hgext.acl.hook
970 pretxnchangegroup.acl = python:hgext.acl.hook
945 [acl]
971 [acl]
946 sources = push
972 sources = push
947 [acl.allow]
973 [acl.allow]
948 foo/** = fred
974 foo/** = fred
949 [acl.deny]
975 [acl.deny]
950 foo/bar/** = fred
976 foo/bar/** = fred
951 foo/Bar/** = fred
977 foo/Bar/** = fred
952 [acl.allow]
978 [acl.allow]
953 ** = barney
979 ** = barney
954 **/*.txt = wilma
980 **/*.txt = wilma
955 [acl]
981 [acl]
956 config = ../acl.config
982 config = ../acl.config
957 """
983 """
958 acl.config = """
984 acl.config = """
959 [acl.allow]
985 [acl.allow]
960 foo/** = betty
986 foo/** = betty
961 [hooks]
987 [hooks]
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
969 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
997 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
970 911600dab2ae7a9baff75958b84fe606851ce955
998 911600dab2ae7a9baff75958b84fe606851ce955
971 adding changesets
999 adding changesets
972 bundling: 1 changesets
1000 bundling: 1 changesets
973 bundling: 2 changesets
1001 bundling: 2 changesets
974 bundling: 3 changesets
1002 bundling: 3 changesets
975 bundling: 1/3 manifests (33.33%)
1003 bundling: 1/3 manifests (33.33%)
976 bundling: 2/3 manifests (66.67%)
1004 bundling: 2/3 manifests (66.67%)
977 bundling: 3/3 manifests (100.00%)
1005 bundling: 3/3 manifests (100.00%)
978 bundling: foo/Bar/file.txt 0/3 files (0.00%)
1006 bundling: foo/Bar/file.txt 0/3 files (0.00%)
979 bundling: foo/file.txt 1/3 files (33.33%)
1007 bundling: foo/file.txt 1/3 files (33.33%)
980 bundling: quux/file.py 2/3 files (66.67%)
1008 bundling: quux/file.py 2/3 files (66.67%)
981 changesets: 1 chunks
1009 changesets: 1 chunks
982 add changeset ef1ea85a6374
1010 add changeset ef1ea85a6374
983 changesets: 2 chunks
1011 changesets: 2 chunks
984 add changeset f9cafe1212c8
1012 add changeset f9cafe1212c8
985 changesets: 3 chunks
1013 changesets: 3 chunks
986 add changeset 911600dab2ae
1014 add changeset 911600dab2ae
987 adding manifests
1015 adding manifests
988 manifests: 1/3 chunks (33.33%)
1016 manifests: 1/3 chunks (33.33%)
989 manifests: 2/3 chunks (66.67%)
1017 manifests: 2/3 chunks (66.67%)
990 manifests: 3/3 chunks (100.00%)
1018 manifests: 3/3 chunks (100.00%)
991 adding file changes
1019 adding file changes
992 adding foo/Bar/file.txt revisions
1020 adding foo/Bar/file.txt revisions
993 files: 1/3 chunks (33.33%)
1021 files: 1/3 chunks (33.33%)
994 adding foo/file.txt revisions
1022 adding foo/file.txt revisions
995 files: 2/3 chunks (66.67%)
1023 files: 2/3 chunks (66.67%)
996 adding quux/file.py revisions
1024 adding quux/file.py revisions
997 files: 3/3 chunks (100.00%)
1025 files: 3/3 chunks (100.00%)
998 added 3 changesets with 3 changes to 3 files
1026 added 3 changesets with 3 changes to 3 files
999 calling hook pretxnchangegroup.acl: hgext.acl.hook
1027 calling hook pretxnchangegroup.acl: hgext.acl.hook
1000 acl: acl.allow.branches not enabled
1028 acl: acl.allow.branches not enabled
1001 acl: acl.deny.branches not enabled
1029 acl: acl.deny.branches not enabled
1002 acl: acl.allow enabled, 1 entries for user barney
1030 acl: acl.allow enabled, 1 entries for user barney
1003 acl: acl.deny enabled, 0 entries for user barney
1031 acl: acl.deny enabled, 0 entries for user barney
1004 acl: branch access granted: "ef1ea85a6374" on branch "default"
1032 acl: branch access granted: "ef1ea85a6374" on branch "default"
1005 acl: allowing changeset ef1ea85a6374
1033 acl: allowing changeset ef1ea85a6374
1006 acl: branch access granted: "f9cafe1212c8" on branch "default"
1034 acl: branch access granted: "f9cafe1212c8" on branch "default"
1007 acl: allowing changeset f9cafe1212c8
1035 acl: allowing changeset f9cafe1212c8
1008 acl: branch access granted: "911600dab2ae" on branch "default"
1036 acl: branch access granted: "911600dab2ae" on branch "default"
1009 acl: allowing changeset 911600dab2ae
1037 acl: allowing changeset 911600dab2ae
1010 updating the branch cache
1038 updating the branch cache
1011 checking for updated bookmarks
1039 checking for updated bookmarks
1012 repository tip rolled back to revision 0 (undo push)
1040 repository tip rolled back to revision 0 (undo push)
1013 working directory now based on revision 0
1041 working directory now based on revision 0
1014 0:6675d58eff77
1042 0:6675d58eff77
1015
1043
1016
1044
1017 asterisk
1045 asterisk
1018
1046
1019 $ init_config
1047 $ init_config
1020
1048
1021 asterisk test
1049 asterisk test
1022
1050
1023 $ echo '[acl.allow]' >> $config
1051 $ echo '[acl.allow]' >> $config
1024 $ echo "** = fred" >> $config
1052 $ echo "** = fred" >> $config
1025
1053
1026 fred is always allowed
1054 fred is always allowed
1027
1055
1028 $ do_push fred
1056 $ do_push fred
1029 Pushing as user fred
1057 Pushing as user fred
1030 hgrc = """
1058 hgrc = """
1031 [acl]
1059 [acl]
1032 sources = push
1060 sources = push
1033 [extensions]
1061 [extensions]
1034 [acl.allow]
1062 [acl.allow]
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:
1042 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1072 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1043 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1073 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1044 911600dab2ae7a9baff75958b84fe606851ce955
1074 911600dab2ae7a9baff75958b84fe606851ce955
1045 adding changesets
1075 adding changesets
1046 bundling: 1 changesets
1076 bundling: 1 changesets
1047 bundling: 2 changesets
1077 bundling: 2 changesets
1048 bundling: 3 changesets
1078 bundling: 3 changesets
1049 bundling: 1/3 manifests (33.33%)
1079 bundling: 1/3 manifests (33.33%)
1050 bundling: 2/3 manifests (66.67%)
1080 bundling: 2/3 manifests (66.67%)
1051 bundling: 3/3 manifests (100.00%)
1081 bundling: 3/3 manifests (100.00%)
1052 bundling: foo/Bar/file.txt 0/3 files (0.00%)
1082 bundling: foo/Bar/file.txt 0/3 files (0.00%)
1053 bundling: foo/file.txt 1/3 files (33.33%)
1083 bundling: foo/file.txt 1/3 files (33.33%)
1054 bundling: quux/file.py 2/3 files (66.67%)
1084 bundling: quux/file.py 2/3 files (66.67%)
1055 changesets: 1 chunks
1085 changesets: 1 chunks
1056 add changeset ef1ea85a6374
1086 add changeset ef1ea85a6374
1057 changesets: 2 chunks
1087 changesets: 2 chunks
1058 add changeset f9cafe1212c8
1088 add changeset f9cafe1212c8
1059 changesets: 3 chunks
1089 changesets: 3 chunks
1060 add changeset 911600dab2ae
1090 add changeset 911600dab2ae
1061 adding manifests
1091 adding manifests
1062 manifests: 1/3 chunks (33.33%)
1092 manifests: 1/3 chunks (33.33%)
1063 manifests: 2/3 chunks (66.67%)
1093 manifests: 2/3 chunks (66.67%)
1064 manifests: 3/3 chunks (100.00%)
1094 manifests: 3/3 chunks (100.00%)
1065 adding file changes
1095 adding file changes
1066 adding foo/Bar/file.txt revisions
1096 adding foo/Bar/file.txt revisions
1067 files: 1/3 chunks (33.33%)
1097 files: 1/3 chunks (33.33%)
1068 adding foo/file.txt revisions
1098 adding foo/file.txt revisions
1069 files: 2/3 chunks (66.67%)
1099 files: 2/3 chunks (66.67%)
1070 adding quux/file.py revisions
1100 adding quux/file.py revisions
1071 files: 3/3 chunks (100.00%)
1101 files: 3/3 chunks (100.00%)
1072 added 3 changesets with 3 changes to 3 files
1102 added 3 changesets with 3 changes to 3 files
1073 calling hook pretxnchangegroup.acl: hgext.acl.hook
1103 calling hook pretxnchangegroup.acl: hgext.acl.hook
1074 acl: acl.allow.branches not enabled
1104 acl: acl.allow.branches not enabled
1075 acl: acl.deny.branches not enabled
1105 acl: acl.deny.branches not enabled
1076 acl: acl.allow enabled, 1 entries for user fred
1106 acl: acl.allow enabled, 1 entries for user fred
1077 acl: acl.deny not enabled
1107 acl: acl.deny not enabled
1078 acl: branch access granted: "ef1ea85a6374" on branch "default"
1108 acl: branch access granted: "ef1ea85a6374" on branch "default"
1079 acl: allowing changeset ef1ea85a6374
1109 acl: allowing changeset ef1ea85a6374
1080 acl: branch access granted: "f9cafe1212c8" on branch "default"
1110 acl: branch access granted: "f9cafe1212c8" on branch "default"
1081 acl: allowing changeset f9cafe1212c8
1111 acl: allowing changeset f9cafe1212c8
1082 acl: branch access granted: "911600dab2ae" on branch "default"
1112 acl: branch access granted: "911600dab2ae" on branch "default"
1083 acl: allowing changeset 911600dab2ae
1113 acl: allowing changeset 911600dab2ae
1084 updating the branch cache
1114 updating the branch cache
1085 checking for updated bookmarks
1115 checking for updated bookmarks
1086 repository tip rolled back to revision 0 (undo push)
1116 repository tip rolled back to revision 0 (undo push)
1087 working directory now based on revision 0
1117 working directory now based on revision 0
1088 0:6675d58eff77
1118 0:6675d58eff77
1089
1119
1090
1120
1091 $ echo '[acl.deny]' >> $config
1121 $ echo '[acl.deny]' >> $config
1092 $ echo "foo/Bar/** = *" >> $config
1122 $ echo "foo/Bar/** = *" >> $config
1093
1123
1094 no one is allowed inside foo/Bar/
1124 no one is allowed inside foo/Bar/
1095
1125
1096 $ do_push fred
1126 $ do_push fred
1097 Pushing as user fred
1127 Pushing as user fred
1098 hgrc = """
1128 hgrc = """
1099 [acl]
1129 [acl]
1100 sources = push
1130 sources = push
1101 [extensions]
1131 [extensions]
1102 [acl.allow]
1132 [acl.allow]
1103 ** = fred
1133 ** = fred
1104 [acl.deny]
1134 [acl.deny]
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:
1112 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1144 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1113 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1145 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1114 911600dab2ae7a9baff75958b84fe606851ce955
1146 911600dab2ae7a9baff75958b84fe606851ce955
1115 adding changesets
1147 adding changesets
1116 bundling: 1 changesets
1148 bundling: 1 changesets
1117 bundling: 2 changesets
1149 bundling: 2 changesets
1118 bundling: 3 changesets
1150 bundling: 3 changesets
1119 bundling: 1/3 manifests (33.33%)
1151 bundling: 1/3 manifests (33.33%)
1120 bundling: 2/3 manifests (66.67%)
1152 bundling: 2/3 manifests (66.67%)
1121 bundling: 3/3 manifests (100.00%)
1153 bundling: 3/3 manifests (100.00%)
1122 bundling: foo/Bar/file.txt 0/3 files (0.00%)
1154 bundling: foo/Bar/file.txt 0/3 files (0.00%)
1123 bundling: foo/file.txt 1/3 files (33.33%)
1155 bundling: foo/file.txt 1/3 files (33.33%)
1124 bundling: quux/file.py 2/3 files (66.67%)
1156 bundling: quux/file.py 2/3 files (66.67%)
1125 changesets: 1 chunks
1157 changesets: 1 chunks
1126 add changeset ef1ea85a6374
1158 add changeset ef1ea85a6374
1127 changesets: 2 chunks
1159 changesets: 2 chunks
1128 add changeset f9cafe1212c8
1160 add changeset f9cafe1212c8
1129 changesets: 3 chunks
1161 changesets: 3 chunks
1130 add changeset 911600dab2ae
1162 add changeset 911600dab2ae
1131 adding manifests
1163 adding manifests
1132 manifests: 1/3 chunks (33.33%)
1164 manifests: 1/3 chunks (33.33%)
1133 manifests: 2/3 chunks (66.67%)
1165 manifests: 2/3 chunks (66.67%)
1134 manifests: 3/3 chunks (100.00%)
1166 manifests: 3/3 chunks (100.00%)
1135 adding file changes
1167 adding file changes
1136 adding foo/Bar/file.txt revisions
1168 adding foo/Bar/file.txt revisions
1137 files: 1/3 chunks (33.33%)
1169 files: 1/3 chunks (33.33%)
1138 adding foo/file.txt revisions
1170 adding foo/file.txt revisions
1139 files: 2/3 chunks (66.67%)
1171 files: 2/3 chunks (66.67%)
1140 adding quux/file.py revisions
1172 adding quux/file.py revisions
1141 files: 3/3 chunks (100.00%)
1173 files: 3/3 chunks (100.00%)
1142 added 3 changesets with 3 changes to 3 files
1174 added 3 changesets with 3 changes to 3 files
1143 calling hook pretxnchangegroup.acl: hgext.acl.hook
1175 calling hook pretxnchangegroup.acl: hgext.acl.hook
1144 acl: acl.allow.branches not enabled
1176 acl: acl.allow.branches not enabled
1145 acl: acl.deny.branches not enabled
1177 acl: acl.deny.branches not enabled
1146 acl: acl.allow enabled, 1 entries for user fred
1178 acl: acl.allow enabled, 1 entries for user fred
1147 acl: acl.deny enabled, 1 entries for user fred
1179 acl: acl.deny enabled, 1 entries for user fred
1148 acl: branch access granted: "ef1ea85a6374" on branch "default"
1180 acl: branch access granted: "ef1ea85a6374" on branch "default"
1149 acl: allowing changeset ef1ea85a6374
1181 acl: allowing changeset ef1ea85a6374
1150 acl: branch access granted: "f9cafe1212c8" on branch "default"
1182 acl: branch access granted: "f9cafe1212c8" on branch "default"
1151 acl: user fred denied on foo/Bar/file.txt
1183 acl: user fred denied on foo/Bar/file.txt
1152 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8
1184 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8
1153 transaction abort!
1185 transaction abort!
1154 rollback completed
1186 rollback completed
1155 abort: acl: access denied for changeset f9cafe1212c8
1187 abort: acl: access denied for changeset f9cafe1212c8
1156 no rollback information available
1188 no rollback information available
1157 0:6675d58eff77
1189 0:6675d58eff77
1158
1190
1159
1191
1160 Groups
1192 Groups
1161
1193
1162 $ init_config
1194 $ init_config
1163
1195
1164 OS-level groups
1196 OS-level groups
1165
1197
1166 $ echo '[acl.allow]' >> $config
1198 $ echo '[acl.allow]' >> $config
1167 $ echo "** = @group1" >> $config
1199 $ echo "** = @group1" >> $config
1168
1200
1169 @group1 is always allowed
1201 @group1 is always allowed
1170
1202
1171 $ do_push fred
1203 $ do_push fred
1172 Pushing as user fred
1204 Pushing as user fred
1173 hgrc = """
1205 hgrc = """
1174 [acl]
1206 [acl]
1175 sources = push
1207 sources = push
1176 [extensions]
1208 [extensions]
1177 [acl.allow]
1209 [acl.allow]
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
1185 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1219 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1186 911600dab2ae7a9baff75958b84fe606851ce955
1220 911600dab2ae7a9baff75958b84fe606851ce955
1187 adding changesets
1221 adding changesets
1188 bundling: 1 changesets
1222 bundling: 1 changesets
1189 bundling: 2 changesets
1223 bundling: 2 changesets
1190 bundling: 3 changesets
1224 bundling: 3 changesets
1191 bundling: 1/3 manifests (33.33%)
1225 bundling: 1/3 manifests (33.33%)
1192 bundling: 2/3 manifests (66.67%)
1226 bundling: 2/3 manifests (66.67%)
1193 bundling: 3/3 manifests (100.00%)
1227 bundling: 3/3 manifests (100.00%)
1194 bundling: foo/Bar/file.txt 0/3 files (0.00%)
1228 bundling: foo/Bar/file.txt 0/3 files (0.00%)
1195 bundling: foo/file.txt 1/3 files (33.33%)
1229 bundling: foo/file.txt 1/3 files (33.33%)
1196 bundling: quux/file.py 2/3 files (66.67%)
1230 bundling: quux/file.py 2/3 files (66.67%)
1197 changesets: 1 chunks
1231 changesets: 1 chunks
1198 add changeset ef1ea85a6374
1232 add changeset ef1ea85a6374
1199 changesets: 2 chunks
1233 changesets: 2 chunks
1200 add changeset f9cafe1212c8
1234 add changeset f9cafe1212c8
1201 changesets: 3 chunks
1235 changesets: 3 chunks
1202 add changeset 911600dab2ae
1236 add changeset 911600dab2ae
1203 adding manifests
1237 adding manifests
1204 manifests: 1/3 chunks (33.33%)
1238 manifests: 1/3 chunks (33.33%)
1205 manifests: 2/3 chunks (66.67%)
1239 manifests: 2/3 chunks (66.67%)
1206 manifests: 3/3 chunks (100.00%)
1240 manifests: 3/3 chunks (100.00%)
1207 adding file changes
1241 adding file changes
1208 adding foo/Bar/file.txt revisions
1242 adding foo/Bar/file.txt revisions
1209 files: 1/3 chunks (33.33%)
1243 files: 1/3 chunks (33.33%)
1210 adding foo/file.txt revisions
1244 adding foo/file.txt revisions
1211 files: 2/3 chunks (66.67%)
1245 files: 2/3 chunks (66.67%)
1212 adding quux/file.py revisions
1246 adding quux/file.py revisions
1213 files: 3/3 chunks (100.00%)
1247 files: 3/3 chunks (100.00%)
1214 added 3 changesets with 3 changes to 3 files
1248 added 3 changesets with 3 changes to 3 files
1215 calling hook pretxnchangegroup.acl: hgext.acl.hook
1249 calling hook pretxnchangegroup.acl: hgext.acl.hook
1216 acl: acl.allow.branches not enabled
1250 acl: acl.allow.branches not enabled
1217 acl: acl.deny.branches not enabled
1251 acl: acl.deny.branches not enabled
1218 acl: "group1" not defined in [acl.groups]
1252 acl: "group1" not defined in [acl.groups]
1219 acl: acl.allow enabled, 1 entries for user fred
1253 acl: acl.allow enabled, 1 entries for user fred
1220 acl: acl.deny not enabled
1254 acl: acl.deny not enabled
1221 acl: branch access granted: "ef1ea85a6374" on branch "default"
1255 acl: branch access granted: "ef1ea85a6374" on branch "default"
1222 acl: allowing changeset ef1ea85a6374
1256 acl: allowing changeset ef1ea85a6374
1223 acl: branch access granted: "f9cafe1212c8" on branch "default"
1257 acl: branch access granted: "f9cafe1212c8" on branch "default"
1224 acl: allowing changeset f9cafe1212c8
1258 acl: allowing changeset f9cafe1212c8
1225 acl: branch access granted: "911600dab2ae" on branch "default"
1259 acl: branch access granted: "911600dab2ae" on branch "default"
1226 acl: allowing changeset 911600dab2ae
1260 acl: allowing changeset 911600dab2ae
1227 updating the branch cache
1261 updating the branch cache
1228 checking for updated bookmarks
1262 checking for updated bookmarks
1229 repository tip rolled back to revision 0 (undo push)
1263 repository tip rolled back to revision 0 (undo push)
1230 working directory now based on revision 0
1264 working directory now based on revision 0
1231 0:6675d58eff77
1265 0:6675d58eff77
1232
1266
1233
1267
1234 $ echo '[acl.deny]' >> $config
1268 $ echo '[acl.deny]' >> $config
1235 $ echo "foo/Bar/** = @group1" >> $config
1269 $ echo "foo/Bar/** = @group1" >> $config
1236
1270
1237 @group is allowed inside anything but foo/Bar/
1271 @group is allowed inside anything but foo/Bar/
1238
1272
1239 $ do_push fred
1273 $ do_push fred
1240 Pushing as user fred
1274 Pushing as user fred
1241 hgrc = """
1275 hgrc = """
1242 [acl]
1276 [acl]
1243 sources = push
1277 sources = push
1244 [extensions]
1278 [extensions]
1245 [acl.allow]
1279 [acl.allow]
1246 ** = @group1
1280 ** = @group1
1247 [acl.deny]
1281 [acl.deny]
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:
1255 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1291 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1256 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1292 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1257 911600dab2ae7a9baff75958b84fe606851ce955
1293 911600dab2ae7a9baff75958b84fe606851ce955
1258 adding changesets
1294 adding changesets
1259 bundling: 1 changesets
1295 bundling: 1 changesets
1260 bundling: 2 changesets
1296 bundling: 2 changesets
1261 bundling: 3 changesets
1297 bundling: 3 changesets
1262 bundling: 1/3 manifests (33.33%)
1298 bundling: 1/3 manifests (33.33%)
1263 bundling: 2/3 manifests (66.67%)
1299 bundling: 2/3 manifests (66.67%)
1264 bundling: 3/3 manifests (100.00%)
1300 bundling: 3/3 manifests (100.00%)
1265 bundling: foo/Bar/file.txt 0/3 files (0.00%)
1301 bundling: foo/Bar/file.txt 0/3 files (0.00%)
1266 bundling: foo/file.txt 1/3 files (33.33%)
1302 bundling: foo/file.txt 1/3 files (33.33%)
1267 bundling: quux/file.py 2/3 files (66.67%)
1303 bundling: quux/file.py 2/3 files (66.67%)
1268 changesets: 1 chunks
1304 changesets: 1 chunks
1269 add changeset ef1ea85a6374
1305 add changeset ef1ea85a6374
1270 changesets: 2 chunks
1306 changesets: 2 chunks
1271 add changeset f9cafe1212c8
1307 add changeset f9cafe1212c8
1272 changesets: 3 chunks
1308 changesets: 3 chunks
1273 add changeset 911600dab2ae
1309 add changeset 911600dab2ae
1274 adding manifests
1310 adding manifests
1275 manifests: 1/3 chunks (33.33%)
1311 manifests: 1/3 chunks (33.33%)
1276 manifests: 2/3 chunks (66.67%)
1312 manifests: 2/3 chunks (66.67%)
1277 manifests: 3/3 chunks (100.00%)
1313 manifests: 3/3 chunks (100.00%)
1278 adding file changes
1314 adding file changes
1279 adding foo/Bar/file.txt revisions
1315 adding foo/Bar/file.txt revisions
1280 files: 1/3 chunks (33.33%)
1316 files: 1/3 chunks (33.33%)
1281 adding foo/file.txt revisions
1317 adding foo/file.txt revisions
1282 files: 2/3 chunks (66.67%)
1318 files: 2/3 chunks (66.67%)
1283 adding quux/file.py revisions
1319 adding quux/file.py revisions
1284 files: 3/3 chunks (100.00%)
1320 files: 3/3 chunks (100.00%)
1285 added 3 changesets with 3 changes to 3 files
1321 added 3 changesets with 3 changes to 3 files
1286 calling hook pretxnchangegroup.acl: hgext.acl.hook
1322 calling hook pretxnchangegroup.acl: hgext.acl.hook
1287 acl: acl.allow.branches not enabled
1323 acl: acl.allow.branches not enabled
1288 acl: acl.deny.branches not enabled
1324 acl: acl.deny.branches not enabled
1289 acl: "group1" not defined in [acl.groups]
1325 acl: "group1" not defined in [acl.groups]
1290 acl: acl.allow enabled, 1 entries for user fred
1326 acl: acl.allow enabled, 1 entries for user fred
1291 acl: "group1" not defined in [acl.groups]
1327 acl: "group1" not defined in [acl.groups]
1292 acl: acl.deny enabled, 1 entries for user fred
1328 acl: acl.deny enabled, 1 entries for user fred
1293 acl: branch access granted: "ef1ea85a6374" on branch "default"
1329 acl: branch access granted: "ef1ea85a6374" on branch "default"
1294 acl: allowing changeset ef1ea85a6374
1330 acl: allowing changeset ef1ea85a6374
1295 acl: branch access granted: "f9cafe1212c8" on branch "default"
1331 acl: branch access granted: "f9cafe1212c8" on branch "default"
1296 acl: user fred denied on foo/Bar/file.txt
1332 acl: user fred denied on foo/Bar/file.txt
1297 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8
1333 error: pretxnchangegroup.acl hook failed: acl: access denied for changeset f9cafe1212c8
1298 transaction abort!
1334 transaction abort!
1299 rollback completed
1335 rollback completed
1300 abort: acl: access denied for changeset f9cafe1212c8
1336 abort: acl: access denied for changeset f9cafe1212c8
1301 no rollback information available
1337 no rollback information available
1302 0:6675d58eff77
1338 0:6675d58eff77
1303
1339
1304
1340
1305 Invalid group
1341 Invalid group
1306
1342
1307 Disable the fakegroups trick to get real failures
1343 Disable the fakegroups trick to get real failures
1308
1344
1309 $ grep -v fakegroups $config > config.tmp
1345 $ grep -v fakegroups $config > config.tmp
1310 $ mv config.tmp $config
1346 $ mv config.tmp $config
1311 $ echo '[acl.allow]' >> $config
1347 $ echo '[acl.allow]' >> $config
1312 $ echo "** = @unlikelytoexist" >> $config
1348 $ echo "** = @unlikelytoexist" >> $config
1313 $ do_push fred 2>&1 | grep unlikelytoexist
1349 $ do_push fred 2>&1 | grep unlikelytoexist
1314 ** = @unlikelytoexist
1350 ** = @unlikelytoexist
1315 acl: "unlikelytoexist" not defined in [acl.groups]
1351 acl: "unlikelytoexist" not defined in [acl.groups]
1316 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1352 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1317 abort: group 'unlikelytoexist' is undefined
1353 abort: group 'unlikelytoexist' is undefined
1318
1354
1319
1355
1320 Branch acl tests setup
1356 Branch acl tests setup
1321
1357
1322 $ init_config
1358 $ init_config
1323 $ cd b
1359 $ cd b
1324 $ hg up
1360 $ hg up
1325 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1361 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1326 $ hg branch foobar
1362 $ hg branch foobar
1327 marked working directory as branch foobar
1363 marked working directory as branch foobar
1328 $ hg commit -m 'create foobar'
1364 $ hg commit -m 'create foobar'
1329 $ echo 'foo contents' > abc.txt
1365 $ echo 'foo contents' > abc.txt
1330 $ hg add abc.txt
1366 $ hg add abc.txt
1331 $ hg commit -m 'foobar contents'
1367 $ hg commit -m 'foobar contents'
1332 $ cd ..
1368 $ cd ..
1333 $ hg --cwd a pull ../b
1369 $ hg --cwd a pull ../b
1334 pulling from ../b
1370 pulling from ../b
1335 searching for changes
1371 searching for changes
1336 adding changesets
1372 adding changesets
1337 adding manifests
1373 adding manifests
1338 adding file changes
1374 adding file changes
1339 added 2 changesets with 1 changes to 1 files (+1 heads)
1375 added 2 changesets with 1 changes to 1 files (+1 heads)
1340 (run 'hg heads' to see heads)
1376 (run 'hg heads' to see heads)
1341
1377
1342 Create additional changeset on foobar branch
1378 Create additional changeset on foobar branch
1343
1379
1344 $ cd a
1380 $ cd a
1345 $ hg up -C foobar
1381 $ hg up -C foobar
1346 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1382 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1347 $ echo 'foo contents2' > abc.txt
1383 $ echo 'foo contents2' > abc.txt
1348 $ hg commit -m 'foobar contents2'
1384 $ hg commit -m 'foobar contents2'
1349 $ cd ..
1385 $ cd ..
1350
1386
1351
1387
1352 No branch acls specified
1388 No branch acls specified
1353
1389
1354 $ do_push astro
1390 $ do_push astro
1355 Pushing as user astro
1391 Pushing as user astro
1356 hgrc = """
1392 hgrc = """
1357 [acl]
1393 [acl]
1358 sources = push
1394 sources = push
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
1366 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1404 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1367 911600dab2ae7a9baff75958b84fe606851ce955
1405 911600dab2ae7a9baff75958b84fe606851ce955
1368 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1406 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1369 adding changesets
1407 adding changesets
1370 bundling: 1 changesets
1408 bundling: 1 changesets
1371 bundling: 2 changesets
1409 bundling: 2 changesets
1372 bundling: 3 changesets
1410 bundling: 3 changesets
1373 bundling: 4 changesets
1411 bundling: 4 changesets
1374 bundling: 1/4 manifests (25.00%)
1412 bundling: 1/4 manifests (25.00%)
1375 bundling: 2/4 manifests (50.00%)
1413 bundling: 2/4 manifests (50.00%)
1376 bundling: 3/4 manifests (75.00%)
1414 bundling: 3/4 manifests (75.00%)
1377 bundling: 4/4 manifests (100.00%)
1415 bundling: 4/4 manifests (100.00%)
1378 bundling: abc.txt 0/4 files (0.00%)
1416 bundling: abc.txt 0/4 files (0.00%)
1379 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1417 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1380 bundling: foo/file.txt 2/4 files (50.00%)
1418 bundling: foo/file.txt 2/4 files (50.00%)
1381 bundling: quux/file.py 3/4 files (75.00%)
1419 bundling: quux/file.py 3/4 files (75.00%)
1382 changesets: 1 chunks
1420 changesets: 1 chunks
1383 add changeset ef1ea85a6374
1421 add changeset ef1ea85a6374
1384 changesets: 2 chunks
1422 changesets: 2 chunks
1385 add changeset f9cafe1212c8
1423 add changeset f9cafe1212c8
1386 changesets: 3 chunks
1424 changesets: 3 chunks
1387 add changeset 911600dab2ae
1425 add changeset 911600dab2ae
1388 changesets: 4 chunks
1426 changesets: 4 chunks
1389 add changeset e8fc755d4d82
1427 add changeset e8fc755d4d82
1390 adding manifests
1428 adding manifests
1391 manifests: 1/4 chunks (25.00%)
1429 manifests: 1/4 chunks (25.00%)
1392 manifests: 2/4 chunks (50.00%)
1430 manifests: 2/4 chunks (50.00%)
1393 manifests: 3/4 chunks (75.00%)
1431 manifests: 3/4 chunks (75.00%)
1394 manifests: 4/4 chunks (100.00%)
1432 manifests: 4/4 chunks (100.00%)
1395 adding file changes
1433 adding file changes
1396 adding abc.txt revisions
1434 adding abc.txt revisions
1397 files: 1/4 chunks (25.00%)
1435 files: 1/4 chunks (25.00%)
1398 adding foo/Bar/file.txt revisions
1436 adding foo/Bar/file.txt revisions
1399 files: 2/4 chunks (50.00%)
1437 files: 2/4 chunks (50.00%)
1400 adding foo/file.txt revisions
1438 adding foo/file.txt revisions
1401 files: 3/4 chunks (75.00%)
1439 files: 3/4 chunks (75.00%)
1402 adding quux/file.py revisions
1440 adding quux/file.py revisions
1403 files: 4/4 chunks (100.00%)
1441 files: 4/4 chunks (100.00%)
1404 added 4 changesets with 4 changes to 4 files (+1 heads)
1442 added 4 changesets with 4 changes to 4 files (+1 heads)
1405 calling hook pretxnchangegroup.acl: hgext.acl.hook
1443 calling hook pretxnchangegroup.acl: hgext.acl.hook
1406 acl: acl.allow.branches not enabled
1444 acl: acl.allow.branches not enabled
1407 acl: acl.deny.branches not enabled
1445 acl: acl.deny.branches not enabled
1408 acl: acl.allow not enabled
1446 acl: acl.allow not enabled
1409 acl: acl.deny not enabled
1447 acl: acl.deny not enabled
1410 acl: branch access granted: "ef1ea85a6374" on branch "default"
1448 acl: branch access granted: "ef1ea85a6374" on branch "default"
1411 acl: allowing changeset ef1ea85a6374
1449 acl: allowing changeset ef1ea85a6374
1412 acl: branch access granted: "f9cafe1212c8" on branch "default"
1450 acl: branch access granted: "f9cafe1212c8" on branch "default"
1413 acl: allowing changeset f9cafe1212c8
1451 acl: allowing changeset f9cafe1212c8
1414 acl: branch access granted: "911600dab2ae" on branch "default"
1452 acl: branch access granted: "911600dab2ae" on branch "default"
1415 acl: allowing changeset 911600dab2ae
1453 acl: allowing changeset 911600dab2ae
1416 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1454 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1417 acl: allowing changeset e8fc755d4d82
1455 acl: allowing changeset e8fc755d4d82
1418 updating the branch cache
1456 updating the branch cache
1419 checking for updated bookmarks
1457 checking for updated bookmarks
1420 repository tip rolled back to revision 2 (undo push)
1458 repository tip rolled back to revision 2 (undo push)
1421 working directory now based on revision 2
1459 working directory now based on revision 2
1422 2:fb35475503ef
1460 2:fb35475503ef
1423
1461
1424
1462
1425 Branch acl deny test
1463 Branch acl deny test
1426
1464
1427 $ echo "[acl.deny.branches]" >> $config
1465 $ echo "[acl.deny.branches]" >> $config
1428 $ echo "foobar = *" >> $config
1466 $ echo "foobar = *" >> $config
1429 $ do_push astro
1467 $ do_push astro
1430 Pushing as user astro
1468 Pushing as user astro
1431 hgrc = """
1469 hgrc = """
1432 [acl]
1470 [acl]
1433 sources = push
1471 sources = push
1434 [extensions]
1472 [extensions]
1435 [acl.deny.branches]
1473 [acl.deny.branches]
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:
1443 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1483 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1444 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1484 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1445 911600dab2ae7a9baff75958b84fe606851ce955
1485 911600dab2ae7a9baff75958b84fe606851ce955
1446 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1486 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1447 adding changesets
1487 adding changesets
1448 bundling: 1 changesets
1488 bundling: 1 changesets
1449 bundling: 2 changesets
1489 bundling: 2 changesets
1450 bundling: 3 changesets
1490 bundling: 3 changesets
1451 bundling: 4 changesets
1491 bundling: 4 changesets
1452 bundling: 1/4 manifests (25.00%)
1492 bundling: 1/4 manifests (25.00%)
1453 bundling: 2/4 manifests (50.00%)
1493 bundling: 2/4 manifests (50.00%)
1454 bundling: 3/4 manifests (75.00%)
1494 bundling: 3/4 manifests (75.00%)
1455 bundling: 4/4 manifests (100.00%)
1495 bundling: 4/4 manifests (100.00%)
1456 bundling: abc.txt 0/4 files (0.00%)
1496 bundling: abc.txt 0/4 files (0.00%)
1457 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1497 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1458 bundling: foo/file.txt 2/4 files (50.00%)
1498 bundling: foo/file.txt 2/4 files (50.00%)
1459 bundling: quux/file.py 3/4 files (75.00%)
1499 bundling: quux/file.py 3/4 files (75.00%)
1460 changesets: 1 chunks
1500 changesets: 1 chunks
1461 add changeset ef1ea85a6374
1501 add changeset ef1ea85a6374
1462 changesets: 2 chunks
1502 changesets: 2 chunks
1463 add changeset f9cafe1212c8
1503 add changeset f9cafe1212c8
1464 changesets: 3 chunks
1504 changesets: 3 chunks
1465 add changeset 911600dab2ae
1505 add changeset 911600dab2ae
1466 changesets: 4 chunks
1506 changesets: 4 chunks
1467 add changeset e8fc755d4d82
1507 add changeset e8fc755d4d82
1468 adding manifests
1508 adding manifests
1469 manifests: 1/4 chunks (25.00%)
1509 manifests: 1/4 chunks (25.00%)
1470 manifests: 2/4 chunks (50.00%)
1510 manifests: 2/4 chunks (50.00%)
1471 manifests: 3/4 chunks (75.00%)
1511 manifests: 3/4 chunks (75.00%)
1472 manifests: 4/4 chunks (100.00%)
1512 manifests: 4/4 chunks (100.00%)
1473 adding file changes
1513 adding file changes
1474 adding abc.txt revisions
1514 adding abc.txt revisions
1475 files: 1/4 chunks (25.00%)
1515 files: 1/4 chunks (25.00%)
1476 adding foo/Bar/file.txt revisions
1516 adding foo/Bar/file.txt revisions
1477 files: 2/4 chunks (50.00%)
1517 files: 2/4 chunks (50.00%)
1478 adding foo/file.txt revisions
1518 adding foo/file.txt revisions
1479 files: 3/4 chunks (75.00%)
1519 files: 3/4 chunks (75.00%)
1480 adding quux/file.py revisions
1520 adding quux/file.py revisions
1481 files: 4/4 chunks (100.00%)
1521 files: 4/4 chunks (100.00%)
1482 added 4 changesets with 4 changes to 4 files (+1 heads)
1522 added 4 changesets with 4 changes to 4 files (+1 heads)
1483 calling hook pretxnchangegroup.acl: hgext.acl.hook
1523 calling hook pretxnchangegroup.acl: hgext.acl.hook
1484 acl: acl.allow.branches not enabled
1524 acl: acl.allow.branches not enabled
1485 acl: acl.deny.branches enabled, 1 entries for user astro
1525 acl: acl.deny.branches enabled, 1 entries for user astro
1486 acl: acl.allow not enabled
1526 acl: acl.allow not enabled
1487 acl: acl.deny not enabled
1527 acl: acl.deny not enabled
1488 acl: branch access granted: "ef1ea85a6374" on branch "default"
1528 acl: branch access granted: "ef1ea85a6374" on branch "default"
1489 acl: allowing changeset ef1ea85a6374
1529 acl: allowing changeset ef1ea85a6374
1490 acl: branch access granted: "f9cafe1212c8" on branch "default"
1530 acl: branch access granted: "f9cafe1212c8" on branch "default"
1491 acl: allowing changeset f9cafe1212c8
1531 acl: allowing changeset f9cafe1212c8
1492 acl: branch access granted: "911600dab2ae" on branch "default"
1532 acl: branch access granted: "911600dab2ae" on branch "default"
1493 acl: allowing changeset 911600dab2ae
1533 acl: allowing changeset 911600dab2ae
1494 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1534 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1495 transaction abort!
1535 transaction abort!
1496 rollback completed
1536 rollback completed
1497 abort: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1537 abort: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1498 no rollback information available
1538 no rollback information available
1499 2:fb35475503ef
1539 2:fb35475503ef
1500
1540
1501
1541
1502 Branch acl empty allow test
1542 Branch acl empty allow test
1503
1543
1504 $ init_config
1544 $ init_config
1505 $ echo "[acl.allow.branches]" >> $config
1545 $ echo "[acl.allow.branches]" >> $config
1506 $ do_push astro
1546 $ do_push astro
1507 Pushing as user astro
1547 Pushing as user astro
1508 hgrc = """
1548 hgrc = """
1509 [acl]
1549 [acl]
1510 sources = push
1550 sources = push
1511 [extensions]
1551 [extensions]
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
1519 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1561 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1520 911600dab2ae7a9baff75958b84fe606851ce955
1562 911600dab2ae7a9baff75958b84fe606851ce955
1521 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1563 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1522 adding changesets
1564 adding changesets
1523 bundling: 1 changesets
1565 bundling: 1 changesets
1524 bundling: 2 changesets
1566 bundling: 2 changesets
1525 bundling: 3 changesets
1567 bundling: 3 changesets
1526 bundling: 4 changesets
1568 bundling: 4 changesets
1527 bundling: 1/4 manifests (25.00%)
1569 bundling: 1/4 manifests (25.00%)
1528 bundling: 2/4 manifests (50.00%)
1570 bundling: 2/4 manifests (50.00%)
1529 bundling: 3/4 manifests (75.00%)
1571 bundling: 3/4 manifests (75.00%)
1530 bundling: 4/4 manifests (100.00%)
1572 bundling: 4/4 manifests (100.00%)
1531 bundling: abc.txt 0/4 files (0.00%)
1573 bundling: abc.txt 0/4 files (0.00%)
1532 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1574 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1533 bundling: foo/file.txt 2/4 files (50.00%)
1575 bundling: foo/file.txt 2/4 files (50.00%)
1534 bundling: quux/file.py 3/4 files (75.00%)
1576 bundling: quux/file.py 3/4 files (75.00%)
1535 changesets: 1 chunks
1577 changesets: 1 chunks
1536 add changeset ef1ea85a6374
1578 add changeset ef1ea85a6374
1537 changesets: 2 chunks
1579 changesets: 2 chunks
1538 add changeset f9cafe1212c8
1580 add changeset f9cafe1212c8
1539 changesets: 3 chunks
1581 changesets: 3 chunks
1540 add changeset 911600dab2ae
1582 add changeset 911600dab2ae
1541 changesets: 4 chunks
1583 changesets: 4 chunks
1542 add changeset e8fc755d4d82
1584 add changeset e8fc755d4d82
1543 adding manifests
1585 adding manifests
1544 manifests: 1/4 chunks (25.00%)
1586 manifests: 1/4 chunks (25.00%)
1545 manifests: 2/4 chunks (50.00%)
1587 manifests: 2/4 chunks (50.00%)
1546 manifests: 3/4 chunks (75.00%)
1588 manifests: 3/4 chunks (75.00%)
1547 manifests: 4/4 chunks (100.00%)
1589 manifests: 4/4 chunks (100.00%)
1548 adding file changes
1590 adding file changes
1549 adding abc.txt revisions
1591 adding abc.txt revisions
1550 files: 1/4 chunks (25.00%)
1592 files: 1/4 chunks (25.00%)
1551 adding foo/Bar/file.txt revisions
1593 adding foo/Bar/file.txt revisions
1552 files: 2/4 chunks (50.00%)
1594 files: 2/4 chunks (50.00%)
1553 adding foo/file.txt revisions
1595 adding foo/file.txt revisions
1554 files: 3/4 chunks (75.00%)
1596 files: 3/4 chunks (75.00%)
1555 adding quux/file.py revisions
1597 adding quux/file.py revisions
1556 files: 4/4 chunks (100.00%)
1598 files: 4/4 chunks (100.00%)
1557 added 4 changesets with 4 changes to 4 files (+1 heads)
1599 added 4 changesets with 4 changes to 4 files (+1 heads)
1558 calling hook pretxnchangegroup.acl: hgext.acl.hook
1600 calling hook pretxnchangegroup.acl: hgext.acl.hook
1559 acl: acl.allow.branches enabled, 0 entries for user astro
1601 acl: acl.allow.branches enabled, 0 entries for user astro
1560 acl: acl.deny.branches not enabled
1602 acl: acl.deny.branches not enabled
1561 acl: acl.allow not enabled
1603 acl: acl.allow not enabled
1562 acl: acl.deny not enabled
1604 acl: acl.deny not enabled
1563 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1605 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1564 transaction abort!
1606 transaction abort!
1565 rollback completed
1607 rollback completed
1566 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1608 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1567 no rollback information available
1609 no rollback information available
1568 2:fb35475503ef
1610 2:fb35475503ef
1569
1611
1570
1612
1571 Branch acl allow other
1613 Branch acl allow other
1572
1614
1573 $ init_config
1615 $ init_config
1574 $ echo "[acl.allow.branches]" >> $config
1616 $ echo "[acl.allow.branches]" >> $config
1575 $ echo "* = george" >> $config
1617 $ echo "* = george" >> $config
1576 $ do_push astro
1618 $ do_push astro
1577 Pushing as user astro
1619 Pushing as user astro
1578 hgrc = """
1620 hgrc = """
1579 [acl]
1621 [acl]
1580 sources = push
1622 sources = push
1581 [extensions]
1623 [extensions]
1582 [acl.allow.branches]
1624 [acl.allow.branches]
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
1590 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1634 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1591 911600dab2ae7a9baff75958b84fe606851ce955
1635 911600dab2ae7a9baff75958b84fe606851ce955
1592 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1636 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1593 adding changesets
1637 adding changesets
1594 bundling: 1 changesets
1638 bundling: 1 changesets
1595 bundling: 2 changesets
1639 bundling: 2 changesets
1596 bundling: 3 changesets
1640 bundling: 3 changesets
1597 bundling: 4 changesets
1641 bundling: 4 changesets
1598 bundling: 1/4 manifests (25.00%)
1642 bundling: 1/4 manifests (25.00%)
1599 bundling: 2/4 manifests (50.00%)
1643 bundling: 2/4 manifests (50.00%)
1600 bundling: 3/4 manifests (75.00%)
1644 bundling: 3/4 manifests (75.00%)
1601 bundling: 4/4 manifests (100.00%)
1645 bundling: 4/4 manifests (100.00%)
1602 bundling: abc.txt 0/4 files (0.00%)
1646 bundling: abc.txt 0/4 files (0.00%)
1603 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1647 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1604 bundling: foo/file.txt 2/4 files (50.00%)
1648 bundling: foo/file.txt 2/4 files (50.00%)
1605 bundling: quux/file.py 3/4 files (75.00%)
1649 bundling: quux/file.py 3/4 files (75.00%)
1606 changesets: 1 chunks
1650 changesets: 1 chunks
1607 add changeset ef1ea85a6374
1651 add changeset ef1ea85a6374
1608 changesets: 2 chunks
1652 changesets: 2 chunks
1609 add changeset f9cafe1212c8
1653 add changeset f9cafe1212c8
1610 changesets: 3 chunks
1654 changesets: 3 chunks
1611 add changeset 911600dab2ae
1655 add changeset 911600dab2ae
1612 changesets: 4 chunks
1656 changesets: 4 chunks
1613 add changeset e8fc755d4d82
1657 add changeset e8fc755d4d82
1614 adding manifests
1658 adding manifests
1615 manifests: 1/4 chunks (25.00%)
1659 manifests: 1/4 chunks (25.00%)
1616 manifests: 2/4 chunks (50.00%)
1660 manifests: 2/4 chunks (50.00%)
1617 manifests: 3/4 chunks (75.00%)
1661 manifests: 3/4 chunks (75.00%)
1618 manifests: 4/4 chunks (100.00%)
1662 manifests: 4/4 chunks (100.00%)
1619 adding file changes
1663 adding file changes
1620 adding abc.txt revisions
1664 adding abc.txt revisions
1621 files: 1/4 chunks (25.00%)
1665 files: 1/4 chunks (25.00%)
1622 adding foo/Bar/file.txt revisions
1666 adding foo/Bar/file.txt revisions
1623 files: 2/4 chunks (50.00%)
1667 files: 2/4 chunks (50.00%)
1624 adding foo/file.txt revisions
1668 adding foo/file.txt revisions
1625 files: 3/4 chunks (75.00%)
1669 files: 3/4 chunks (75.00%)
1626 adding quux/file.py revisions
1670 adding quux/file.py revisions
1627 files: 4/4 chunks (100.00%)
1671 files: 4/4 chunks (100.00%)
1628 added 4 changesets with 4 changes to 4 files (+1 heads)
1672 added 4 changesets with 4 changes to 4 files (+1 heads)
1629 calling hook pretxnchangegroup.acl: hgext.acl.hook
1673 calling hook pretxnchangegroup.acl: hgext.acl.hook
1630 acl: acl.allow.branches enabled, 0 entries for user astro
1674 acl: acl.allow.branches enabled, 0 entries for user astro
1631 acl: acl.deny.branches not enabled
1675 acl: acl.deny.branches not enabled
1632 acl: acl.allow not enabled
1676 acl: acl.allow not enabled
1633 acl: acl.deny not enabled
1677 acl: acl.deny not enabled
1634 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1678 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1635 transaction abort!
1679 transaction abort!
1636 rollback completed
1680 rollback completed
1637 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1681 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1638 no rollback information available
1682 no rollback information available
1639 2:fb35475503ef
1683 2:fb35475503ef
1640
1684
1641 $ do_push george
1685 $ do_push george
1642 Pushing as user george
1686 Pushing as user george
1643 hgrc = """
1687 hgrc = """
1644 [acl]
1688 [acl]
1645 sources = push
1689 sources = push
1646 [extensions]
1690 [extensions]
1647 [acl.allow.branches]
1691 [acl.allow.branches]
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
1655 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1701 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1656 911600dab2ae7a9baff75958b84fe606851ce955
1702 911600dab2ae7a9baff75958b84fe606851ce955
1657 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1703 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1658 adding changesets
1704 adding changesets
1659 bundling: 1 changesets
1705 bundling: 1 changesets
1660 bundling: 2 changesets
1706 bundling: 2 changesets
1661 bundling: 3 changesets
1707 bundling: 3 changesets
1662 bundling: 4 changesets
1708 bundling: 4 changesets
1663 bundling: 1/4 manifests (25.00%)
1709 bundling: 1/4 manifests (25.00%)
1664 bundling: 2/4 manifests (50.00%)
1710 bundling: 2/4 manifests (50.00%)
1665 bundling: 3/4 manifests (75.00%)
1711 bundling: 3/4 manifests (75.00%)
1666 bundling: 4/4 manifests (100.00%)
1712 bundling: 4/4 manifests (100.00%)
1667 bundling: abc.txt 0/4 files (0.00%)
1713 bundling: abc.txt 0/4 files (0.00%)
1668 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1714 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1669 bundling: foo/file.txt 2/4 files (50.00%)
1715 bundling: foo/file.txt 2/4 files (50.00%)
1670 bundling: quux/file.py 3/4 files (75.00%)
1716 bundling: quux/file.py 3/4 files (75.00%)
1671 changesets: 1 chunks
1717 changesets: 1 chunks
1672 add changeset ef1ea85a6374
1718 add changeset ef1ea85a6374
1673 changesets: 2 chunks
1719 changesets: 2 chunks
1674 add changeset f9cafe1212c8
1720 add changeset f9cafe1212c8
1675 changesets: 3 chunks
1721 changesets: 3 chunks
1676 add changeset 911600dab2ae
1722 add changeset 911600dab2ae
1677 changesets: 4 chunks
1723 changesets: 4 chunks
1678 add changeset e8fc755d4d82
1724 add changeset e8fc755d4d82
1679 adding manifests
1725 adding manifests
1680 manifests: 1/4 chunks (25.00%)
1726 manifests: 1/4 chunks (25.00%)
1681 manifests: 2/4 chunks (50.00%)
1727 manifests: 2/4 chunks (50.00%)
1682 manifests: 3/4 chunks (75.00%)
1728 manifests: 3/4 chunks (75.00%)
1683 manifests: 4/4 chunks (100.00%)
1729 manifests: 4/4 chunks (100.00%)
1684 adding file changes
1730 adding file changes
1685 adding abc.txt revisions
1731 adding abc.txt revisions
1686 files: 1/4 chunks (25.00%)
1732 files: 1/4 chunks (25.00%)
1687 adding foo/Bar/file.txt revisions
1733 adding foo/Bar/file.txt revisions
1688 files: 2/4 chunks (50.00%)
1734 files: 2/4 chunks (50.00%)
1689 adding foo/file.txt revisions
1735 adding foo/file.txt revisions
1690 files: 3/4 chunks (75.00%)
1736 files: 3/4 chunks (75.00%)
1691 adding quux/file.py revisions
1737 adding quux/file.py revisions
1692 files: 4/4 chunks (100.00%)
1738 files: 4/4 chunks (100.00%)
1693 added 4 changesets with 4 changes to 4 files (+1 heads)
1739 added 4 changesets with 4 changes to 4 files (+1 heads)
1694 calling hook pretxnchangegroup.acl: hgext.acl.hook
1740 calling hook pretxnchangegroup.acl: hgext.acl.hook
1695 acl: acl.allow.branches enabled, 1 entries for user george
1741 acl: acl.allow.branches enabled, 1 entries for user george
1696 acl: acl.deny.branches not enabled
1742 acl: acl.deny.branches not enabled
1697 acl: acl.allow not enabled
1743 acl: acl.allow not enabled
1698 acl: acl.deny not enabled
1744 acl: acl.deny not enabled
1699 acl: branch access granted: "ef1ea85a6374" on branch "default"
1745 acl: branch access granted: "ef1ea85a6374" on branch "default"
1700 acl: allowing changeset ef1ea85a6374
1746 acl: allowing changeset ef1ea85a6374
1701 acl: branch access granted: "f9cafe1212c8" on branch "default"
1747 acl: branch access granted: "f9cafe1212c8" on branch "default"
1702 acl: allowing changeset f9cafe1212c8
1748 acl: allowing changeset f9cafe1212c8
1703 acl: branch access granted: "911600dab2ae" on branch "default"
1749 acl: branch access granted: "911600dab2ae" on branch "default"
1704 acl: allowing changeset 911600dab2ae
1750 acl: allowing changeset 911600dab2ae
1705 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1751 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1706 acl: allowing changeset e8fc755d4d82
1752 acl: allowing changeset e8fc755d4d82
1707 updating the branch cache
1753 updating the branch cache
1708 checking for updated bookmarks
1754 checking for updated bookmarks
1709 repository tip rolled back to revision 2 (undo push)
1755 repository tip rolled back to revision 2 (undo push)
1710 working directory now based on revision 2
1756 working directory now based on revision 2
1711 2:fb35475503ef
1757 2:fb35475503ef
1712
1758
1713
1759
1714 Branch acl conflicting allow
1760 Branch acl conflicting allow
1715 asterisk ends up applying to all branches and allowing george to
1761 asterisk ends up applying to all branches and allowing george to
1716 push foobar into the remote
1762 push foobar into the remote
1717
1763
1718 $ init_config
1764 $ init_config
1719 $ echo "[acl.allow.branches]" >> $config
1765 $ echo "[acl.allow.branches]" >> $config
1720 $ echo "foobar = astro" >> $config
1766 $ echo "foobar = astro" >> $config
1721 $ echo "* = george" >> $config
1767 $ echo "* = george" >> $config
1722 $ do_push george
1768 $ do_push george
1723 Pushing as user george
1769 Pushing as user george
1724 hgrc = """
1770 hgrc = """
1725 [acl]
1771 [acl]
1726 sources = push
1772 sources = push
1727 [extensions]
1773 [extensions]
1728 [acl.allow.branches]
1774 [acl.allow.branches]
1729 foobar = astro
1775 foobar = astro
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:
1737 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1785 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1738 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1786 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1739 911600dab2ae7a9baff75958b84fe606851ce955
1787 911600dab2ae7a9baff75958b84fe606851ce955
1740 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1788 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1741 adding changesets
1789 adding changesets
1742 bundling: 1 changesets
1790 bundling: 1 changesets
1743 bundling: 2 changesets
1791 bundling: 2 changesets
1744 bundling: 3 changesets
1792 bundling: 3 changesets
1745 bundling: 4 changesets
1793 bundling: 4 changesets
1746 bundling: 1/4 manifests (25.00%)
1794 bundling: 1/4 manifests (25.00%)
1747 bundling: 2/4 manifests (50.00%)
1795 bundling: 2/4 manifests (50.00%)
1748 bundling: 3/4 manifests (75.00%)
1796 bundling: 3/4 manifests (75.00%)
1749 bundling: 4/4 manifests (100.00%)
1797 bundling: 4/4 manifests (100.00%)
1750 bundling: abc.txt 0/4 files (0.00%)
1798 bundling: abc.txt 0/4 files (0.00%)
1751 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1799 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1752 bundling: foo/file.txt 2/4 files (50.00%)
1800 bundling: foo/file.txt 2/4 files (50.00%)
1753 bundling: quux/file.py 3/4 files (75.00%)
1801 bundling: quux/file.py 3/4 files (75.00%)
1754 changesets: 1 chunks
1802 changesets: 1 chunks
1755 add changeset ef1ea85a6374
1803 add changeset ef1ea85a6374
1756 changesets: 2 chunks
1804 changesets: 2 chunks
1757 add changeset f9cafe1212c8
1805 add changeset f9cafe1212c8
1758 changesets: 3 chunks
1806 changesets: 3 chunks
1759 add changeset 911600dab2ae
1807 add changeset 911600dab2ae
1760 changesets: 4 chunks
1808 changesets: 4 chunks
1761 add changeset e8fc755d4d82
1809 add changeset e8fc755d4d82
1762 adding manifests
1810 adding manifests
1763 manifests: 1/4 chunks (25.00%)
1811 manifests: 1/4 chunks (25.00%)
1764 manifests: 2/4 chunks (50.00%)
1812 manifests: 2/4 chunks (50.00%)
1765 manifests: 3/4 chunks (75.00%)
1813 manifests: 3/4 chunks (75.00%)
1766 manifests: 4/4 chunks (100.00%)
1814 manifests: 4/4 chunks (100.00%)
1767 adding file changes
1815 adding file changes
1768 adding abc.txt revisions
1816 adding abc.txt revisions
1769 files: 1/4 chunks (25.00%)
1817 files: 1/4 chunks (25.00%)
1770 adding foo/Bar/file.txt revisions
1818 adding foo/Bar/file.txt revisions
1771 files: 2/4 chunks (50.00%)
1819 files: 2/4 chunks (50.00%)
1772 adding foo/file.txt revisions
1820 adding foo/file.txt revisions
1773 files: 3/4 chunks (75.00%)
1821 files: 3/4 chunks (75.00%)
1774 adding quux/file.py revisions
1822 adding quux/file.py revisions
1775 files: 4/4 chunks (100.00%)
1823 files: 4/4 chunks (100.00%)
1776 added 4 changesets with 4 changes to 4 files (+1 heads)
1824 added 4 changesets with 4 changes to 4 files (+1 heads)
1777 calling hook pretxnchangegroup.acl: hgext.acl.hook
1825 calling hook pretxnchangegroup.acl: hgext.acl.hook
1778 acl: acl.allow.branches enabled, 1 entries for user george
1826 acl: acl.allow.branches enabled, 1 entries for user george
1779 acl: acl.deny.branches not enabled
1827 acl: acl.deny.branches not enabled
1780 acl: acl.allow not enabled
1828 acl: acl.allow not enabled
1781 acl: acl.deny not enabled
1829 acl: acl.deny not enabled
1782 acl: branch access granted: "ef1ea85a6374" on branch "default"
1830 acl: branch access granted: "ef1ea85a6374" on branch "default"
1783 acl: allowing changeset ef1ea85a6374
1831 acl: allowing changeset ef1ea85a6374
1784 acl: branch access granted: "f9cafe1212c8" on branch "default"
1832 acl: branch access granted: "f9cafe1212c8" on branch "default"
1785 acl: allowing changeset f9cafe1212c8
1833 acl: allowing changeset f9cafe1212c8
1786 acl: branch access granted: "911600dab2ae" on branch "default"
1834 acl: branch access granted: "911600dab2ae" on branch "default"
1787 acl: allowing changeset 911600dab2ae
1835 acl: allowing changeset 911600dab2ae
1788 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1836 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1789 acl: allowing changeset e8fc755d4d82
1837 acl: allowing changeset e8fc755d4d82
1790 updating the branch cache
1838 updating the branch cache
1791 checking for updated bookmarks
1839 checking for updated bookmarks
1792 repository tip rolled back to revision 2 (undo push)
1840 repository tip rolled back to revision 2 (undo push)
1793 working directory now based on revision 2
1841 working directory now based on revision 2
1794 2:fb35475503ef
1842 2:fb35475503ef
1795
1843
1796 Branch acl conflicting deny
1844 Branch acl conflicting deny
1797
1845
1798 $ init_config
1846 $ init_config
1799 $ echo "[acl.deny.branches]" >> $config
1847 $ echo "[acl.deny.branches]" >> $config
1800 $ echo "foobar = astro" >> $config
1848 $ echo "foobar = astro" >> $config
1801 $ echo "default = astro" >> $config
1849 $ echo "default = astro" >> $config
1802 $ echo "* = george" >> $config
1850 $ echo "* = george" >> $config
1803 $ do_push george
1851 $ do_push george
1804 Pushing as user george
1852 Pushing as user george
1805 hgrc = """
1853 hgrc = """
1806 [acl]
1854 [acl]
1807 sources = push
1855 sources = push
1808 [extensions]
1856 [extensions]
1809 [acl.deny.branches]
1857 [acl.deny.branches]
1810 foobar = astro
1858 foobar = astro
1811 default = astro
1859 default = astro
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:
1819 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1869 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1820 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1870 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1821 911600dab2ae7a9baff75958b84fe606851ce955
1871 911600dab2ae7a9baff75958b84fe606851ce955
1822 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1872 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1823 adding changesets
1873 adding changesets
1824 bundling: 1 changesets
1874 bundling: 1 changesets
1825 bundling: 2 changesets
1875 bundling: 2 changesets
1826 bundling: 3 changesets
1876 bundling: 3 changesets
1827 bundling: 4 changesets
1877 bundling: 4 changesets
1828 bundling: 1/4 manifests (25.00%)
1878 bundling: 1/4 manifests (25.00%)
1829 bundling: 2/4 manifests (50.00%)
1879 bundling: 2/4 manifests (50.00%)
1830 bundling: 3/4 manifests (75.00%)
1880 bundling: 3/4 manifests (75.00%)
1831 bundling: 4/4 manifests (100.00%)
1881 bundling: 4/4 manifests (100.00%)
1832 bundling: abc.txt 0/4 files (0.00%)
1882 bundling: abc.txt 0/4 files (0.00%)
1833 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1883 bundling: foo/Bar/file.txt 1/4 files (25.00%)
1834 bundling: foo/file.txt 2/4 files (50.00%)
1884 bundling: foo/file.txt 2/4 files (50.00%)
1835 bundling: quux/file.py 3/4 files (75.00%)
1885 bundling: quux/file.py 3/4 files (75.00%)
1836 changesets: 1 chunks
1886 changesets: 1 chunks
1837 add changeset ef1ea85a6374
1887 add changeset ef1ea85a6374
1838 changesets: 2 chunks
1888 changesets: 2 chunks
1839 add changeset f9cafe1212c8
1889 add changeset f9cafe1212c8
1840 changesets: 3 chunks
1890 changesets: 3 chunks
1841 add changeset 911600dab2ae
1891 add changeset 911600dab2ae
1842 changesets: 4 chunks
1892 changesets: 4 chunks
1843 add changeset e8fc755d4d82
1893 add changeset e8fc755d4d82
1844 adding manifests
1894 adding manifests
1845 manifests: 1/4 chunks (25.00%)
1895 manifests: 1/4 chunks (25.00%)
1846 manifests: 2/4 chunks (50.00%)
1896 manifests: 2/4 chunks (50.00%)
1847 manifests: 3/4 chunks (75.00%)
1897 manifests: 3/4 chunks (75.00%)
1848 manifests: 4/4 chunks (100.00%)
1898 manifests: 4/4 chunks (100.00%)
1849 adding file changes
1899 adding file changes
1850 adding abc.txt revisions
1900 adding abc.txt revisions
1851 files: 1/4 chunks (25.00%)
1901 files: 1/4 chunks (25.00%)
1852 adding foo/Bar/file.txt revisions
1902 adding foo/Bar/file.txt revisions
1853 files: 2/4 chunks (50.00%)
1903 files: 2/4 chunks (50.00%)
1854 adding foo/file.txt revisions
1904 adding foo/file.txt revisions
1855 files: 3/4 chunks (75.00%)
1905 files: 3/4 chunks (75.00%)
1856 adding quux/file.py revisions
1906 adding quux/file.py revisions
1857 files: 4/4 chunks (100.00%)
1907 files: 4/4 chunks (100.00%)
1858 added 4 changesets with 4 changes to 4 files (+1 heads)
1908 added 4 changesets with 4 changes to 4 files (+1 heads)
1859 calling hook pretxnchangegroup.acl: hgext.acl.hook
1909 calling hook pretxnchangegroup.acl: hgext.acl.hook
1860 acl: acl.allow.branches not enabled
1910 acl: acl.allow.branches not enabled
1861 acl: acl.deny.branches enabled, 1 entries for user george
1911 acl: acl.deny.branches enabled, 1 entries for user george
1862 acl: acl.allow not enabled
1912 acl: acl.allow not enabled
1863 acl: acl.deny not enabled
1913 acl: acl.deny not enabled
1864 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1914 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1865 transaction abort!
1915 transaction abort!
1866 rollback completed
1916 rollback completed
1867 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1917 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1868 no rollback information available
1918 no rollback information available
1869 2:fb35475503ef
1919 2:fb35475503ef
1870
1920
@@ -1,195 +1,193 b''
1 initialize
1 initialize
2
2
3 $ hg init a
3 $ hg init a
4 $ cd a
4 $ cd a
5 $ echo 'test' > test
5 $ echo 'test' > test
6 $ hg commit -Am'test'
6 $ hg commit -Am'test'
7 adding test
7 adding test
8
8
9 set bookmarks
9 set bookmarks
10
10
11 $ hg bookmark X
11 $ hg bookmark X
12 $ hg bookmark Y
12 $ hg bookmark Y
13 $ hg bookmark Z
13 $ hg bookmark Z
14
14
15 import bookmark by name
15 import bookmark by name
16
16
17 $ hg init ../b
17 $ hg init ../b
18 $ cd ../b
18 $ cd ../b
19 $ hg book Y
19 $ hg book Y
20 $ hg book
20 $ hg book
21 * Y -1:000000000000
21 * Y -1:000000000000
22 $ hg pull ../a
22 $ hg pull ../a
23 pulling from ../a
23 pulling from ../a
24 requesting all changes
24 requesting all changes
25 adding changesets
25 adding changesets
26 adding manifests
26 adding manifests
27 adding file changes
27 adding file changes
28 added 1 changesets with 1 changes to 1 files
28 added 1 changesets with 1 changes to 1 files
29 updating bookmark Y
29 updating bookmark Y
30 (run 'hg update' to get a working copy)
30 (run 'hg update' to get a working copy)
31 $ hg bookmarks
31 $ hg bookmarks
32 Y 0:4e3505fd9583
32 Y 0:4e3505fd9583
33 $ hg debugpushkey ../a namespaces
33 $ hg debugpushkey ../a namespaces
34 bookmarks
34 bookmarks
35 namespaces
35 namespaces
36 $ hg debugpushkey ../a bookmarks
36 $ hg debugpushkey ../a bookmarks
37 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
37 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
38 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
38 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
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
46 X 0:4e3505fd9583
45 X 0:4e3505fd9583
47 Y 0:4e3505fd9583
46 Y 0:4e3505fd9583
48
47
49 export bookmark by name
48 export bookmark by name
50
49
51 $ hg bookmark W
50 $ hg bookmark W
52 $ hg bookmark foo
51 $ hg bookmark foo
53 $ hg bookmark foobar
52 $ hg bookmark foobar
54 $ hg push -B W ../a
53 $ hg push -B W ../a
55 pushing to ../a
54 pushing to ../a
56 searching for changes
55 searching for changes
57 no changes found
56 no changes found
58 exporting bookmark W
57 exporting bookmark W
59 $ hg -R ../a bookmarks
58 $ hg -R ../a bookmarks
60 W -1:000000000000
59 W -1:000000000000
61 X 0:4e3505fd9583
60 X 0:4e3505fd9583
62 Y 0:4e3505fd9583
61 Y 0:4e3505fd9583
63 * Z 0:4e3505fd9583
62 * Z 0:4e3505fd9583
64
63
65 delete a remote bookmark
64 delete a remote bookmark
66
65
67 $ hg book -d W
66 $ hg book -d W
68 $ hg push -B W ../a
67 $ hg push -B W ../a
69 pushing to ../a
68 pushing to ../a
70 searching for changes
69 searching for changes
71 no changes found
70 no changes found
72 deleting remote bookmark W
71 deleting remote bookmark W
73
72
74 push/pull name that doesn't exist
73 push/pull name that doesn't exist
75
74
76 $ hg push -B badname ../a
75 $ hg push -B badname ../a
77 pushing to ../a
76 pushing to ../a
78 searching for changes
77 searching for changes
79 no changes found
78 no changes found
80 bookmark badname does not exist on the local or remote repository!
79 bookmark badname does not exist on the local or remote repository!
81 [2]
80 [2]
82 $ hg pull -B anotherbadname ../a
81 $ hg pull -B anotherbadname ../a
83 pulling from ../a
82 pulling from ../a
84 abort: remote bookmark anotherbadname not found!
83 abort: remote bookmark anotherbadname not found!
85 [255]
84 [255]
86
85
87 divergent bookmarks
86 divergent bookmarks
88
87
89 $ cd ../a
88 $ cd ../a
90 $ echo c1 > f1
89 $ echo c1 > f1
91 $ hg ci -Am1
90 $ hg ci -Am1
92 adding f1
91 adding f1
93 $ hg book -f X
92 $ hg book -f X
94 $ hg book
93 $ hg book
95 * X 1:0d2164f0ce0d
94 * X 1:0d2164f0ce0d
96 Y 0:4e3505fd9583
95 Y 0:4e3505fd9583
97 Z 1:0d2164f0ce0d
96 Z 1:0d2164f0ce0d
98
97
99 $ cd ../b
98 $ cd ../b
100 $ hg up
99 $ hg up
101 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
100 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 $ echo c2 > f2
101 $ echo c2 > f2
103 $ hg ci -Am2
102 $ hg ci -Am2
104 adding f2
103 adding f2
105 $ hg book -f X
104 $ hg book -f X
106 $ hg book
105 $ hg book
107 * X 1:9b140be10808
106 * X 1:9b140be10808
108 Y 0:4e3505fd9583
107 Y 0:4e3505fd9583
109 foo -1:000000000000
108 foo -1:000000000000
110 foobar -1:000000000000
109 foobar -1:000000000000
111
110
112 $ hg pull ../a
111 $ hg pull ../a
113 pulling from ../a
112 pulling from ../a
114 searching for changes
113 searching for changes
115 adding changesets
114 adding changesets
116 adding manifests
115 adding manifests
117 adding file changes
116 adding file changes
118 added 1 changesets with 1 changes to 1 files (+1 heads)
117 added 1 changesets with 1 changes to 1 files (+1 heads)
119 not updating divergent bookmark X
118 not updating divergent bookmark X
120 (run 'hg heads' to see heads, 'hg merge' to merge)
119 (run 'hg heads' to see heads, 'hg merge' to merge)
121 $ hg book
120 $ hg book
122 * X 1:9b140be10808
121 * X 1:9b140be10808
123 Y 0:4e3505fd9583
122 Y 0:4e3505fd9583
124 foo -1:000000000000
123 foo -1:000000000000
125 foobar -1:000000000000
124 foobar -1:000000000000
126 $ hg push -f ../a
125 $ hg push -f ../a
127 pushing to ../a
126 pushing to ../a
128 searching for changes
127 searching for changes
129 adding changesets
128 adding changesets
130 adding manifests
129 adding manifests
131 adding file changes
130 adding file changes
132 added 1 changesets with 1 changes to 1 files (+1 heads)
131 added 1 changesets with 1 changes to 1 files (+1 heads)
133 $ hg -R ../a book
132 $ hg -R ../a book
134 * X 1:0d2164f0ce0d
133 * X 1:0d2164f0ce0d
135 Y 0:4e3505fd9583
134 Y 0:4e3505fd9583
136 Z 1:0d2164f0ce0d
135 Z 1:0d2164f0ce0d
137
136
138 hgweb
137 hgweb
139
138
140 $ cat <<EOF > .hg/hgrc
139 $ cat <<EOF > .hg/hgrc
141 > [web]
140 > [web]
142 > push_ssl = false
141 > push_ssl = false
143 > allow_push = *
142 > allow_push = *
144 > EOF
143 > EOF
145
144
146 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
145 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
147 $ cat ../hg.pid >> $DAEMON_PIDS
146 $ cat ../hg.pid >> $DAEMON_PIDS
148 $ cd ../a
147 $ cd ../a
149
148
150 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
149 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
151 bookmarks
150 bookmarks
152 namespaces
151 namespaces
153 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
152 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
154 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
153 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
155 X 9b140be1080824d768c5a4691a564088eede71f9
154 X 9b140be1080824d768c5a4691a564088eede71f9
156 foo 0000000000000000000000000000000000000000
155 foo 0000000000000000000000000000000000000000
157 foobar 0000000000000000000000000000000000000000
156 foobar 0000000000000000000000000000000000000000
158 $ hg out -B http://localhost:$HGPORT/
157 $ hg out -B http://localhost:$HGPORT/
159 comparing with http://localhost:$HGPORT/
158 comparing with http://localhost:$HGPORT/
160 searching for changed bookmarks
159 searching for changed bookmarks
161 Z 0d2164f0ce0d
160 Z 0d2164f0ce0d
162 $ hg push -B Z http://localhost:$HGPORT/
161 $ hg push -B Z http://localhost:$HGPORT/
163 pushing to http://localhost:$HGPORT/
162 pushing to http://localhost:$HGPORT/
164 searching for changes
163 searching for changes
165 no changes found
164 no changes found
166 exporting bookmark Z
165 exporting bookmark Z
167 $ hg book -d Z
166 $ hg book -d Z
168 $ hg in -B http://localhost:$HGPORT/
167 $ hg in -B http://localhost:$HGPORT/
169 comparing with http://localhost:$HGPORT/
168 comparing with http://localhost:$HGPORT/
170 searching for changed bookmarks
169 searching for changed bookmarks
171 Z 0d2164f0ce0d
170 Z 0d2164f0ce0d
172 foo 000000000000
171 foo 000000000000
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
180 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
178 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
181 requesting all changes
179 requesting all changes
182 adding changesets
180 adding changesets
183 adding manifests
181 adding manifests
184 adding file changes
182 adding file changes
185 added 3 changesets with 3 changes to 3 files (+1 heads)
183 added 3 changesets with 3 changes to 3 files (+1 heads)
186 updating to branch default
184 updating to branch default
187 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
185 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
188 $ hg -R cloned-bookmarks bookmarks
186 $ hg -R cloned-bookmarks bookmarks
189 X 1:9b140be10808
187 X 1:9b140be10808
190 Y 0:4e3505fd9583
188 Y 0:4e3505fd9583
191 Z 2:0d2164f0ce0d
189 Z 2:0d2164f0ce0d
192 foo -1:000000000000
190 foo -1:000000000000
193 foobar -1:000000000000
191 foobar -1:000000000000
194
192
195 $ kill `cat ../hg.pid`
193 $ kill `cat ../hg.pid`
@@ -1,575 +1,577 b''
1 Setting up test
1 Setting up test
2
2
3 $ hg init test
3 $ hg init test
4 $ cd test
4 $ cd test
5 $ echo 0 > afile
5 $ echo 0 > afile
6 $ hg add afile
6 $ hg add afile
7 $ hg commit -m "0.0"
7 $ hg commit -m "0.0"
8 $ echo 1 >> afile
8 $ echo 1 >> afile
9 $ hg commit -m "0.1"
9 $ hg commit -m "0.1"
10 $ echo 2 >> afile
10 $ echo 2 >> afile
11 $ hg commit -m "0.2"
11 $ hg commit -m "0.2"
12 $ echo 3 >> afile
12 $ echo 3 >> afile
13 $ hg commit -m "0.3"
13 $ hg commit -m "0.3"
14 $ hg update -C 0
14 $ hg update -C 0
15 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
15 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
16 $ echo 1 >> afile
16 $ echo 1 >> afile
17 $ hg commit -m "1.1"
17 $ hg commit -m "1.1"
18 created new head
18 created new head
19 $ echo 2 >> afile
19 $ echo 2 >> afile
20 $ hg commit -m "1.2"
20 $ hg commit -m "1.2"
21 $ echo "a line" > fred
21 $ echo "a line" > fred
22 $ echo 3 >> afile
22 $ echo 3 >> afile
23 $ hg add fred
23 $ hg add fred
24 $ hg commit -m "1.3"
24 $ hg commit -m "1.3"
25 $ hg mv afile adifferentfile
25 $ hg mv afile adifferentfile
26 $ hg commit -m "1.3m"
26 $ hg commit -m "1.3m"
27 $ hg update -C 3
27 $ hg update -C 3
28 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
28 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
29 $ hg mv afile anotherfile
29 $ hg mv afile anotherfile
30 $ hg commit -m "0.3m"
30 $ hg commit -m "0.3m"
31 $ hg verify
31 $ hg verify
32 checking changesets
32 checking changesets
33 checking manifests
33 checking manifests
34 crosschecking files in changesets and manifests
34 crosschecking files in changesets and manifests
35 checking files
35 checking files
36 4 files, 9 changesets, 7 total revisions
36 4 files, 9 changesets, 7 total revisions
37 $ cd ..
37 $ cd ..
38 $ hg init empty
38 $ hg init empty
39
39
40 Bundle --all
40 Bundle --all
41
41
42 $ hg -R test bundle --all all.hg
42 $ hg -R test bundle --all all.hg
43 9 changesets found
43 9 changesets found
44
44
45 Bundle test to full.hg
45 Bundle test to full.hg
46
46
47 $ hg -R test bundle full.hg empty
47 $ hg -R test bundle full.hg empty
48 searching for changes
48 searching for changes
49 9 changesets found
49 9 changesets found
50
50
51 Unbundle full.hg in test
51 Unbundle full.hg in test
52
52
53 $ hg -R test unbundle full.hg
53 $ hg -R test unbundle full.hg
54 adding changesets
54 adding changesets
55 adding manifests
55 adding manifests
56 adding file changes
56 adding file changes
57 added 0 changesets with 0 changes to 4 files
57 added 0 changesets with 0 changes to 4 files
58 (run 'hg update' to get a working copy)
58 (run 'hg update' to get a working copy)
59
59
60 Verify empty
60 Verify empty
61
61
62 $ hg -R empty heads
62 $ hg -R empty heads
63 [1]
63 [1]
64 $ hg -R empty verify
64 $ hg -R empty verify
65 checking changesets
65 checking changesets
66 checking manifests
66 checking manifests
67 crosschecking files in changesets and manifests
67 crosschecking files in changesets and manifests
68 checking files
68 checking files
69 0 files, 0 changesets, 0 total revisions
69 0 files, 0 changesets, 0 total revisions
70
70
71 Pull full.hg into test (using --cwd)
71 Pull full.hg into test (using --cwd)
72
72
73 $ hg --cwd test pull ../full.hg
73 $ hg --cwd test pull ../full.hg
74 pulling from ../full.hg
74 pulling from ../full.hg
75 searching for changes
75 searching for changes
76 no changes found
76 no changes found
77
77
78 Pull full.hg into empty (using --cwd)
78 Pull full.hg into empty (using --cwd)
79
79
80 $ hg --cwd empty pull ../full.hg
80 $ hg --cwd empty pull ../full.hg
81 pulling from ../full.hg
81 pulling from ../full.hg
82 requesting all changes
82 requesting all changes
83 adding changesets
83 adding changesets
84 adding manifests
84 adding manifests
85 adding file changes
85 adding file changes
86 added 9 changesets with 7 changes to 4 files (+1 heads)
86 added 9 changesets with 7 changes to 4 files (+1 heads)
87 (run 'hg heads' to see heads, 'hg merge' to merge)
87 (run 'hg heads' to see heads, 'hg merge' to merge)
88
88
89 Rollback empty
89 Rollback empty
90
90
91 $ hg -R empty rollback
91 $ hg -R empty rollback
92 repository tip rolled back to revision -1 (undo pull)
92 repository tip rolled back to revision -1 (undo pull)
93 working directory now based on revision -1
93 working directory now based on revision -1
94
94
95 Pull full.hg into empty again (using --cwd)
95 Pull full.hg into empty again (using --cwd)
96
96
97 $ hg --cwd empty pull ../full.hg
97 $ hg --cwd empty pull ../full.hg
98 pulling from ../full.hg
98 pulling from ../full.hg
99 requesting all changes
99 requesting all changes
100 adding changesets
100 adding changesets
101 adding manifests
101 adding manifests
102 adding file changes
102 adding file changes
103 added 9 changesets with 7 changes to 4 files (+1 heads)
103 added 9 changesets with 7 changes to 4 files (+1 heads)
104 (run 'hg heads' to see heads, 'hg merge' to merge)
104 (run 'hg heads' to see heads, 'hg merge' to merge)
105
105
106 Pull full.hg into test (using -R)
106 Pull full.hg into test (using -R)
107
107
108 $ hg -R test pull full.hg
108 $ hg -R test pull full.hg
109 pulling from full.hg
109 pulling from full.hg
110 searching for changes
110 searching for changes
111 no changes found
111 no changes found
112
112
113 Pull full.hg into empty (using -R)
113 Pull full.hg into empty (using -R)
114
114
115 $ hg -R empty pull full.hg
115 $ hg -R empty pull full.hg
116 pulling from full.hg
116 pulling from full.hg
117 searching for changes
117 searching for changes
118 no changes found
118 no changes found
119
119
120 Rollback empty
120 Rollback empty
121
121
122 $ hg -R empty rollback
122 $ hg -R empty rollback
123 repository tip rolled back to revision -1 (undo pull)
123 repository tip rolled back to revision -1 (undo pull)
124 working directory now based on revision -1
124 working directory now based on revision -1
125
125
126 Pull full.hg into empty again (using -R)
126 Pull full.hg into empty again (using -R)
127
127
128 $ hg -R empty pull full.hg
128 $ hg -R empty pull full.hg
129 pulling from full.hg
129 pulling from full.hg
130 requesting all changes
130 requesting all changes
131 adding changesets
131 adding changesets
132 adding manifests
132 adding manifests
133 adding file changes
133 adding file changes
134 added 9 changesets with 7 changes to 4 files (+1 heads)
134 added 9 changesets with 7 changes to 4 files (+1 heads)
135 (run 'hg heads' to see heads, 'hg merge' to merge)
135 (run 'hg heads' to see heads, 'hg merge' to merge)
136
136
137 Log -R full.hg in fresh empty
137 Log -R full.hg in fresh empty
138
138
139 $ rm -r empty
139 $ rm -r empty
140 $ hg init empty
140 $ hg init empty
141 $ cd empty
141 $ cd empty
142 $ hg -R bundle://../full.hg log
142 $ hg -R bundle://../full.hg log
143 changeset: 8:aa35859c02ea
143 changeset: 8:aa35859c02ea
144 tag: tip
144 tag: tip
145 parent: 3:eebf5a27f8ca
145 parent: 3:eebf5a27f8ca
146 user: test
146 user: test
147 date: Thu Jan 01 00:00:00 1970 +0000
147 date: Thu Jan 01 00:00:00 1970 +0000
148 summary: 0.3m
148 summary: 0.3m
149
149
150 changeset: 7:a6a34bfa0076
150 changeset: 7:a6a34bfa0076
151 user: test
151 user: test
152 date: Thu Jan 01 00:00:00 1970 +0000
152 date: Thu Jan 01 00:00:00 1970 +0000
153 summary: 1.3m
153 summary: 1.3m
154
154
155 changeset: 6:7373c1169842
155 changeset: 6:7373c1169842
156 user: test
156 user: test
157 date: Thu Jan 01 00:00:00 1970 +0000
157 date: Thu Jan 01 00:00:00 1970 +0000
158 summary: 1.3
158 summary: 1.3
159
159
160 changeset: 5:1bb50a9436a7
160 changeset: 5:1bb50a9436a7
161 user: test
161 user: test
162 date: Thu Jan 01 00:00:00 1970 +0000
162 date: Thu Jan 01 00:00:00 1970 +0000
163 summary: 1.2
163 summary: 1.2
164
164
165 changeset: 4:095197eb4973
165 changeset: 4:095197eb4973
166 parent: 0:f9ee2f85a263
166 parent: 0:f9ee2f85a263
167 user: test
167 user: test
168 date: Thu Jan 01 00:00:00 1970 +0000
168 date: Thu Jan 01 00:00:00 1970 +0000
169 summary: 1.1
169 summary: 1.1
170
170
171 changeset: 3:eebf5a27f8ca
171 changeset: 3:eebf5a27f8ca
172 user: test
172 user: test
173 date: Thu Jan 01 00:00:00 1970 +0000
173 date: Thu Jan 01 00:00:00 1970 +0000
174 summary: 0.3
174 summary: 0.3
175
175
176 changeset: 2:e38ba6f5b7e0
176 changeset: 2:e38ba6f5b7e0
177 user: test
177 user: test
178 date: Thu Jan 01 00:00:00 1970 +0000
178 date: Thu Jan 01 00:00:00 1970 +0000
179 summary: 0.2
179 summary: 0.2
180
180
181 changeset: 1:34c2bf6b0626
181 changeset: 1:34c2bf6b0626
182 user: test
182 user: test
183 date: Thu Jan 01 00:00:00 1970 +0000
183 date: Thu Jan 01 00:00:00 1970 +0000
184 summary: 0.1
184 summary: 0.1
185
185
186 changeset: 0:f9ee2f85a263
186 changeset: 0:f9ee2f85a263
187 user: test
187 user: test
188 date: Thu Jan 01 00:00:00 1970 +0000
188 date: Thu Jan 01 00:00:00 1970 +0000
189 summary: 0.0
189 summary: 0.0
190
190
191 Make sure bundlerepo doesn't leak tempfiles (issue2491)
191 Make sure bundlerepo doesn't leak tempfiles (issue2491)
192
192
193 $ ls .hg
193 $ ls .hg
194 00changelog.i
194 00changelog.i
195 cache
195 cache
196 requires
196 requires
197 store
197 store
198
198
199 Pull ../full.hg into empty (with hook)
199 Pull ../full.hg into empty (with hook)
200
200
201 $ echo '[hooks]' >> .hg/hgrc
201 $ echo '[hooks]' >> .hg/hgrc
202 $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup' >> .hg/hgrc
202 $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup' >> .hg/hgrc
203
203
204 doesn't work (yet ?)
204 doesn't work (yet ?)
205
205
206 hg -R bundle://../full.hg verify
206 hg -R bundle://../full.hg verify
207
207
208 $ hg pull bundle://../full.hg
208 $ hg pull bundle://../full.hg
209 pulling from bundle:../full.hg
209 pulling from bundle:../full.hg
210 requesting all changes
210 requesting all changes
211 adding changesets
211 adding changesets
212 adding manifests
212 adding manifests
213 adding file changes
213 adding file changes
214 added 9 changesets with 7 changes to 4 files (+1 heads)
214 added 9 changesets with 7 changes to 4 files (+1 heads)
215 changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:../full.hg
215 changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:../full.hg
216 (run 'hg heads' to see heads, 'hg merge' to merge)
216 (run 'hg heads' to see heads, 'hg merge' to merge)
217
217
218 Rollback empty
218 Rollback empty
219
219
220 $ hg rollback
220 $ hg rollback
221 repository tip rolled back to revision -1 (undo pull)
221 repository tip rolled back to revision -1 (undo pull)
222 working directory now based on revision -1
222 working directory now based on revision -1
223 $ cd ..
223 $ cd ..
224
224
225 Log -R bundle:empty+full.hg
225 Log -R bundle:empty+full.hg
226
226
227 $ hg -R bundle:empty+full.hg log --template="{rev} "; echo ""
227 $ hg -R bundle:empty+full.hg log --template="{rev} "; echo ""
228 8 7 6 5 4 3 2 1 0
228 8 7 6 5 4 3 2 1 0
229
229
230 Pull full.hg into empty again (using -R; with hook)
230 Pull full.hg into empty again (using -R; with hook)
231
231
232 $ hg -R empty pull full.hg
232 $ hg -R empty pull full.hg
233 pulling from full.hg
233 pulling from full.hg
234 requesting all changes
234 requesting all changes
235 adding changesets
235 adding changesets
236 adding manifests
236 adding manifests
237 adding file changes
237 adding file changes
238 added 9 changesets with 7 changes to 4 files (+1 heads)
238 added 9 changesets with 7 changes to 4 files (+1 heads)
239 changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:empty+full.hg
239 changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:empty+full.hg
240 (run 'hg heads' to see heads, 'hg merge' to merge)
240 (run 'hg heads' to see heads, 'hg merge' to merge)
241
241
242 Create partial clones
242 Create partial clones
243
243
244 $ rm -r empty
244 $ rm -r empty
245 $ hg init empty
245 $ hg init empty
246 $ hg clone -r 3 test partial
246 $ hg clone -r 3 test partial
247 adding changesets
247 adding changesets
248 adding manifests
248 adding manifests
249 adding file changes
249 adding file changes
250 added 4 changesets with 4 changes to 1 files
250 added 4 changesets with 4 changes to 1 files
251 updating to branch default
251 updating to branch default
252 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
252 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
253 $ hg clone partial partial2
253 $ hg clone partial partial2
254 updating to branch default
254 updating to branch default
255 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
255 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
256 $ cd partial
256 $ cd partial
257
257
258 Log -R full.hg in partial
258 Log -R full.hg in partial
259
259
260 $ hg -R bundle://../full.hg log
260 $ hg -R bundle://../full.hg log
261 changeset: 8:aa35859c02ea
261 changeset: 8:aa35859c02ea
262 tag: tip
262 tag: tip
263 parent: 3:eebf5a27f8ca
263 parent: 3:eebf5a27f8ca
264 user: test
264 user: test
265 date: Thu Jan 01 00:00:00 1970 +0000
265 date: Thu Jan 01 00:00:00 1970 +0000
266 summary: 0.3m
266 summary: 0.3m
267
267
268 changeset: 7:a6a34bfa0076
268 changeset: 7:a6a34bfa0076
269 user: test
269 user: test
270 date: Thu Jan 01 00:00:00 1970 +0000
270 date: Thu Jan 01 00:00:00 1970 +0000
271 summary: 1.3m
271 summary: 1.3m
272
272
273 changeset: 6:7373c1169842
273 changeset: 6:7373c1169842
274 user: test
274 user: test
275 date: Thu Jan 01 00:00:00 1970 +0000
275 date: Thu Jan 01 00:00:00 1970 +0000
276 summary: 1.3
276 summary: 1.3
277
277
278 changeset: 5:1bb50a9436a7
278 changeset: 5:1bb50a9436a7
279 user: test
279 user: test
280 date: Thu Jan 01 00:00:00 1970 +0000
280 date: Thu Jan 01 00:00:00 1970 +0000
281 summary: 1.2
281 summary: 1.2
282
282
283 changeset: 4:095197eb4973
283 changeset: 4:095197eb4973
284 parent: 0:f9ee2f85a263
284 parent: 0:f9ee2f85a263
285 user: test
285 user: test
286 date: Thu Jan 01 00:00:00 1970 +0000
286 date: Thu Jan 01 00:00:00 1970 +0000
287 summary: 1.1
287 summary: 1.1
288
288
289 changeset: 3:eebf5a27f8ca
289 changeset: 3:eebf5a27f8ca
290 user: test
290 user: test
291 date: Thu Jan 01 00:00:00 1970 +0000
291 date: Thu Jan 01 00:00:00 1970 +0000
292 summary: 0.3
292 summary: 0.3
293
293
294 changeset: 2:e38ba6f5b7e0
294 changeset: 2:e38ba6f5b7e0
295 user: test
295 user: test
296 date: Thu Jan 01 00:00:00 1970 +0000
296 date: Thu Jan 01 00:00:00 1970 +0000
297 summary: 0.2
297 summary: 0.2
298
298
299 changeset: 1:34c2bf6b0626
299 changeset: 1:34c2bf6b0626
300 user: test
300 user: test
301 date: Thu Jan 01 00:00:00 1970 +0000
301 date: Thu Jan 01 00:00:00 1970 +0000
302 summary: 0.1
302 summary: 0.1
303
303
304 changeset: 0:f9ee2f85a263
304 changeset: 0:f9ee2f85a263
305 user: test
305 user: test
306 date: Thu Jan 01 00:00:00 1970 +0000
306 date: Thu Jan 01 00:00:00 1970 +0000
307 summary: 0.0
307 summary: 0.0
308
308
309
309
310 Incoming full.hg in partial
310 Incoming full.hg in partial
311
311
312 $ hg incoming bundle://../full.hg
312 $ hg incoming bundle://../full.hg
313 comparing with bundle:../full.hg
313 comparing with bundle:../full.hg
314 searching for changes
314 searching for changes
315 changeset: 4:095197eb4973
315 changeset: 4:095197eb4973
316 parent: 0:f9ee2f85a263
316 parent: 0:f9ee2f85a263
317 user: test
317 user: test
318 date: Thu Jan 01 00:00:00 1970 +0000
318 date: Thu Jan 01 00:00:00 1970 +0000
319 summary: 1.1
319 summary: 1.1
320
320
321 changeset: 5:1bb50a9436a7
321 changeset: 5:1bb50a9436a7
322 user: test
322 user: test
323 date: Thu Jan 01 00:00:00 1970 +0000
323 date: Thu Jan 01 00:00:00 1970 +0000
324 summary: 1.2
324 summary: 1.2
325
325
326 changeset: 6:7373c1169842
326 changeset: 6:7373c1169842
327 user: test
327 user: test
328 date: Thu Jan 01 00:00:00 1970 +0000
328 date: Thu Jan 01 00:00:00 1970 +0000
329 summary: 1.3
329 summary: 1.3
330
330
331 changeset: 7:a6a34bfa0076
331 changeset: 7:a6a34bfa0076
332 user: test
332 user: test
333 date: Thu Jan 01 00:00:00 1970 +0000
333 date: Thu Jan 01 00:00:00 1970 +0000
334 summary: 1.3m
334 summary: 1.3m
335
335
336 changeset: 8:aa35859c02ea
336 changeset: 8:aa35859c02ea
337 tag: tip
337 tag: tip
338 parent: 3:eebf5a27f8ca
338 parent: 3:eebf5a27f8ca
339 user: test
339 user: test
340 date: Thu Jan 01 00:00:00 1970 +0000
340 date: Thu Jan 01 00:00:00 1970 +0000
341 summary: 0.3m
341 summary: 0.3m
342
342
343
343
344 Outgoing -R full.hg vs partial2 in partial
344 Outgoing -R full.hg vs partial2 in partial
345
345
346 $ hg -R bundle://../full.hg outgoing ../partial2
346 $ hg -R bundle://../full.hg outgoing ../partial2
347 comparing with ../partial2
347 comparing with ../partial2
348 searching for changes
348 searching for changes
349 changeset: 4:095197eb4973
349 changeset: 4:095197eb4973
350 parent: 0:f9ee2f85a263
350 parent: 0:f9ee2f85a263
351 user: test
351 user: test
352 date: Thu Jan 01 00:00:00 1970 +0000
352 date: Thu Jan 01 00:00:00 1970 +0000
353 summary: 1.1
353 summary: 1.1
354
354
355 changeset: 5:1bb50a9436a7
355 changeset: 5:1bb50a9436a7
356 user: test
356 user: test
357 date: Thu Jan 01 00:00:00 1970 +0000
357 date: Thu Jan 01 00:00:00 1970 +0000
358 summary: 1.2
358 summary: 1.2
359
359
360 changeset: 6:7373c1169842
360 changeset: 6:7373c1169842
361 user: test
361 user: test
362 date: Thu Jan 01 00:00:00 1970 +0000
362 date: Thu Jan 01 00:00:00 1970 +0000
363 summary: 1.3
363 summary: 1.3
364
364
365 changeset: 7:a6a34bfa0076
365 changeset: 7:a6a34bfa0076
366 user: test
366 user: test
367 date: Thu Jan 01 00:00:00 1970 +0000
367 date: Thu Jan 01 00:00:00 1970 +0000
368 summary: 1.3m
368 summary: 1.3m
369
369
370 changeset: 8:aa35859c02ea
370 changeset: 8:aa35859c02ea
371 tag: tip
371 tag: tip
372 parent: 3:eebf5a27f8ca
372 parent: 3:eebf5a27f8ca
373 user: test
373 user: test
374 date: Thu Jan 01 00:00:00 1970 +0000
374 date: Thu Jan 01 00:00:00 1970 +0000
375 summary: 0.3m
375 summary: 0.3m
376
376
377
377
378 Outgoing -R does-not-exist.hg vs partial2 in partial
378 Outgoing -R does-not-exist.hg vs partial2 in partial
379
379
380 $ hg -R bundle://../does-not-exist.hg outgoing ../partial2
380 $ hg -R bundle://../does-not-exist.hg outgoing ../partial2
381 abort: No such file or directory: ../does-not-exist.hg
381 abort: No such file or directory: ../does-not-exist.hg
382 [255]
382 [255]
383 $ cd ..
383 $ cd ..
384
384
385 Direct clone from bundle (all-history)
385 Direct clone from bundle (all-history)
386
386
387 $ hg clone full.hg full-clone
387 $ hg clone full.hg full-clone
388 requesting all changes
388 requesting all changes
389 adding changesets
389 adding changesets
390 adding manifests
390 adding manifests
391 adding file changes
391 adding file changes
392 added 9 changesets with 7 changes to 4 files (+1 heads)
392 added 9 changesets with 7 changes to 4 files (+1 heads)
393 updating to branch default
393 updating to branch default
394 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
394 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
395 $ hg -R full-clone heads
395 $ hg -R full-clone heads
396 changeset: 8:aa35859c02ea
396 changeset: 8:aa35859c02ea
397 tag: tip
397 tag: tip
398 parent: 3:eebf5a27f8ca
398 parent: 3:eebf5a27f8ca
399 user: test
399 user: test
400 date: Thu Jan 01 00:00:00 1970 +0000
400 date: Thu Jan 01 00:00:00 1970 +0000
401 summary: 0.3m
401 summary: 0.3m
402
402
403 changeset: 7:a6a34bfa0076
403 changeset: 7:a6a34bfa0076
404 user: test
404 user: test
405 date: Thu Jan 01 00:00:00 1970 +0000
405 date: Thu Jan 01 00:00:00 1970 +0000
406 summary: 1.3m
406 summary: 1.3m
407
407
408 $ rm -r full-clone
408 $ rm -r full-clone
409
409
410 When cloning from a non-copiable repository into '', do not
410 When cloning from a non-copiable repository into '', do not
411 recurse infinitely (issue 2528)
411 recurse infinitely (issue 2528)
412
412
413 $ hg clone full.hg ''
413 $ hg clone full.hg ''
414 abort: No such file or directory
414 abort: No such file or directory
415 [255]
415 [255]
416
416
417 test for http://mercurial.selenic.com/bts/issue216
417 test for http://mercurial.selenic.com/bts/issue216
418
418
419 Unbundle incremental bundles into fresh empty in one go
419 Unbundle incremental bundles into fresh empty in one go
420
420
421 $ rm -r empty
421 $ rm -r empty
422 $ hg init empty
422 $ hg init empty
423 $ hg -R test bundle --base null -r 0 ../0.hg
423 $ hg -R test bundle --base null -r 0 ../0.hg
424 1 changesets found
424 1 changesets found
425 $ hg -R test bundle --base 0 -r 1 ../1.hg
425 $ hg -R test bundle --base 0 -r 1 ../1.hg
426 1 changesets found
426 1 changesets found
427 $ hg -R empty unbundle -u ../0.hg ../1.hg
427 $ hg -R empty unbundle -u ../0.hg ../1.hg
428 adding changesets
428 adding changesets
429 adding manifests
429 adding manifests
430 adding file changes
430 adding file changes
431 added 1 changesets with 1 changes to 1 files
431 added 1 changesets with 1 changes to 1 files
432 adding changesets
432 adding changesets
433 adding manifests
433 adding manifests
434 adding file changes
434 adding file changes
435 added 1 changesets with 1 changes to 1 files
435 added 1 changesets with 1 changes to 1 files
436 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
436 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
437
437
438 test for 540d1059c802
438 test for 540d1059c802
439
439
440 test for 540d1059c802
440 test for 540d1059c802
441
441
442 $ hg init orig
442 $ hg init orig
443 $ cd orig
443 $ cd orig
444 $ echo foo > foo
444 $ echo foo > foo
445 $ hg add foo
445 $ hg add foo
446 $ hg ci -m 'add foo'
446 $ hg ci -m 'add foo'
447
447
448 $ hg clone . ../copy
448 $ hg clone . ../copy
449 updating to branch default
449 updating to branch default
450 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
450 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
451 $ hg tag foo
451 $ hg tag foo
452
452
453 $ cd ../copy
453 $ cd ../copy
454 $ echo >> foo
454 $ echo >> foo
455 $ hg ci -m 'change foo'
455 $ hg ci -m 'change foo'
456 $ hg bundle ../bundle.hg ../orig
456 $ hg bundle ../bundle.hg ../orig
457 searching for changes
457 searching for changes
458 1 changesets found
458 1 changesets found
459
459
460 $ cd ../orig
460 $ cd ../orig
461 $ hg incoming ../bundle.hg
461 $ hg incoming ../bundle.hg
462 comparing with ../bundle.hg
462 comparing with ../bundle.hg
463 searching for changes
463 searching for changes
464 changeset: 2:ed1b79f46b9a
464 changeset: 2:ed1b79f46b9a
465 tag: tip
465 tag: tip
466 parent: 0:bbd179dfa0a7
466 parent: 0:bbd179dfa0a7
467 user: test
467 user: test
468 date: Thu Jan 01 00:00:00 1970 +0000
468 date: Thu Jan 01 00:00:00 1970 +0000
469 summary: change foo
469 summary: change foo
470
470
471 $ cd ..
471 $ cd ..
472
472
473 test bundle with # in the filename (issue2154):
473 test bundle with # in the filename (issue2154):
474
474
475 $ cp bundle.hg 'test#bundle.hg'
475 $ cp bundle.hg 'test#bundle.hg'
476 $ cd orig
476 $ cd orig
477 $ hg incoming '../test#bundle.hg'
477 $ hg incoming '../test#bundle.hg'
478 comparing with ../test
478 comparing with ../test
479 abort: unknown revision 'bundle.hg'!
479 abort: unknown revision 'bundle.hg'!
480 [255]
480 [255]
481
481
482 note that percent encoding is not handled:
482 note that percent encoding is not handled:
483
483
484 $ hg incoming ../test%23bundle.hg
484 $ hg incoming ../test%23bundle.hg
485 abort: repository ../test%23bundle.hg not found!
485 abort: repository ../test%23bundle.hg not found!
486 [255]
486 [255]
487 $ cd ..
487 $ cd ..
488
488
489 test for http://mercurial.selenic.com/bts/issue1144
489 test for http://mercurial.selenic.com/bts/issue1144
490
490
491 test that verify bundle does not traceback
491 test that verify bundle does not traceback
492
492
493 partial history bundle, fails w/ unkown parent
493 partial history bundle, fails w/ unkown parent
494
494
495 $ hg -R bundle.hg verify
495 $ hg -R bundle.hg verify
496 abort: 00changelog.i@bbd179dfa0a7: unknown parent!
496 abort: 00changelog.i@bbd179dfa0a7: unknown parent!
497 [255]
497 [255]
498
498
499 full history bundle, refuses to verify non-local repo
499 full history bundle, refuses to verify non-local repo
500
500
501 $ hg -R all.hg verify
501 $ hg -R all.hg verify
502 abort: cannot verify bundle or remote repos
502 abort: cannot verify bundle or remote repos
503 [255]
503 [255]
504
504
505 but, regular verify must continue to work
505 but, regular verify must continue to work
506
506
507 $ hg -R orig verify
507 $ hg -R orig verify
508 checking changesets
508 checking changesets
509 checking manifests
509 checking manifests
510 crosschecking files in changesets and manifests
510 crosschecking files in changesets and manifests
511 checking files
511 checking files
512 2 files, 2 changesets, 2 total revisions
512 2 files, 2 changesets, 2 total revisions
513
513
514 diff against bundle
514 diff against bundle
515
515
516 $ hg init b
516 $ hg init b
517 $ cd b
517 $ cd b
518 $ hg -R ../all.hg diff -r tip
518 $ hg -R ../all.hg diff -r tip
519 diff -r aa35859c02ea anotherfile
519 diff -r aa35859c02ea anotherfile
520 --- a/anotherfile Thu Jan 01 00:00:00 1970 +0000
520 --- a/anotherfile Thu Jan 01 00:00:00 1970 +0000
521 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
521 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
522 @@ -1,4 +0,0 @@
522 @@ -1,4 +0,0 @@
523 -0
523 -0
524 -1
524 -1
525 -2
525 -2
526 -3
526 -3
527 $ cd ..
527 $ cd ..
528
528
529 bundle single branch
529 bundle single branch
530
530
531 $ hg init branchy
531 $ hg init branchy
532 $ cd branchy
532 $ cd branchy
533 $ echo a >a
533 $ echo a >a
534 $ hg ci -Ama
534 $ hg ci -Ama
535 adding a
535 adding a
536 $ echo b >b
536 $ echo b >b
537 $ hg ci -Amb
537 $ hg ci -Amb
538 adding b
538 adding b
539 $ echo b1 >b1
539 $ echo b1 >b1
540 $ hg ci -Amb1
540 $ hg ci -Amb1
541 adding b1
541 adding b1
542 $ hg up 0
542 $ hg up 0
543 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
543 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
544 $ echo c >c
544 $ echo c >c
545 $ hg ci -Amc
545 $ hg ci -Amc
546 adding c
546 adding c
547 created new head
547 created new head
548 $ echo c1 >c1
548 $ echo c1 >c1
549 $ hg ci -Amc1
549 $ hg ci -Amc1
550 adding c1
550 adding c1
551 $ hg clone -q .#tip part
551 $ hg clone -q .#tip part
552
552
553 == bundling via incoming
553 == bundling via incoming
554
554
555 $ hg in -R part --bundle incoming.hg --template "{node}\n" .
555 $ hg in -R part --bundle incoming.hg --template "{node}\n" .
556 comparing with .
556 comparing with .
557 searching for changes
557 searching for changes
558 d2ae7f538514cd87c17547b0de4cea71fe1af9fb
558 d2ae7f538514cd87c17547b0de4cea71fe1af9fb
559 5ece8e77363e2b5269e27c66828b72da29e4341a
559 5ece8e77363e2b5269e27c66828b72da29e4341a
560
560
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
568 5ece8e77363e2b5269e27c66828b72da29e4341a
570 5ece8e77363e2b5269e27c66828b72da29e4341a
569 bundling: 1 changesets
571 bundling: 1 changesets
570 bundling: 2 changesets
572 bundling: 2 changesets
571 bundling: 1/2 manifests (50.00%)
573 bundling: 1/2 manifests (50.00%)
572 bundling: 2/2 manifests (100.00%)
574 bundling: 2/2 manifests (100.00%)
573 bundling: b 0/2 files (0.00%)
575 bundling: b 0/2 files (0.00%)
574 bundling: b1 1/2 files (50.00%)
576 bundling: b1 1/2 files (50.00%)
575
577
@@ -1,261 +1,263 b''
1 Show all commands except debug commands
1 Show all commands except debug commands
2 $ hg debugcomplete
2 $ hg debugcomplete
3 add
3 add
4 addremove
4 addremove
5 annotate
5 annotate
6 archive
6 archive
7 backout
7 backout
8 bisect
8 bisect
9 bookmarks
9 bookmarks
10 branch
10 branch
11 branches
11 branches
12 bundle
12 bundle
13 cat
13 cat
14 clone
14 clone
15 commit
15 commit
16 copy
16 copy
17 diff
17 diff
18 export
18 export
19 forget
19 forget
20 grep
20 grep
21 heads
21 heads
22 help
22 help
23 identify
23 identify
24 import
24 import
25 incoming
25 incoming
26 init
26 init
27 locate
27 locate
28 log
28 log
29 manifest
29 manifest
30 merge
30 merge
31 outgoing
31 outgoing
32 parents
32 parents
33 paths
33 paths
34 pull
34 pull
35 push
35 push
36 recover
36 recover
37 remove
37 remove
38 rename
38 rename
39 resolve
39 resolve
40 revert
40 revert
41 rollback
41 rollback
42 root
42 root
43 serve
43 serve
44 showconfig
44 showconfig
45 status
45 status
46 summary
46 summary
47 tag
47 tag
48 tags
48 tags
49 tip
49 tip
50 unbundle
50 unbundle
51 update
51 update
52 verify
52 verify
53 version
53 version
54
54
55 Show all commands that start with "a"
55 Show all commands that start with "a"
56 $ hg debugcomplete a
56 $ hg debugcomplete a
57 add
57 add
58 addremove
58 addremove
59 annotate
59 annotate
60 archive
60 archive
61
61
62 Do not show debug commands if there are other candidates
62 Do not show debug commands if there are other candidates
63 $ hg debugcomplete d
63 $ hg debugcomplete d
64 diff
64 diff
65
65
66 Show debug commands if there are no other candidates
66 Show debug commands if there are no other candidates
67 $ hg debugcomplete debug
67 $ hg debugcomplete debug
68 debugancestor
68 debugancestor
69 debugbuilddag
69 debugbuilddag
70 debugbundle
70 debugbundle
71 debugcheckstate
71 debugcheckstate
72 debugcommands
72 debugcommands
73 debugcomplete
73 debugcomplete
74 debugconfig
74 debugconfig
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
81 debugindex
82 debugindex
82 debugindexdot
83 debugindexdot
83 debuginstall
84 debuginstall
84 debugknown
85 debugknown
85 debugpushkey
86 debugpushkey
86 debugrebuildstate
87 debugrebuildstate
87 debugrename
88 debugrename
88 debugrevspec
89 debugrevspec
89 debugsetparents
90 debugsetparents
90 debugstate
91 debugstate
91 debugsub
92 debugsub
92 debugwalk
93 debugwalk
93 debugwireargs
94 debugwireargs
94
95
95 Do not show the alias of a debug command if there are other candidates
96 Do not show the alias of a debug command if there are other candidates
96 (this should hide rawcommit)
97 (this should hide rawcommit)
97 $ hg debugcomplete r
98 $ hg debugcomplete r
98 recover
99 recover
99 remove
100 remove
100 rename
101 rename
101 resolve
102 resolve
102 revert
103 revert
103 rollback
104 rollback
104 root
105 root
105 Show the alias of a debug command if there are no other candidates
106 Show the alias of a debug command if there are no other candidates
106 $ hg debugcomplete rawc
107 $ hg debugcomplete rawc
107
108
108
109
109 Show the global options
110 Show the global options
110 $ hg debugcomplete --options | sort
111 $ hg debugcomplete --options | sort
111 --config
112 --config
112 --cwd
113 --cwd
113 --debug
114 --debug
114 --debugger
115 --debugger
115 --encoding
116 --encoding
116 --encodingmode
117 --encodingmode
117 --help
118 --help
118 --noninteractive
119 --noninteractive
119 --profile
120 --profile
120 --quiet
121 --quiet
121 --repository
122 --repository
122 --time
123 --time
123 --traceback
124 --traceback
124 --verbose
125 --verbose
125 --version
126 --version
126 -R
127 -R
127 -h
128 -h
128 -q
129 -q
129 -v
130 -v
130 -y
131 -y
131
132
132 Show the options for the "serve" command
133 Show the options for the "serve" command
133 $ hg debugcomplete --options serve | sort
134 $ hg debugcomplete --options serve | sort
134 --accesslog
135 --accesslog
135 --address
136 --address
136 --certificate
137 --certificate
137 --config
138 --config
138 --cwd
139 --cwd
139 --daemon
140 --daemon
140 --daemon-pipefds
141 --daemon-pipefds
141 --debug
142 --debug
142 --debugger
143 --debugger
143 --encoding
144 --encoding
144 --encodingmode
145 --encodingmode
145 --errorlog
146 --errorlog
146 --help
147 --help
147 --ipv6
148 --ipv6
148 --name
149 --name
149 --noninteractive
150 --noninteractive
150 --pid-file
151 --pid-file
151 --port
152 --port
152 --prefix
153 --prefix
153 --profile
154 --profile
154 --quiet
155 --quiet
155 --repository
156 --repository
156 --stdio
157 --stdio
157 --style
158 --style
158 --templates
159 --templates
159 --time
160 --time
160 --traceback
161 --traceback
161 --verbose
162 --verbose
162 --version
163 --version
163 --web-conf
164 --web-conf
164 -6
165 -6
165 -A
166 -A
166 -E
167 -E
167 -R
168 -R
168 -a
169 -a
169 -d
170 -d
170 -h
171 -h
171 -n
172 -n
172 -p
173 -p
173 -q
174 -q
174 -t
175 -t
175 -v
176 -v
176 -y
177 -y
177
178
178 Show an error if we use --options with an ambiguous abbreviation
179 Show an error if we use --options with an ambiguous abbreviation
179 $ hg debugcomplete --options s
180 $ hg debugcomplete --options s
180 hg: command 's' is ambiguous:
181 hg: command 's' is ambiguous:
181 serve showconfig status summary
182 serve showconfig status summary
182 [255]
183 [255]
183
184
184 Show all commands + options
185 Show all commands + options
185 $ hg debugcommands
186 $ hg debugcommands
186 add: include, exclude, subrepos, dry-run
187 add: include, exclude, subrepos, dry-run
187 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, include, exclude
188 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, include, exclude
188 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
189 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
189 commit: addremove, close-branch, include, exclude, message, logfile, date, user
190 commit: addremove, close-branch, include, exclude, message, logfile, date, user
190 diff: rev, change, text, git, nodates, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude, subrepos
191 diff: rev, change, text, git, nodates, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude, subrepos
191 export: output, switch-parent, rev, text, git, nodates
192 export: output, switch-parent, rev, text, git, nodates
192 forget: include, exclude
193 forget: include, exclude
193 init: ssh, remotecmd, insecure
194 init: ssh, remotecmd, insecure
194 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, style, template, include, exclude
195 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, style, template, include, exclude
195 merge: force, tool, rev, preview
196 merge: force, tool, rev, preview
196 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
197 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
197 push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
198 push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
198 remove: after, force, include, exclude
199 remove: after, force, include, exclude
199 serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate
200 serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate
200 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos
201 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos
201 summary: remote
202 summary: remote
202 update: clean, check, date, rev
203 update: clean, check, date, rev
203 addremove: similarity, include, exclude, dry-run
204 addremove: similarity, include, exclude, dry-run
204 archive: no-decode, prefix, rev, type, subrepos, include, exclude
205 archive: no-decode, prefix, rev, type, subrepos, include, exclude
205 backout: merge, parent, tool, rev, include, exclude, message, logfile, date, user
206 backout: merge, parent, tool, rev, include, exclude, message, logfile, date, user
206 bisect: reset, good, bad, skip, extend, command, noupdate
207 bisect: reset, good, bad, skip, extend, command, noupdate
207 bookmarks: force, rev, delete, rename
208 bookmarks: force, rev, delete, rename
208 branch: force, clean
209 branch: force, clean
209 branches: active, closed
210 branches: active, closed
210 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
211 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
211 cat: output, rev, decode, include, exclude
212 cat: output, rev, decode, include, exclude
212 copy: after, force, include, exclude, dry-run
213 copy: after, force, include, exclude, dry-run
213 debugancestor:
214 debugancestor:
214 debugbuilddag: mergeable-file, overwritten-file, new-file
215 debugbuilddag: mergeable-file, overwritten-file, new-file
215 debugbundle: all
216 debugbundle: all
216 debugcheckstate:
217 debugcheckstate:
217 debugcommands:
218 debugcommands:
218 debugcomplete: options
219 debugcomplete: 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:
225 debugindex: format
227 debugindex: format
226 debugindexdot:
228 debugindexdot:
227 debuginstall:
229 debuginstall:
228 debugknown:
230 debugknown:
229 debugpushkey:
231 debugpushkey:
230 debugrebuildstate: rev
232 debugrebuildstate: rev
231 debugrename: rev
233 debugrename: rev
232 debugrevspec:
234 debugrevspec:
233 debugsetparents:
235 debugsetparents:
234 debugstate: nodates, datesort
236 debugstate: nodates, datesort
235 debugsub: rev
237 debugsub: rev
236 debugwalk: include, exclude
238 debugwalk: include, exclude
237 debugwireargs: three, four, five, ssh, remotecmd, insecure
239 debugwireargs: three, four, five, ssh, remotecmd, insecure
238 grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
240 grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
239 heads: rev, topo, active, closed, style, template
241 heads: rev, topo, active, closed, style, template
240 help:
242 help:
241 identify: rev, num, id, branch, tags, bookmarks
243 identify: rev, num, id, branch, tags, bookmarks
242 import: strip, base, force, no-commit, exact, import-branch, message, logfile, date, user, similarity
244 import: strip, base, force, no-commit, exact, import-branch, message, logfile, date, user, similarity
243 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
245 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
244 locate: rev, print0, fullpath, include, exclude
246 locate: rev, print0, fullpath, include, exclude
245 manifest: rev
247 manifest: rev
246 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
248 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
247 parents: rev, style, template
249 parents: rev, style, template
248 paths:
250 paths:
249 recover:
251 recover:
250 rename: after, force, include, exclude, dry-run
252 rename: after, force, include, exclude, dry-run
251 resolve: all, list, mark, unmark, tool, no-status, include, exclude
253 resolve: all, list, mark, unmark, tool, no-status, include, exclude
252 revert: all, date, rev, no-backup, include, exclude, dry-run
254 revert: all, date, rev, no-backup, include, exclude, dry-run
253 rollback: dry-run
255 rollback: dry-run
254 root:
256 root:
255 showconfig: untrusted
257 showconfig: untrusted
256 tag: force, local, rev, remove, edit, message, date, user
258 tag: force, local, rev, remove, edit, message, date, user
257 tags:
259 tags:
258 tip: patch, git, style, template
260 tip: patch, git, style, template
259 unbundle: update
261 unbundle: update
260 verify:
262 verify:
261 version:
263 version:
@@ -1,537 +1,537 b''
1 commit hooks can see env vars
1 commit hooks can see env vars
2
2
3 $ hg init a
3 $ hg init a
4 $ cd a
4 $ cd a
5 $ echo "[hooks]" > .hg/hgrc
5 $ echo "[hooks]" > .hg/hgrc
6 $ echo 'commit = unset HG_LOCAL HG_TAG; python "$TESTDIR"/printenv.py commit' >> .hg/hgrc
6 $ echo 'commit = unset HG_LOCAL HG_TAG; python "$TESTDIR"/printenv.py commit' >> .hg/hgrc
7 $ echo 'commit.b = unset HG_LOCAL HG_TAG; python "$TESTDIR"/printenv.py commit.b' >> .hg/hgrc
7 $ echo 'commit.b = unset HG_LOCAL HG_TAG; python "$TESTDIR"/printenv.py commit.b' >> .hg/hgrc
8 $ echo 'precommit = unset HG_LOCAL HG_NODE HG_TAG; python "$TESTDIR"/printenv.py precommit' >> .hg/hgrc
8 $ echo 'precommit = unset HG_LOCAL HG_NODE HG_TAG; python "$TESTDIR"/printenv.py precommit' >> .hg/hgrc
9 $ echo 'pretxncommit = unset HG_LOCAL HG_TAG; python "$TESTDIR"/printenv.py pretxncommit' >> .hg/hgrc
9 $ echo 'pretxncommit = unset HG_LOCAL HG_TAG; python "$TESTDIR"/printenv.py pretxncommit' >> .hg/hgrc
10 $ echo 'pretxncommit.tip = hg -q tip' >> .hg/hgrc
10 $ echo 'pretxncommit.tip = hg -q tip' >> .hg/hgrc
11 $ echo 'pre-identify = python "$TESTDIR"/printenv.py pre-identify 1' >> .hg/hgrc
11 $ echo 'pre-identify = python "$TESTDIR"/printenv.py pre-identify 1' >> .hg/hgrc
12 $ echo 'pre-cat = python "$TESTDIR"/printenv.py pre-cat' >> .hg/hgrc
12 $ echo 'pre-cat = python "$TESTDIR"/printenv.py pre-cat' >> .hg/hgrc
13 $ echo 'post-cat = python "$TESTDIR"/printenv.py post-cat' >> .hg/hgrc
13 $ echo 'post-cat = python "$TESTDIR"/printenv.py post-cat' >> .hg/hgrc
14 $ echo a > a
14 $ echo a > a
15 $ hg add a
15 $ hg add a
16 $ hg commit -m a
16 $ hg commit -m a
17 precommit hook: HG_PARENT1=0000000000000000000000000000000000000000
17 precommit hook: HG_PARENT1=0000000000000000000000000000000000000000
18 pretxncommit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$TESTTMP/a
18 pretxncommit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$TESTTMP/a
19 0:cb9a9f314b8b
19 0:cb9a9f314b8b
20 commit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
20 commit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
21 commit.b hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
21 commit.b hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
22
22
23 $ hg clone . ../b
23 $ hg clone . ../b
24 updating to branch default
24 updating to branch default
25 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
25 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
26 $ cd ../b
26 $ cd ../b
27
27
28 changegroup hooks can see env vars
28 changegroup hooks can see env vars
29
29
30 $ echo '[hooks]' > .hg/hgrc
30 $ echo '[hooks]' > .hg/hgrc
31 $ echo 'prechangegroup = python "$TESTDIR"/printenv.py prechangegroup' >> .hg/hgrc
31 $ echo 'prechangegroup = python "$TESTDIR"/printenv.py prechangegroup' >> .hg/hgrc
32 $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup' >> .hg/hgrc
32 $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup' >> .hg/hgrc
33 $ echo 'incoming = python "$TESTDIR"/printenv.py incoming' >> .hg/hgrc
33 $ echo 'incoming = python "$TESTDIR"/printenv.py incoming' >> .hg/hgrc
34
34
35 pretxncommit and commit hooks can see both parents of merge
35 pretxncommit and commit hooks can see both parents of merge
36
36
37 $ cd ../a
37 $ cd ../a
38 $ echo b >> a
38 $ echo b >> a
39 $ hg commit -m a1 -d "1 0"
39 $ hg commit -m a1 -d "1 0"
40 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
40 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
41 pretxncommit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
41 pretxncommit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
42 1:ab228980c14d
42 1:ab228980c14d
43 commit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
43 commit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
44 commit.b hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
44 commit.b hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
45 $ hg update -C 0
45 $ hg update -C 0
46 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
47 $ echo b > b
47 $ echo b > b
48 $ hg add b
48 $ hg add b
49 $ hg commit -m b -d '1 0'
49 $ hg commit -m b -d '1 0'
50 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
50 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
51 pretxncommit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
51 pretxncommit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
52 2:ee9deb46ab31
52 2:ee9deb46ab31
53 commit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
53 commit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
54 commit.b hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
54 commit.b hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
55 created new head
55 created new head
56 $ hg merge 1
56 $ hg merge 1
57 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
57 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
58 (branch merge, don't forget to commit)
58 (branch merge, don't forget to commit)
59 $ hg commit -m merge -d '2 0'
59 $ hg commit -m merge -d '2 0'
60 precommit hook: HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
60 precommit hook: HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
61 pretxncommit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd HG_PENDING=$TESTTMP/a
61 pretxncommit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd HG_PENDING=$TESTTMP/a
62 3:07f3376c1e65
62 3:07f3376c1e65
63 commit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
63 commit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
64 commit.b hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
64 commit.b hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
65
65
66 test generic hooks
66 test generic hooks
67
67
68 $ hg id
68 $ hg id
69 pre-identify hook: HG_ARGS=id HG_OPTS={'bookmarks': None, 'branch': None, 'id': None, 'num': None, 'rev': '', 'tags': None} HG_PATS=[]
69 pre-identify hook: HG_ARGS=id HG_OPTS={'bookmarks': None, 'branch': None, 'id': None, 'num': None, 'rev': '', 'tags': None} HG_PATS=[]
70 warning: pre-identify hook exited with status 1
70 warning: pre-identify hook exited with status 1
71 [1]
71 [1]
72 $ hg cat b
72 $ hg cat b
73 pre-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b']
73 pre-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b']
74 b
74 b
75 post-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] HG_RESULT=0
75 post-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] HG_RESULT=0
76
76
77 $ cd ../b
77 $ cd ../b
78 $ hg pull ../a
78 $ hg pull ../a
79 pulling from ../a
79 pulling from ../a
80 searching for changes
80 searching for changes
81 prechangegroup hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a
81 prechangegroup hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a
82 adding changesets
82 adding changesets
83 adding manifests
83 adding manifests
84 adding file changes
84 adding file changes
85 added 3 changesets with 2 changes to 2 files
85 added 3 changesets with 2 changes to 2 files
86 changegroup hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a
86 changegroup hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a
87 incoming hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a
87 incoming hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a
88 incoming hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a
88 incoming hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a
89 incoming hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a
89 incoming hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a
90 (run 'hg update' to get a working copy)
90 (run 'hg update' to get a working copy)
91
91
92 tag hooks can see env vars
92 tag hooks can see env vars
93
93
94 $ cd ../a
94 $ cd ../a
95 $ echo 'pretag = python "$TESTDIR"/printenv.py pretag' >> .hg/hgrc
95 $ echo 'pretag = python "$TESTDIR"/printenv.py pretag' >> .hg/hgrc
96 $ echo 'tag = unset HG_PARENT1 HG_PARENT2; python "$TESTDIR"/printenv.py tag' >> .hg/hgrc
96 $ echo 'tag = unset HG_PARENT1 HG_PARENT2; python "$TESTDIR"/printenv.py tag' >> .hg/hgrc
97 $ hg tag -d '3 0' a
97 $ hg tag -d '3 0' a
98 pretag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
98 pretag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
99 precommit hook: HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
99 precommit hook: HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
100 pretxncommit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PENDING=$TESTTMP/a
100 pretxncommit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PENDING=$TESTTMP/a
101 4:539e4b31b6dc
101 4:539e4b31b6dc
102 commit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
102 commit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
103 commit.b hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
103 commit.b hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
104 tag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
104 tag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
105 $ hg tag -l la
105 $ hg tag -l la
106 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
106 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
107 tag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
107 tag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
108
108
109 pretag hook can forbid tagging
109 pretag hook can forbid tagging
110
110
111 $ echo 'pretag.forbid = python "$TESTDIR"/printenv.py pretag.forbid 1' >> .hg/hgrc
111 $ echo 'pretag.forbid = python "$TESTDIR"/printenv.py pretag.forbid 1' >> .hg/hgrc
112 $ hg tag -d '4 0' fa
112 $ hg tag -d '4 0' fa
113 pretag hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
113 pretag hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
114 pretag.forbid hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
114 pretag.forbid hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
115 abort: pretag.forbid hook exited with status 1
115 abort: pretag.forbid hook exited with status 1
116 [255]
116 [255]
117 $ hg tag -l fla
117 $ hg tag -l fla
118 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
118 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
119 pretag.forbid hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
119 pretag.forbid hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
120 abort: pretag.forbid hook exited with status 1
120 abort: pretag.forbid hook exited with status 1
121 [255]
121 [255]
122
122
123 pretxncommit hook can see changeset, can roll back txn, changeset no
123 pretxncommit hook can see changeset, can roll back txn, changeset no
124 more there after
124 more there after
125
125
126 $ echo 'pretxncommit.forbid0 = hg tip -q' >> .hg/hgrc
126 $ echo 'pretxncommit.forbid0 = hg tip -q' >> .hg/hgrc
127 $ echo 'pretxncommit.forbid1 = python "$TESTDIR"/printenv.py pretxncommit.forbid 1' >> .hg/hgrc
127 $ echo 'pretxncommit.forbid1 = python "$TESTDIR"/printenv.py pretxncommit.forbid 1' >> .hg/hgrc
128 $ echo z > z
128 $ echo z > z
129 $ hg add z
129 $ hg add z
130 $ hg -q tip
130 $ hg -q tip
131 4:539e4b31b6dc
131 4:539e4b31b6dc
132 $ hg commit -m 'fail' -d '4 0'
132 $ hg commit -m 'fail' -d '4 0'
133 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
133 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
134 pretxncommit hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
134 pretxncommit hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
135 5:6f611f8018c1
135 5:6f611f8018c1
136 5:6f611f8018c1
136 5:6f611f8018c1
137 pretxncommit.forbid hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
137 pretxncommit.forbid hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
138 transaction abort!
138 transaction abort!
139 rollback completed
139 rollback completed
140 abort: pretxncommit.forbid1 hook exited with status 1
140 abort: pretxncommit.forbid1 hook exited with status 1
141 [255]
141 [255]
142 $ hg -q tip
142 $ hg -q tip
143 4:539e4b31b6dc
143 4:539e4b31b6dc
144
144
145 precommit hook can prevent commit
145 precommit hook can prevent commit
146
146
147 $ echo 'precommit.forbid = python "$TESTDIR"/printenv.py precommit.forbid 1' >> .hg/hgrc
147 $ echo 'precommit.forbid = python "$TESTDIR"/printenv.py precommit.forbid 1' >> .hg/hgrc
148 $ hg commit -m 'fail' -d '4 0'
148 $ hg commit -m 'fail' -d '4 0'
149 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
149 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
150 precommit.forbid hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
150 precommit.forbid hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
151 abort: precommit.forbid hook exited with status 1
151 abort: precommit.forbid hook exited with status 1
152 [255]
152 [255]
153 $ hg -q tip
153 $ hg -q tip
154 4:539e4b31b6dc
154 4:539e4b31b6dc
155
155
156 preupdate hook can prevent update
156 preupdate hook can prevent update
157
157
158 $ echo 'preupdate = python "$TESTDIR"/printenv.py preupdate' >> .hg/hgrc
158 $ echo 'preupdate = python "$TESTDIR"/printenv.py preupdate' >> .hg/hgrc
159 $ hg update 1
159 $ hg update 1
160 preupdate hook: HG_PARENT1=ab228980c14d
160 preupdate hook: HG_PARENT1=ab228980c14d
161 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
161 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
162
162
163 update hook
163 update hook
164
164
165 $ echo 'update = python "$TESTDIR"/printenv.py update' >> .hg/hgrc
165 $ echo 'update = python "$TESTDIR"/printenv.py update' >> .hg/hgrc
166 $ hg update
166 $ hg update
167 preupdate hook: HG_PARENT1=539e4b31b6dc
167 preupdate hook: HG_PARENT1=539e4b31b6dc
168 update hook: HG_ERROR=0 HG_PARENT1=539e4b31b6dc
168 update hook: HG_ERROR=0 HG_PARENT1=539e4b31b6dc
169 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
169 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
170
170
171 pushkey hook
171 pushkey hook
172
172
173 $ echo 'pushkey = python "$TESTDIR"/printenv.py pushkey' >> .hg/hgrc
173 $ echo 'pushkey = python "$TESTDIR"/printenv.py pushkey' >> .hg/hgrc
174 $ cd ../b
174 $ cd ../b
175 $ hg bookmark -r null foo
175 $ hg bookmark -r null foo
176 $ hg push -B foo ../a
176 $ hg push -B foo ../a
177 pushing to ../a
177 pushing to ../a
178 searching for changes
178 searching for changes
179 no changes found
179 no changes found
180 exporting bookmark foo
180 exporting bookmark foo
181 pushkey hook: HG_KEY=foo HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_RET=1
181 pushkey hook: HG_KEY=foo HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_RET=1
182 $ cd ../a
182 $ cd ../a
183
183
184 listkeys hook
184 listkeys hook
185
185
186 $ echo 'listkeys = python "$TESTDIR"/printenv.py listkeys' >> .hg/hgrc
186 $ echo 'listkeys = python "$TESTDIR"/printenv.py listkeys' >> .hg/hgrc
187 $ hg bookmark -r null bar
187 $ hg bookmark -r null bar
188 $ cd ../b
188 $ cd ../b
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
196
196
197 test that prepushkey can prevent incoming keys
197 test that prepushkey can prevent incoming keys
198
198
199 $ echo 'prepushkey = python "$TESTDIR"/printenv.py prepushkey.forbid 1' >> .hg/hgrc
199 $ echo 'prepushkey = python "$TESTDIR"/printenv.py prepushkey.forbid 1' >> .hg/hgrc
200 $ cd ../b
200 $ cd ../b
201 $ hg bookmark -r null baz
201 $ hg bookmark -r null baz
202 $ hg push -B baz ../a
202 $ hg push -B baz ../a
203 pushing to ../a
203 pushing to ../a
204 searching for changes
204 searching for changes
205 no changes found
205 no changes found
206 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
206 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
207 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
207 listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
208 exporting bookmark baz
208 exporting bookmark baz
209 prepushkey.forbid hook: HG_KEY=baz HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000
209 prepushkey.forbid hook: HG_KEY=baz HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000
210 abort: prepushkey hook exited with status 1
210 abort: prepushkey hook exited with status 1
211 [255]
211 [255]
212 $ cd ../a
212 $ cd ../a
213
213
214 test that prelistkeys can prevent listing keys
214 test that prelistkeys can prevent listing keys
215
215
216 $ echo 'prelistkeys = python "$TESTDIR"/printenv.py prelistkeys.forbid 1' >> .hg/hgrc
216 $ echo 'prelistkeys = python "$TESTDIR"/printenv.py prelistkeys.forbid 1' >> .hg/hgrc
217 $ hg bookmark -r null quux
217 $ hg bookmark -r null quux
218 $ cd ../b
218 $ cd ../b
219 $ hg pull -B quux ../a
219 $ hg pull -B quux ../a
220 pulling from ../a
220 pulling from ../a
221 prelistkeys.forbid hook: HG_NAMESPACE=bookmarks
221 prelistkeys.forbid hook: HG_NAMESPACE=bookmarks
222 abort: prelistkeys hook exited with status 1
222 abort: prelistkeys hook exited with status 1
223 [255]
223 [255]
224 $ cd ../a
224 $ cd ../a
225
225
226 prechangegroup hook can prevent incoming changes
226 prechangegroup hook can prevent incoming changes
227
227
228 $ cd ../b
228 $ cd ../b
229 $ hg -q tip
229 $ hg -q tip
230 3:07f3376c1e65
230 3:07f3376c1e65
231 $ echo '[hooks]' > .hg/hgrc
231 $ echo '[hooks]' > .hg/hgrc
232 $ echo 'prechangegroup.forbid = python "$TESTDIR"/printenv.py prechangegroup.forbid 1' >> .hg/hgrc
232 $ echo 'prechangegroup.forbid = python "$TESTDIR"/printenv.py prechangegroup.forbid 1' >> .hg/hgrc
233 $ hg pull ../a
233 $ hg pull ../a
234 pulling from ../a
234 pulling from ../a
235 searching for changes
235 searching for changes
236 prechangegroup.forbid hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a
236 prechangegroup.forbid hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a
237 abort: prechangegroup.forbid hook exited with status 1
237 abort: prechangegroup.forbid hook exited with status 1
238 [255]
238 [255]
239
239
240 pretxnchangegroup hook can see incoming changes, can roll back txn,
240 pretxnchangegroup hook can see incoming changes, can roll back txn,
241 incoming changes no longer there after
241 incoming changes no longer there after
242
242
243 $ echo '[hooks]' > .hg/hgrc
243 $ echo '[hooks]' > .hg/hgrc
244 $ echo 'pretxnchangegroup.forbid0 = hg tip -q' >> .hg/hgrc
244 $ echo 'pretxnchangegroup.forbid0 = hg tip -q' >> .hg/hgrc
245 $ echo 'pretxnchangegroup.forbid1 = python "$TESTDIR"/printenv.py pretxnchangegroup.forbid 1' >> .hg/hgrc
245 $ echo 'pretxnchangegroup.forbid1 = python "$TESTDIR"/printenv.py pretxnchangegroup.forbid 1' >> .hg/hgrc
246 $ hg pull ../a
246 $ hg pull ../a
247 pulling from ../a
247 pulling from ../a
248 searching for changes
248 searching for changes
249 adding changesets
249 adding changesets
250 adding manifests
250 adding manifests
251 adding file changes
251 adding file changes
252 added 1 changesets with 1 changes to 1 files
252 added 1 changesets with 1 changes to 1 files
253 4:539e4b31b6dc
253 4:539e4b31b6dc
254 pretxnchangegroup.forbid hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/b HG_SOURCE=pull HG_URL=file:$TESTTMP/a
254 pretxnchangegroup.forbid hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/b HG_SOURCE=pull HG_URL=file:$TESTTMP/a
255 transaction abort!
255 transaction abort!
256 rollback completed
256 rollback completed
257 abort: pretxnchangegroup.forbid1 hook exited with status 1
257 abort: pretxnchangegroup.forbid1 hook exited with status 1
258 [255]
258 [255]
259 $ hg -q tip
259 $ hg -q tip
260 3:07f3376c1e65
260 3:07f3376c1e65
261
261
262 outgoing hooks can see env vars
262 outgoing hooks can see env vars
263
263
264 $ rm .hg/hgrc
264 $ rm .hg/hgrc
265 $ echo '[hooks]' > ../a/.hg/hgrc
265 $ echo '[hooks]' > ../a/.hg/hgrc
266 $ echo 'preoutgoing = python "$TESTDIR"/printenv.py preoutgoing' >> ../a/.hg/hgrc
266 $ echo 'preoutgoing = python "$TESTDIR"/printenv.py preoutgoing' >> ../a/.hg/hgrc
267 $ echo 'outgoing = python "$TESTDIR"/printenv.py outgoing' >> ../a/.hg/hgrc
267 $ echo 'outgoing = python "$TESTDIR"/printenv.py outgoing' >> ../a/.hg/hgrc
268 $ hg pull ../a
268 $ hg pull ../a
269 pulling from ../a
269 pulling from ../a
270 searching for changes
270 searching for changes
271 preoutgoing hook: HG_SOURCE=pull
271 preoutgoing hook: HG_SOURCE=pull
272 adding changesets
272 adding changesets
273 outgoing hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_SOURCE=pull
273 outgoing hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_SOURCE=pull
274 adding manifests
274 adding manifests
275 adding file changes
275 adding file changes
276 added 1 changesets with 1 changes to 1 files
276 added 1 changesets with 1 changes to 1 files
277 (run 'hg update' to get a working copy)
277 (run 'hg update' to get a working copy)
278 $ hg rollback
278 $ hg rollback
279 repository tip rolled back to revision 3 (undo pull)
279 repository tip rolled back to revision 3 (undo pull)
280 working directory now based on revision 0
280 working directory now based on revision 0
281
281
282 preoutgoing hook can prevent outgoing changes
282 preoutgoing hook can prevent outgoing changes
283
283
284 $ echo 'preoutgoing.forbid = python "$TESTDIR"/printenv.py preoutgoing.forbid 1' >> ../a/.hg/hgrc
284 $ echo 'preoutgoing.forbid = python "$TESTDIR"/printenv.py preoutgoing.forbid 1' >> ../a/.hg/hgrc
285 $ hg pull ../a
285 $ hg pull ../a
286 pulling from ../a
286 pulling from ../a
287 searching for changes
287 searching for changes
288 preoutgoing hook: HG_SOURCE=pull
288 preoutgoing hook: HG_SOURCE=pull
289 preoutgoing.forbid hook: HG_SOURCE=pull
289 preoutgoing.forbid hook: HG_SOURCE=pull
290 abort: preoutgoing.forbid hook exited with status 1
290 abort: preoutgoing.forbid hook exited with status 1
291 [255]
291 [255]
292
292
293 outgoing hooks work for local clones
293 outgoing hooks work for local clones
294
294
295 $ cd ..
295 $ cd ..
296 $ echo '[hooks]' > a/.hg/hgrc
296 $ echo '[hooks]' > a/.hg/hgrc
297 $ echo 'preoutgoing = python "$TESTDIR"/printenv.py preoutgoing' >> a/.hg/hgrc
297 $ echo 'preoutgoing = python "$TESTDIR"/printenv.py preoutgoing' >> a/.hg/hgrc
298 $ echo 'outgoing = python "$TESTDIR"/printenv.py outgoing' >> a/.hg/hgrc
298 $ echo 'outgoing = python "$TESTDIR"/printenv.py outgoing' >> a/.hg/hgrc
299 $ hg clone a c
299 $ hg clone a c
300 preoutgoing hook: HG_SOURCE=clone
300 preoutgoing hook: HG_SOURCE=clone
301 outgoing hook: HG_NODE=0000000000000000000000000000000000000000 HG_SOURCE=clone
301 outgoing hook: HG_NODE=0000000000000000000000000000000000000000 HG_SOURCE=clone
302 updating to branch default
302 updating to branch default
303 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
303 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
304 $ rm -rf c
304 $ rm -rf c
305
305
306 preoutgoing hook can prevent outgoing changes for local clones
306 preoutgoing hook can prevent outgoing changes for local clones
307
307
308 $ echo 'preoutgoing.forbid = python "$TESTDIR"/printenv.py preoutgoing.forbid 1' >> a/.hg/hgrc
308 $ echo 'preoutgoing.forbid = python "$TESTDIR"/printenv.py preoutgoing.forbid 1' >> a/.hg/hgrc
309 $ hg clone a zzz
309 $ hg clone a zzz
310 preoutgoing hook: HG_SOURCE=clone
310 preoutgoing hook: HG_SOURCE=clone
311 preoutgoing.forbid hook: HG_SOURCE=clone
311 preoutgoing.forbid hook: HG_SOURCE=clone
312 abort: preoutgoing.forbid hook exited with status 1
312 abort: preoutgoing.forbid hook exited with status 1
313 [255]
313 [255]
314 $ cd b
314 $ cd b
315
315
316 $ cat > hooktests.py <<EOF
316 $ cat > hooktests.py <<EOF
317 > from mercurial import util
317 > from mercurial import util
318 >
318 >
319 > uncallable = 0
319 > uncallable = 0
320 >
320 >
321 > def printargs(args):
321 > def printargs(args):
322 > args.pop('ui', None)
322 > args.pop('ui', None)
323 > args.pop('repo', None)
323 > args.pop('repo', None)
324 > a = list(args.items())
324 > a = list(args.items())
325 > a.sort()
325 > a.sort()
326 > print 'hook args:'
326 > print 'hook args:'
327 > for k, v in a:
327 > for k, v in a:
328 > print ' ', k, v
328 > print ' ', k, v
329 >
329 >
330 > def passhook(**args):
330 > def passhook(**args):
331 > printargs(args)
331 > printargs(args)
332 >
332 >
333 > def failhook(**args):
333 > def failhook(**args):
334 > printargs(args)
334 > printargs(args)
335 > return True
335 > return True
336 >
336 >
337 > class LocalException(Exception):
337 > class LocalException(Exception):
338 > pass
338 > pass
339 >
339 >
340 > def raisehook(**args):
340 > def raisehook(**args):
341 > raise LocalException('exception from hook')
341 > raise LocalException('exception from hook')
342 >
342 >
343 > def aborthook(**args):
343 > def aborthook(**args):
344 > raise util.Abort('raise abort from hook')
344 > raise util.Abort('raise abort from hook')
345 >
345 >
346 > def brokenhook(**args):
346 > def brokenhook(**args):
347 > return 1 + {}
347 > return 1 + {}
348 >
348 >
349 > class container:
349 > class container:
350 > unreachable = 1
350 > unreachable = 1
351 > EOF
351 > EOF
352
352
353 test python hooks
353 test python hooks
354
354
355 $ PYTHONPATH="`pwd`:$PYTHONPATH"
355 $ PYTHONPATH="`pwd`:$PYTHONPATH"
356 $ export PYTHONPATH
356 $ export PYTHONPATH
357
357
358 $ echo '[hooks]' > ../a/.hg/hgrc
358 $ echo '[hooks]' > ../a/.hg/hgrc
359 $ echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc
359 $ echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc
360 $ hg pull ../a 2>&1 | grep 'raised an exception'
360 $ hg pull ../a 2>&1 | grep 'raised an exception'
361 error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
361 error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
362
362
363 $ echo '[hooks]' > ../a/.hg/hgrc
363 $ echo '[hooks]' > ../a/.hg/hgrc
364 $ echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc
364 $ echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc
365 $ hg pull ../a 2>&1 | grep 'raised an exception'
365 $ hg pull ../a 2>&1 | grep 'raised an exception'
366 error: preoutgoing.raise hook raised an exception: exception from hook
366 error: preoutgoing.raise hook raised an exception: exception from hook
367
367
368 $ echo '[hooks]' > ../a/.hg/hgrc
368 $ echo '[hooks]' > ../a/.hg/hgrc
369 $ echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc
369 $ echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc
370 $ hg pull ../a
370 $ hg pull ../a
371 pulling from ../a
371 pulling from ../a
372 searching for changes
372 searching for changes
373 error: preoutgoing.abort hook failed: raise abort from hook
373 error: preoutgoing.abort hook failed: raise abort from hook
374 abort: raise abort from hook
374 abort: raise abort from hook
375 [255]
375 [255]
376
376
377 $ echo '[hooks]' > ../a/.hg/hgrc
377 $ echo '[hooks]' > ../a/.hg/hgrc
378 $ echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc
378 $ echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc
379 $ hg pull ../a
379 $ hg pull ../a
380 pulling from ../a
380 pulling from ../a
381 searching for changes
381 searching for changes
382 hook args:
382 hook args:
383 hooktype preoutgoing
383 hooktype preoutgoing
384 source pull
384 source pull
385 abort: preoutgoing.fail hook failed
385 abort: preoutgoing.fail hook failed
386 [255]
386 [255]
387
387
388 $ echo '[hooks]' > ../a/.hg/hgrc
388 $ echo '[hooks]' > ../a/.hg/hgrc
389 $ echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc
389 $ echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc
390 $ hg pull ../a
390 $ hg pull ../a
391 pulling from ../a
391 pulling from ../a
392 searching for changes
392 searching for changes
393 abort: preoutgoing.uncallable hook is invalid ("hooktests.uncallable" is not callable)
393 abort: preoutgoing.uncallable hook is invalid ("hooktests.uncallable" is not callable)
394 [255]
394 [255]
395
395
396 $ echo '[hooks]' > ../a/.hg/hgrc
396 $ echo '[hooks]' > ../a/.hg/hgrc
397 $ echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc
397 $ echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc
398 $ hg pull ../a
398 $ hg pull ../a
399 pulling from ../a
399 pulling from ../a
400 searching for changes
400 searching for changes
401 abort: preoutgoing.nohook hook is invalid ("hooktests.nohook" is not defined)
401 abort: preoutgoing.nohook hook is invalid ("hooktests.nohook" is not defined)
402 [255]
402 [255]
403
403
404 $ echo '[hooks]' > ../a/.hg/hgrc
404 $ echo '[hooks]' > ../a/.hg/hgrc
405 $ echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc
405 $ echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc
406 $ hg pull ../a
406 $ hg pull ../a
407 pulling from ../a
407 pulling from ../a
408 searching for changes
408 searching for changes
409 abort: preoutgoing.nomodule hook is invalid ("nomodule" not in a module)
409 abort: preoutgoing.nomodule hook is invalid ("nomodule" not in a module)
410 [255]
410 [255]
411
411
412 $ echo '[hooks]' > ../a/.hg/hgrc
412 $ echo '[hooks]' > ../a/.hg/hgrc
413 $ echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc
413 $ echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc
414 $ hg pull ../a
414 $ hg pull ../a
415 pulling from ../a
415 pulling from ../a
416 searching for changes
416 searching for changes
417 abort: preoutgoing.badmodule hook is invalid (import of "nomodule" failed)
417 abort: preoutgoing.badmodule hook is invalid (import of "nomodule" failed)
418 [255]
418 [255]
419
419
420 $ echo '[hooks]' > ../a/.hg/hgrc
420 $ echo '[hooks]' > ../a/.hg/hgrc
421 $ echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc
421 $ echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc
422 $ hg pull ../a
422 $ hg pull ../a
423 pulling from ../a
423 pulling from ../a
424 searching for changes
424 searching for changes
425 abort: preoutgoing.unreachable hook is invalid (import of "hooktests.container" failed)
425 abort: preoutgoing.unreachable hook is invalid (import of "hooktests.container" failed)
426 [255]
426 [255]
427
427
428 $ echo '[hooks]' > ../a/.hg/hgrc
428 $ echo '[hooks]' > ../a/.hg/hgrc
429 $ echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc
429 $ echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc
430 $ hg pull ../a
430 $ hg pull ../a
431 pulling from ../a
431 pulling from ../a
432 searching for changes
432 searching for changes
433 hook args:
433 hook args:
434 hooktype preoutgoing
434 hooktype preoutgoing
435 source pull
435 source pull
436 adding changesets
436 adding changesets
437 adding manifests
437 adding manifests
438 adding file changes
438 adding file changes
439 added 1 changesets with 1 changes to 1 files
439 added 1 changesets with 1 changes to 1 files
440 (run 'hg update' to get a working copy)
440 (run 'hg update' to get a working copy)
441
441
442 make sure --traceback works
442 make sure --traceback works
443
443
444 $ echo '[hooks]' > .hg/hgrc
444 $ echo '[hooks]' > .hg/hgrc
445 $ echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc
445 $ echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc
446
446
447 $ echo aa > a
447 $ echo aa > a
448 $ hg --traceback commit -d '0 0' -ma 2>&1 | grep '^Traceback'
448 $ hg --traceback commit -d '0 0' -ma 2>&1 | grep '^Traceback'
449 Traceback (most recent call last):
449 Traceback (most recent call last):
450
450
451 $ cd ..
451 $ cd ..
452 $ hg init c
452 $ hg init c
453 $ cd c
453 $ cd c
454
454
455 $ cat > hookext.py <<EOF
455 $ cat > hookext.py <<EOF
456 > def autohook(**args):
456 > def autohook(**args):
457 > print "Automatically installed hook"
457 > print "Automatically installed hook"
458 >
458 >
459 > def reposetup(ui, repo):
459 > def reposetup(ui, repo):
460 > repo.ui.setconfig("hooks", "commit.auto", autohook)
460 > repo.ui.setconfig("hooks", "commit.auto", autohook)
461 > EOF
461 > EOF
462 $ echo '[extensions]' >> .hg/hgrc
462 $ echo '[extensions]' >> .hg/hgrc
463 $ echo 'hookext = hookext.py' >> .hg/hgrc
463 $ echo 'hookext = hookext.py' >> .hg/hgrc
464
464
465 $ touch foo
465 $ touch foo
466 $ hg add foo
466 $ hg add foo
467 $ hg ci -d '0 0' -m 'add foo'
467 $ hg ci -d '0 0' -m 'add foo'
468 Automatically installed hook
468 Automatically installed hook
469 $ echo >> foo
469 $ echo >> foo
470 $ hg ci --debug -d '0 0' -m 'change foo'
470 $ hg ci --debug -d '0 0' -m 'change foo'
471 foo
471 foo
472 calling hook commit.auto: <function autohook at *> (glob)
472 calling hook commit.auto: <function autohook at *> (glob)
473 Automatically installed hook
473 Automatically installed hook
474 committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
474 committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
475
475
476 $ hg showconfig hooks
476 $ hg showconfig hooks
477 hooks.commit.auto=<function autohook at *> (glob)
477 hooks.commit.auto=<function autohook at *> (glob)
478
478
479 test python hook configured with python:[file]:[hook] syntax
479 test python hook configured with python:[file]:[hook] syntax
480
480
481 $ cd ..
481 $ cd ..
482 $ mkdir d
482 $ mkdir d
483 $ cd d
483 $ cd d
484 $ hg init repo
484 $ hg init repo
485 $ mkdir hooks
485 $ mkdir hooks
486
486
487 $ cd hooks
487 $ cd hooks
488 $ cat > testhooks.py <<EOF
488 $ cat > testhooks.py <<EOF
489 > def testhook(**args):
489 > def testhook(**args):
490 > print 'hook works'
490 > print 'hook works'
491 > EOF
491 > EOF
492 $ echo '[hooks]' > ../repo/.hg/hgrc
492 $ echo '[hooks]' > ../repo/.hg/hgrc
493 $ echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc
493 $ echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc
494
494
495 $ cd ../repo
495 $ cd ../repo
496 $ hg commit -d '0 0'
496 $ hg commit -d '0 0'
497 hook works
497 hook works
498 nothing changed
498 nothing changed
499 [1]
499 [1]
500
500
501 $ cd ../../b
501 $ cd ../../b
502
502
503 make sure --traceback works on hook import failure
503 make sure --traceback works on hook import failure
504
504
505 $ cat > importfail.py <<EOF
505 $ cat > importfail.py <<EOF
506 > import somebogusmodule
506 > import somebogusmodule
507 > # dereference something in the module to force demandimport to load it
507 > # dereference something in the module to force demandimport to load it
508 > somebogusmodule.whatever
508 > somebogusmodule.whatever
509 > EOF
509 > EOF
510
510
511 $ echo '[hooks]' > .hg/hgrc
511 $ echo '[hooks]' > .hg/hgrc
512 $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc
512 $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc
513
513
514 $ echo a >> a
514 $ echo a >> a
515 $ hg --traceback commit -ma 2>&1 | egrep '^(exception|Traceback|ImportError)'
515 $ hg --traceback commit -ma 2>&1 | egrep '^(exception|Traceback|ImportError)'
516 exception from first failed import attempt:
516 exception from first failed import attempt:
517 Traceback (most recent call last):
517 Traceback (most recent call last):
518 ImportError: No module named somebogusmodule
518 ImportError: No module named somebogusmodule
519 exception from second failed import attempt:
519 exception from second failed import attempt:
520 Traceback (most recent call last):
520 Traceback (most recent call last):
521 ImportError: No module named hgext_importfail
521 ImportError: No module named hgext_importfail
522 Traceback (most recent call last):
522 Traceback (most recent call last):
523
523
524 Issue1827: Hooks Update & Commit not completely post operation
524 Issue1827: Hooks Update & Commit not completely post operation
525
525
526 commit and update hooks should run after command completion
526 commit and update hooks should run after command completion
527
527
528 $ echo '[hooks]' > .hg/hgrc
528 $ echo '[hooks]' > .hg/hgrc
529 $ echo 'commit = hg id' >> .hg/hgrc
529 $ echo 'commit = hg id' >> .hg/hgrc
530 $ echo 'update = hg id' >> .hg/hgrc
530 $ echo 'update = hg id' >> .hg/hgrc
531 $ echo bb > a
531 $ echo bb > a
532 $ hg ci -ma
532 $ hg ci -ma
533 223eafe2750c tip
533 223eafe2750c tip
534 $ hg up 0
534 $ hg up 0
535 cb9a9f314b8b
535 cb9a9f314b8b
536 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
536 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
537
537
@@ -1,716 +1,714 b''
1 $ echo "[extensions]" >> $HGRCPATH
1 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "graphlog=" >> $HGRCPATH
2 $ echo "graphlog=" >> $HGRCPATH
3
3
4 $ hg init a
4 $ hg init a
5 $ cd a
5 $ cd a
6 $ echo foo > t1
6 $ echo foo > t1
7 $ hg add t1
7 $ hg add t1
8 $ hg commit -m "1"
8 $ hg commit -m "1"
9
9
10 $ cd ..
10 $ cd ..
11 $ hg clone a b
11 $ hg clone a b
12 updating to branch default
12 updating to branch default
13 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
13 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
14
14
15 $ cd a
15 $ cd a
16 $ echo foo > t2
16 $ echo foo > t2
17 $ hg add t2
17 $ hg add t2
18 $ hg commit -m "2"
18 $ hg commit -m "2"
19
19
20 $ cd ../b
20 $ cd ../b
21 $ echo foo > t3
21 $ echo foo > t3
22 $ hg add t3
22 $ hg add t3
23 $ hg commit -m "3"
23 $ hg commit -m "3"
24
24
25 $ hg push ../a
25 $ hg push ../a
26 pushing to ../a
26 pushing to ../a
27 searching for changes
27 searching for changes
28 abort: push creates new remote heads on branch 'default'!
28 abort: push creates new remote heads on branch 'default'!
29 (you should pull and merge or use push -f to force)
29 (you should pull and merge or use push -f to force)
30 [255]
30 [255]
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'!
45 (you should pull and merge or use push -f to force)
43 (you should pull and merge or use push -f to force)
46 [255]
44 [255]
47
45
48 $ hg pull ../a
46 $ hg pull ../a
49 pulling from ../a
47 pulling from ../a
50 searching for changes
48 searching for changes
51 adding changesets
49 adding changesets
52 adding manifests
50 adding manifests
53 adding file changes
51 adding file changes
54 added 1 changesets with 1 changes to 1 files (+1 heads)
52 added 1 changesets with 1 changes to 1 files (+1 heads)
55 (run 'hg heads' to see heads, 'hg merge' to merge)
53 (run 'hg heads' to see heads, 'hg merge' to merge)
56
54
57 $ hg push ../a
55 $ hg push ../a
58 pushing to ../a
56 pushing to ../a
59 searching for changes
57 searching for changes
60 abort: push creates new remote heads on branch 'default'!
58 abort: push creates new remote heads on branch 'default'!
61 (did you forget to merge? use push -f to force)
59 (did you forget to merge? use push -f to force)
62 [255]
60 [255]
63
61
64 $ hg merge
62 $ hg merge
65 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
63 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
66 (branch merge, don't forget to commit)
64 (branch merge, don't forget to commit)
67
65
68 $ hg commit -m "4"
66 $ hg commit -m "4"
69 $ hg push ../a
67 $ hg push ../a
70 pushing to ../a
68 pushing to ../a
71 searching for changes
69 searching for changes
72 adding changesets
70 adding changesets
73 adding manifests
71 adding manifests
74 adding file changes
72 adding file changes
75 added 2 changesets with 1 changes to 1 files
73 added 2 changesets with 1 changes to 1 files
76
74
77 $ cd ..
75 $ cd ..
78
76
79 $ hg init c
77 $ hg init c
80 $ cd c
78 $ cd c
81 $ for i in 0 1 2; do
79 $ for i in 0 1 2; do
82 > echo $i >> foo
80 > echo $i >> foo
83 > hg ci -Am $i
81 > hg ci -Am $i
84 > done
82 > done
85 adding foo
83 adding foo
86 $ cd ..
84 $ cd ..
87
85
88 $ hg clone c d
86 $ hg clone c d
89 updating to branch default
87 updating to branch default
90 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
91
89
92 $ cd d
90 $ cd d
93 $ for i in 0 1; do
91 $ for i in 0 1; do
94 > hg co -C $i
92 > hg co -C $i
95 > echo d-$i >> foo
93 > echo d-$i >> foo
96 > hg ci -m d-$i
94 > hg ci -m d-$i
97 > done
95 > done
98 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
96 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
99 created new head
97 created new head
100 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 created new head
99 created new head
102
100
103 $ HGMERGE=true hg merge 3
101 $ HGMERGE=true hg merge 3
104 merging foo
102 merging foo
105 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
103 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
106 (branch merge, don't forget to commit)
104 (branch merge, don't forget to commit)
107
105
108 $ hg ci -m c-d
106 $ hg ci -m c-d
109
107
110 $ hg push ../c
108 $ hg push ../c
111 pushing to ../c
109 pushing to ../c
112 searching for changes
110 searching for changes
113 abort: push creates new remote heads on branch 'default'!
111 abort: push creates new remote heads on branch 'default'!
114 (did you forget to merge? use push -f to force)
112 (did you forget to merge? use push -f to force)
115 [255]
113 [255]
116
114
117 $ hg push -r 2 ../c
115 $ hg push -r 2 ../c
118 pushing to ../c
116 pushing to ../c
119 searching for changes
117 searching for changes
120 no changes found
118 no changes found
121
119
122 $ hg push -r 3 ../c
120 $ hg push -r 3 ../c
123 pushing to ../c
121 pushing to ../c
124 searching for changes
122 searching for changes
125 abort: push creates new remote heads on branch 'default'!
123 abort: push creates new remote heads on branch 'default'!
126 (did you forget to merge? use push -f to force)
124 (did you forget to merge? use push -f to force)
127 [255]
125 [255]
128
126
129 $ hg push -r 3 -r 4 ../c
127 $ hg push -r 3 -r 4 ../c
130 pushing to ../c
128 pushing to ../c
131 searching for changes
129 searching for changes
132 abort: push creates new remote heads on branch 'default'!
130 abort: push creates new remote heads on branch 'default'!
133 (did you forget to merge? use push -f to force)
131 (did you forget to merge? use push -f to force)
134 [255]
132 [255]
135
133
136 $ hg push -f -r 3 -r 4 ../c
134 $ hg push -f -r 3 -r 4 ../c
137 pushing to ../c
135 pushing to ../c
138 searching for changes
136 searching for changes
139 adding changesets
137 adding changesets
140 adding manifests
138 adding manifests
141 adding file changes
139 adding file changes
142 added 2 changesets with 2 changes to 1 files (+2 heads)
140 added 2 changesets with 2 changes to 1 files (+2 heads)
143
141
144 $ hg push -r 5 ../c
142 $ hg push -r 5 ../c
145 pushing to ../c
143 pushing to ../c
146 searching for changes
144 searching for changes
147 adding changesets
145 adding changesets
148 adding manifests
146 adding manifests
149 adding file changes
147 adding file changes
150 added 1 changesets with 1 changes to 1 files (-1 heads)
148 added 1 changesets with 1 changes to 1 files (-1 heads)
151
149
152 $ hg in ../c
150 $ hg in ../c
153 comparing with ../c
151 comparing with ../c
154 searching for changes
152 searching for changes
155 no changes found
153 no changes found
156 [1]
154 [1]
157
155
158
156
159 Issue450: push -r warns about remote head creation even if no heads
157 Issue450: push -r warns about remote head creation even if no heads
160 will be created
158 will be created
161
159
162 $ hg init ../e
160 $ hg init ../e
163 $ hg push -r 0 ../e
161 $ hg push -r 0 ../e
164 pushing to ../e
162 pushing to ../e
165 searching for changes
163 searching for changes
166 adding changesets
164 adding changesets
167 adding manifests
165 adding manifests
168 adding file changes
166 adding file changes
169 added 1 changesets with 1 changes to 1 files
167 added 1 changesets with 1 changes to 1 files
170
168
171 $ hg push -r 1 ../e
169 $ hg push -r 1 ../e
172 pushing to ../e
170 pushing to ../e
173 searching for changes
171 searching for changes
174 adding changesets
172 adding changesets
175 adding manifests
173 adding manifests
176 adding file changes
174 adding file changes
177 added 1 changesets with 1 changes to 1 files
175 added 1 changesets with 1 changes to 1 files
178
176
179 $ cd ..
177 $ cd ..
180
178
181
179
182 Issue736: named branches are not considered for detection of
180 Issue736: named branches are not considered for detection of
183 unmerged heads in "hg push"
181 unmerged heads in "hg push"
184
182
185 $ hg init f
183 $ hg init f
186 $ cd f
184 $ cd f
187 $ hg -q branch a
185 $ hg -q branch a
188 $ echo 0 > foo
186 $ echo 0 > foo
189 $ hg -q ci -Am 0
187 $ hg -q ci -Am 0
190 $ echo 1 > foo
188 $ echo 1 > foo
191 $ hg -q ci -m 1
189 $ hg -q ci -m 1
192 $ hg -q up 0
190 $ hg -q up 0
193 $ echo 2 > foo
191 $ echo 2 > foo
194 $ hg -q ci -m 2
192 $ hg -q ci -m 2
195 $ hg -q up 0
193 $ hg -q up 0
196 $ hg -q branch b
194 $ hg -q branch b
197 $ echo 3 > foo
195 $ echo 3 > foo
198 $ hg -q ci -m 3
196 $ hg -q ci -m 3
199 $ cd ..
197 $ cd ..
200
198
201 $ hg -q clone f g
199 $ hg -q clone f g
202 $ cd g
200 $ cd g
203
201
204 Push on existing branch and new branch:
202 Push on existing branch and new branch:
205
203
206 $ hg -q up 1
204 $ hg -q up 1
207 $ echo 4 > foo
205 $ echo 4 > foo
208 $ hg -q ci -m 4
206 $ hg -q ci -m 4
209 $ hg -q up 0
207 $ hg -q up 0
210 $ echo 5 > foo
208 $ echo 5 > foo
211 $ hg -q branch c
209 $ hg -q branch c
212 $ hg -q ci -m 5
210 $ hg -q ci -m 5
213
211
214 $ hg push ../f
212 $ hg push ../f
215 pushing to ../f
213 pushing to ../f
216 searching for changes
214 searching for changes
217 abort: push creates new remote branches: c!
215 abort: push creates new remote branches: c!
218 (use 'hg push --new-branch' to create new remote branches)
216 (use 'hg push --new-branch' to create new remote branches)
219 [255]
217 [255]
220
218
221 $ hg push -r 4 -r 5 ../f
219 $ hg push -r 4 -r 5 ../f
222 pushing to ../f
220 pushing to ../f
223 searching for changes
221 searching for changes
224 abort: push creates new remote branches: c!
222 abort: push creates new remote branches: c!
225 (use 'hg push --new-branch' to create new remote branches)
223 (use 'hg push --new-branch' to create new remote branches)
226 [255]
224 [255]
227
225
228
226
229 Multiple new branches:
227 Multiple new branches:
230
228
231 $ hg -q branch d
229 $ hg -q branch d
232 $ echo 6 > foo
230 $ echo 6 > foo
233 $ hg -q ci -m 6
231 $ hg -q ci -m 6
234
232
235 $ hg push ../f
233 $ hg push ../f
236 pushing to ../f
234 pushing to ../f
237 searching for changes
235 searching for changes
238 abort: push creates new remote branches: c, d!
236 abort: push creates new remote branches: c, d!
239 (use 'hg push --new-branch' to create new remote branches)
237 (use 'hg push --new-branch' to create new remote branches)
240 [255]
238 [255]
241
239
242 $ hg push -r 4 -r 6 ../f
240 $ hg push -r 4 -r 6 ../f
243 pushing to ../f
241 pushing to ../f
244 searching for changes
242 searching for changes
245 abort: push creates new remote branches: c, d!
243 abort: push creates new remote branches: c, d!
246 (use 'hg push --new-branch' to create new remote branches)
244 (use 'hg push --new-branch' to create new remote branches)
247 [255]
245 [255]
248
246
249 $ cd ../g
247 $ cd ../g
250
248
251
249
252 Fail on multiple head push:
250 Fail on multiple head push:
253
251
254 $ hg -q up 1
252 $ hg -q up 1
255 $ echo 7 > foo
253 $ echo 7 > foo
256 $ hg -q ci -m 7
254 $ hg -q ci -m 7
257
255
258 $ hg push -r 4 -r 7 ../f
256 $ hg push -r 4 -r 7 ../f
259 pushing to ../f
257 pushing to ../f
260 searching for changes
258 searching for changes
261 abort: push creates new remote heads on branch 'a'!
259 abort: push creates new remote heads on branch 'a'!
262 (did you forget to merge? use push -f to force)
260 (did you forget to merge? use push -f to force)
263 [255]
261 [255]
264
262
265 Push replacement head on existing branches:
263 Push replacement head on existing branches:
266
264
267 $ hg -q up 3
265 $ hg -q up 3
268 $ echo 8 > foo
266 $ echo 8 > foo
269 $ hg -q ci -m 8
267 $ hg -q ci -m 8
270
268
271 $ hg push -r 7 -r 8 ../f
269 $ hg push -r 7 -r 8 ../f
272 pushing to ../f
270 pushing to ../f
273 searching for changes
271 searching for changes
274 adding changesets
272 adding changesets
275 adding manifests
273 adding manifests
276 adding file changes
274 adding file changes
277 added 2 changesets with 2 changes to 1 files
275 added 2 changesets with 2 changes to 1 files
278
276
279
277
280 Merge of branch a to other branch b followed by unrelated push
278 Merge of branch a to other branch b followed by unrelated push
281 on branch a:
279 on branch a:
282
280
283 $ hg -q up 7
281 $ hg -q up 7
284 $ HGMERGE=true hg -q merge 8
282 $ HGMERGE=true hg -q merge 8
285 $ hg -q ci -m 9
283 $ hg -q ci -m 9
286 $ hg -q up 8
284 $ hg -q up 8
287 $ echo 10 > foo
285 $ echo 10 > foo
288 $ hg -q ci -m 10
286 $ hg -q ci -m 10
289
287
290 $ hg push -r 9 ../f
288 $ hg push -r 9 ../f
291 pushing to ../f
289 pushing to ../f
292 searching for changes
290 searching for changes
293 adding changesets
291 adding changesets
294 adding manifests
292 adding manifests
295 adding file changes
293 adding file changes
296 added 1 changesets with 1 changes to 1 files (-1 heads)
294 added 1 changesets with 1 changes to 1 files (-1 heads)
297
295
298 $ hg push -r 10 ../f
296 $ hg push -r 10 ../f
299 pushing to ../f
297 pushing to ../f
300 searching for changes
298 searching for changes
301 adding changesets
299 adding changesets
302 adding manifests
300 adding manifests
303 adding file changes
301 adding file changes
304 added 1 changesets with 1 changes to 1 files (+1 heads)
302 added 1 changesets with 1 changes to 1 files (+1 heads)
305
303
306
304
307 Cheating the counting algorithm:
305 Cheating the counting algorithm:
308
306
309 $ hg -q up 9
307 $ hg -q up 9
310 $ HGMERGE=true hg -q merge 2
308 $ HGMERGE=true hg -q merge 2
311 $ hg -q ci -m 11
309 $ hg -q ci -m 11
312 $ hg -q up 1
310 $ hg -q up 1
313 $ echo 12 > foo
311 $ echo 12 > foo
314 $ hg -q ci -m 12
312 $ hg -q ci -m 12
315
313
316 $ hg push -r 11 -r 12 ../f
314 $ hg push -r 11 -r 12 ../f
317 pushing to ../f
315 pushing to ../f
318 searching for changes
316 searching for changes
319 adding changesets
317 adding changesets
320 adding manifests
318 adding manifests
321 adding file changes
319 adding file changes
322 added 2 changesets with 2 changes to 1 files
320 added 2 changesets with 2 changes to 1 files
323
321
324
322
325 Failed push of new named branch:
323 Failed push of new named branch:
326
324
327 $ echo 12 > foo
325 $ echo 12 > foo
328 $ hg -q ci -m 12a
326 $ hg -q ci -m 12a
329 [1]
327 [1]
330 $ hg -q up 11
328 $ hg -q up 11
331 $ echo 13 > foo
329 $ echo 13 > foo
332 $ hg -q branch e
330 $ hg -q branch e
333 $ hg -q ci -m 13d
331 $ hg -q ci -m 13d
334
332
335 $ hg push -r 12 -r 13 ../f
333 $ hg push -r 12 -r 13 ../f
336 pushing to ../f
334 pushing to ../f
337 searching for changes
335 searching for changes
338 abort: push creates new remote branches: e!
336 abort: push creates new remote branches: e!
339 (use 'hg push --new-branch' to create new remote branches)
337 (use 'hg push --new-branch' to create new remote branches)
340 [255]
338 [255]
341
339
342
340
343 Using --new-branch to push new named branch:
341 Using --new-branch to push new named branch:
344
342
345 $ hg push --new-branch -r 12 -r 13 ../f
343 $ hg push --new-branch -r 12 -r 13 ../f
346 pushing to ../f
344 pushing to ../f
347 searching for changes
345 searching for changes
348 adding changesets
346 adding changesets
349 adding manifests
347 adding manifests
350 adding file changes
348 adding file changes
351 added 1 changesets with 1 changes to 1 files
349 added 1 changesets with 1 changes to 1 files
352
350
353
351
354 Checking prepush logic does not allow silently pushing
352 Checking prepush logic does not allow silently pushing
355 multiple new heads:
353 multiple new heads:
356
354
357 $ cd ..
355 $ cd ..
358 $ hg init h
356 $ hg init h
359 $ echo init > h/init
357 $ echo init > h/init
360 $ hg -R h ci -Am init
358 $ hg -R h ci -Am init
361 adding init
359 adding init
362 $ echo a > h/a
360 $ echo a > h/a
363 $ hg -R h ci -Am a
361 $ hg -R h ci -Am a
364 adding a
362 adding a
365 $ hg clone h i
363 $ hg clone h i
366 updating to branch default
364 updating to branch default
367 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
365 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
368 $ hg -R h up 0
366 $ hg -R h up 0
369 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
367 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
370 $ echo b > h/b
368 $ echo b > h/b
371 $ hg -R h ci -Am b
369 $ hg -R h ci -Am b
372 adding b
370 adding b
373 created new head
371 created new head
374 $ hg -R i up 0
372 $ hg -R i up 0
375 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
373 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
376 $ echo c > i/c
374 $ echo c > i/c
377 $ hg -R i ci -Am c
375 $ hg -R i ci -Am c
378 adding c
376 adding c
379 created new head
377 created new head
380
378
381 $ hg -R i push h
379 $ hg -R i push h
382 pushing to h
380 pushing to h
383 searching for changes
381 searching for changes
384 abort: push creates new remote heads on branch 'default'!
382 abort: push creates new remote heads on branch 'default'!
385 (you should pull and merge or use push -f to force)
383 (you should pull and merge or use push -f to force)
386 [255]
384 [255]
387
385
388
386
389 Check prepush logic with merged branches:
387 Check prepush logic with merged branches:
390
388
391 $ hg init j
389 $ hg init j
392 $ hg -R j branch a
390 $ hg -R j branch a
393 marked working directory as branch a
391 marked working directory as branch a
394 $ echo init > j/foo
392 $ echo init > j/foo
395 $ hg -R j ci -Am init
393 $ hg -R j ci -Am init
396 adding foo
394 adding foo
397 $ hg clone j k
395 $ hg clone j k
398 updating to branch a
396 updating to branch a
399 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
397 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
400 $ echo a1 > j/foo
398 $ echo a1 > j/foo
401 $ hg -R j ci -m a1
399 $ hg -R j ci -m a1
402 $ hg -R k branch b
400 $ hg -R k branch b
403 marked working directory as branch b
401 marked working directory as branch b
404 $ echo b > k/foo
402 $ echo b > k/foo
405 $ hg -R k ci -m b
403 $ hg -R k ci -m b
406 $ hg -R k up 0
404 $ hg -R k up 0
407 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
405 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
408
406
409 $ hg -R k merge b
407 $ hg -R k merge b
410 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
408 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
411 (branch merge, don't forget to commit)
409 (branch merge, don't forget to commit)
412
410
413 $ hg -R k ci -m merge
411 $ hg -R k ci -m merge
414
412
415 $ hg -R k push -r a j
413 $ hg -R k push -r a j
416 pushing to j
414 pushing to j
417 searching for changes
415 searching for changes
418 abort: push creates new remote branches: b!
416 abort: push creates new remote branches: b!
419 (use 'hg push --new-branch' to create new remote branches)
417 (use 'hg push --new-branch' to create new remote branches)
420 [255]
418 [255]
421
419
422
420
423 Prepush -r should not allow you to sneak in new heads:
421 Prepush -r should not allow you to sneak in new heads:
424
422
425 $ hg init l
423 $ hg init l
426 $ cd l
424 $ cd l
427 $ echo a >> foo
425 $ echo a >> foo
428 $ hg -q add foo
426 $ hg -q add foo
429 $ hg -q branch a
427 $ hg -q branch a
430 $ hg -q ci -ma
428 $ hg -q ci -ma
431 $ hg -q up null
429 $ hg -q up null
432 $ echo a >> foo
430 $ echo a >> foo
433 $ hg -q add foo
431 $ hg -q add foo
434 $ hg -q branch b
432 $ hg -q branch b
435 $ hg -q ci -mb
433 $ hg -q ci -mb
436 $ cd ..
434 $ cd ..
437 $ hg -q clone l m -u a
435 $ hg -q clone l m -u a
438 $ cd m
436 $ cd m
439 $ hg -q merge b
437 $ hg -q merge b
440 $ hg -q ci -mmb
438 $ hg -q ci -mmb
441 $ hg -q up 0
439 $ hg -q up 0
442 $ echo a >> foo
440 $ echo a >> foo
443 $ hg -q ci -ma2
441 $ hg -q ci -ma2
444 $ hg -q up 2
442 $ hg -q up 2
445 $ echo a >> foo
443 $ echo a >> foo
446 $ hg -q branch -f b
444 $ hg -q branch -f b
447 $ hg -q ci -mb2
445 $ hg -q ci -mb2
448 $ hg -q merge 3
446 $ hg -q merge 3
449 $ hg -q ci -mma
447 $ hg -q ci -mma
450
448
451 $ hg push ../l -b b
449 $ hg push ../l -b b
452 pushing to ../l
450 pushing to ../l
453 searching for changes
451 searching for changes
454 abort: push creates new remote heads on branch 'a'!
452 abort: push creates new remote heads on branch 'a'!
455 (did you forget to merge? use push -f to force)
453 (did you forget to merge? use push -f to force)
456 [255]
454 [255]
457
455
458 $ cd ..
456 $ cd ..
459
457
460
458
461 Check prepush with new branch head on former topo non-head:
459 Check prepush with new branch head on former topo non-head:
462
460
463 $ hg init n
461 $ hg init n
464 $ cd n
462 $ cd n
465 $ hg branch A
463 $ hg branch A
466 marked working directory as branch A
464 marked working directory as branch A
467 $ echo a >a
465 $ echo a >a
468 $ hg ci -Ama
466 $ hg ci -Ama
469 adding a
467 adding a
470 $ hg branch B
468 $ hg branch B
471 marked working directory as branch B
469 marked working directory as branch B
472 $ echo b >b
470 $ echo b >b
473 $ hg ci -Amb
471 $ hg ci -Amb
474 adding b
472 adding b
475
473
476 b is now branch head of B, and a topological head
474 b is now branch head of B, and a topological head
477 a is now branch head of A, but not a topological head
475 a is now branch head of A, but not a topological head
478
476
479 $ hg clone . inner
477 $ hg clone . inner
480 updating to branch B
478 updating to branch B
481 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
479 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
482 $ cd inner
480 $ cd inner
483 $ hg up B
481 $ hg up B
484 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
482 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
485 $ echo b1 >b1
483 $ echo b1 >b1
486 $ hg ci -Amb1
484 $ hg ci -Amb1
487 adding b1
485 adding b1
488
486
489 in the clone b1 is now the head of B
487 in the clone b1 is now the head of B
490
488
491 $ cd ..
489 $ cd ..
492 $ hg up 0
490 $ hg up 0
493 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
491 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
494 $ echo a2 >a2
492 $ echo a2 >a2
495 $ hg ci -Ama2
493 $ hg ci -Ama2
496 adding a2
494 adding a2
497
495
498 a2 is now the new branch head of A, and a new topological head
496 a2 is now the new branch head of A, and a new topological head
499 it replaces a former inner branch head, so it should at most warn about
497 it replaces a former inner branch head, so it should at most warn about
500 A, not B
498 A, not B
501
499
502 glog of local:
500 glog of local:
503
501
504 $ hg glog --template "{rev}: {branches} {desc}\n"
502 $ hg glog --template "{rev}: {branches} {desc}\n"
505 @ 2: A a2
503 @ 2: A a2
506 |
504 |
507 | o 1: B b
505 | o 1: B b
508 |/
506 |/
509 o 0: A a
507 o 0: A a
510
508
511 glog of remote:
509 glog of remote:
512
510
513 $ hg glog -R inner --template "{rev}: {branches} {desc}\n"
511 $ hg glog -R inner --template "{rev}: {branches} {desc}\n"
514 @ 2: B b1
512 @ 2: B b1
515 |
513 |
516 o 1: B b
514 o 1: B b
517 |
515 |
518 o 0: A a
516 o 0: A a
519
517
520 outgoing:
518 outgoing:
521
519
522 $ hg out inner --template "{rev}: {branches} {desc}\n"
520 $ hg out inner --template "{rev}: {branches} {desc}\n"
523 comparing with inner
521 comparing with inner
524 searching for changes
522 searching for changes
525 2: A a2
523 2: A a2
526
524
527 $ hg push inner
525 $ hg push inner
528 pushing to inner
526 pushing to inner
529 searching for changes
527 searching for changes
530 adding changesets
528 adding changesets
531 adding manifests
529 adding manifests
532 adding file changes
530 adding file changes
533 added 1 changesets with 1 changes to 1 files (+1 heads)
531 added 1 changesets with 1 changes to 1 files (+1 heads)
534
532
535 $ cd ..
533 $ cd ..
536
534
537
535
538 Check prepush with new branch head on former topo head:
536 Check prepush with new branch head on former topo head:
539
537
540 $ hg init o
538 $ hg init o
541 $ cd o
539 $ cd o
542 $ hg branch A
540 $ hg branch A
543 marked working directory as branch A
541 marked working directory as branch A
544 $ echo a >a
542 $ echo a >a
545 $ hg ci -Ama
543 $ hg ci -Ama
546 adding a
544 adding a
547 $ hg branch B
545 $ hg branch B
548 marked working directory as branch B
546 marked working directory as branch B
549 $ echo b >b
547 $ echo b >b
550 $ hg ci -Amb
548 $ hg ci -Amb
551 adding b
549 adding b
552
550
553 b is now branch head of B, and a topological head
551 b is now branch head of B, and a topological head
554
552
555 $ hg up 0
553 $ hg up 0
556 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
554 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
557 $ echo a1 >a1
555 $ echo a1 >a1
558 $ hg ci -Ama1
556 $ hg ci -Ama1
559 adding a1
557 adding a1
560
558
561 a1 is now branch head of A, and a topological head
559 a1 is now branch head of A, and a topological head
562
560
563 $ hg clone . inner
561 $ hg clone . inner
564 updating to branch A
562 updating to branch A
565 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
563 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
566 $ cd inner
564 $ cd inner
567 $ hg up B
565 $ hg up B
568 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
566 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
569 $ echo b1 >b1
567 $ echo b1 >b1
570 $ hg ci -Amb1
568 $ hg ci -Amb1
571 adding b1
569 adding b1
572
570
573 in the clone b1 is now the head of B
571 in the clone b1 is now the head of B
574
572
575 $ cd ..
573 $ cd ..
576 $ echo a2 >a2
574 $ echo a2 >a2
577 $ hg ci -Ama2
575 $ hg ci -Ama2
578 adding a2
576 adding a2
579
577
580 a2 is now the new branch head of A, and a topological head
578 a2 is now the new branch head of A, and a topological head
581 it replaces a former topological and branch head, so this should not warn
579 it replaces a former topological and branch head, so this should not warn
582
580
583 glog of local:
581 glog of local:
584
582
585 $ hg glog --template "{rev}: {branches} {desc}\n"
583 $ hg glog --template "{rev}: {branches} {desc}\n"
586 @ 3: A a2
584 @ 3: A a2
587 |
585 |
588 o 2: A a1
586 o 2: A a1
589 |
587 |
590 | o 1: B b
588 | o 1: B b
591 |/
589 |/
592 o 0: A a
590 o 0: A a
593
591
594 glog of remote:
592 glog of remote:
595
593
596 $ hg glog -R inner --template "{rev}: {branches} {desc}\n"
594 $ hg glog -R inner --template "{rev}: {branches} {desc}\n"
597 @ 3: B b1
595 @ 3: B b1
598 |
596 |
599 | o 2: A a1
597 | o 2: A a1
600 | |
598 | |
601 o | 1: B b
599 o | 1: B b
602 |/
600 |/
603 o 0: A a
601 o 0: A a
604
602
605 outgoing:
603 outgoing:
606
604
607 $ hg out inner --template "{rev}: {branches} {desc}\n"
605 $ hg out inner --template "{rev}: {branches} {desc}\n"
608 comparing with inner
606 comparing with inner
609 searching for changes
607 searching for changes
610 3: A a2
608 3: A a2
611
609
612 $ hg push inner
610 $ hg push inner
613 pushing to inner
611 pushing to inner
614 searching for changes
612 searching for changes
615 adding changesets
613 adding changesets
616 adding manifests
614 adding manifests
617 adding file changes
615 adding file changes
618 added 1 changesets with 1 changes to 1 files
616 added 1 changesets with 1 changes to 1 files
619
617
620 $ cd ..
618 $ cd ..
621
619
622
620
623 Check prepush with new branch head and new child of former branch head
621 Check prepush with new branch head and new child of former branch head
624 but child is on different branch:
622 but child is on different branch:
625
623
626 $ hg init p
624 $ hg init p
627 $ cd p
625 $ cd p
628 $ hg branch A
626 $ hg branch A
629 marked working directory as branch A
627 marked working directory as branch A
630 $ echo a0 >a
628 $ echo a0 >a
631 $ hg ci -Ama0
629 $ hg ci -Ama0
632 adding a
630 adding a
633 $ echo a1 >a
631 $ echo a1 >a
634 $ hg ci -ma1
632 $ hg ci -ma1
635 $ hg up null
633 $ hg up null
636 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
634 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
637 $ hg branch B
635 $ hg branch B
638 marked working directory as branch B
636 marked working directory as branch B
639 $ echo b0 >b
637 $ echo b0 >b
640 $ hg ci -Amb0
638 $ hg ci -Amb0
641 adding b
639 adding b
642 $ echo b1 >b
640 $ echo b1 >b
643 $ hg ci -mb1
641 $ hg ci -mb1
644
642
645 $ hg clone . inner
643 $ hg clone . inner
646 updating to branch B
644 updating to branch B
647 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
645 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
648
646
649 $ hg up A
647 $ hg up A
650 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
648 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
651 $ hg branch -f B
649 $ hg branch -f B
652 marked working directory as branch B
650 marked working directory as branch B
653 $ echo a3 >a
651 $ echo a3 >a
654 $ hg ci -ma3
652 $ hg ci -ma3
655 created new head
653 created new head
656 $ hg up 3
654 $ hg up 3
657 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
655 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
658 $ hg branch -f A
656 $ hg branch -f A
659 marked working directory as branch A
657 marked working directory as branch A
660 $ echo b3 >b
658 $ echo b3 >b
661 $ hg ci -mb3
659 $ hg ci -mb3
662 created new head
660 created new head
663
661
664 glog of local:
662 glog of local:
665
663
666 $ hg glog --template "{rev}: {branches} {desc}\n"
664 $ hg glog --template "{rev}: {branches} {desc}\n"
667 @ 5: A b3
665 @ 5: A b3
668 |
666 |
669 | o 4: B a3
667 | o 4: B a3
670 | |
668 | |
671 o | 3: B b1
669 o | 3: B b1
672 | |
670 | |
673 o | 2: B b0
671 o | 2: B b0
674 /
672 /
675 o 1: A a1
673 o 1: A a1
676 |
674 |
677 o 0: A a0
675 o 0: A a0
678
676
679 glog of remote:
677 glog of remote:
680
678
681 $ hg glog -R inner --template "{rev}: {branches} {desc}\n"
679 $ hg glog -R inner --template "{rev}: {branches} {desc}\n"
682 @ 3: B b1
680 @ 3: B b1
683 |
681 |
684 o 2: B b0
682 o 2: B b0
685
683
686 o 1: A a1
684 o 1: A a1
687 |
685 |
688 o 0: A a0
686 o 0: A a0
689
687
690 outgoing:
688 outgoing:
691
689
692 $ hg out inner --template "{rev}: {branches} {desc}\n"
690 $ hg out inner --template "{rev}: {branches} {desc}\n"
693 comparing with inner
691 comparing with inner
694 searching for changes
692 searching for changes
695 4: B a3
693 4: B a3
696 5: A b3
694 5: A b3
697
695
698 $ hg push inner
696 $ hg push inner
699 pushing to inner
697 pushing to inner
700 searching for changes
698 searching for changes
701 abort: push creates new remote heads on branch 'A'!
699 abort: push creates new remote heads on branch 'A'!
702 (did you forget to merge? use push -f to force)
700 (did you forget to merge? use push -f to force)
703 [255]
701 [255]
704
702
705 $ hg push inner -r4 -r5
703 $ hg push inner -r4 -r5
706 pushing to inner
704 pushing to inner
707 searching for changes
705 searching for changes
708 abort: push creates new remote heads on branch 'A'!
706 abort: push creates new remote heads on branch 'A'!
709 (did you forget to merge? use push -f to force)
707 (did you forget to merge? use push -f to force)
710 [255]
708 [255]
711
709
712 $ hg in inner
710 $ hg in inner
713 comparing with inner
711 comparing with inner
714 searching for changes
712 searching for changes
715 no changes found
713 no changes found
716 [1]
714 [1]
@@ -1,46 +1,47 b''
1
1
2 $ cat <<EOF >> $HGRCPATH
2 $ cat <<EOF >> $HGRCPATH
3 > [extensions]
3 > [extensions]
4 > schemes=
4 > schemes=
5 >
5 >
6 > [schemes]
6 > [schemes]
7 > l = http://localhost:$HGPORT/
7 > l = http://localhost:$HGPORT/
8 > parts = http://{1}:$HGPORT/
8 > parts = http://{1}:$HGPORT/
9 > z = file:\$PWD/
9 > z = file:\$PWD/
10 > EOF
10 > EOF
11 $ hg init test
11 $ hg init test
12 $ cd test
12 $ cd test
13 $ echo a > a
13 $ echo a > a
14 $ hg ci -Am initial
14 $ hg ci -Am initial
15 adding a
15 adding a
16 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
16 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
17 $ cat hg.pid >> $DAEMON_PIDS
17 $ cat hg.pid >> $DAEMON_PIDS
18 $ hg incoming l://
18 $ hg incoming l://
19 comparing with l://
19 comparing with l://
20 searching for changes
20 searching for changes
21 no changes found
21 no changes found
22 [1]
22 [1]
23
23
24 check that {1} syntax works
24 check that {1} syntax works
25
25
26 $ hg incoming --debug parts://localhost
26 $ hg incoming --debug parts://localhost
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
36 check that paths are expanded
37 check that paths are expanded
37
38
38 $ PWD=`pwd` hg incoming z://
39 $ PWD=`pwd` hg incoming z://
39 comparing with z://
40 comparing with z://
40 searching for changes
41 searching for changes
41 no changes found
42 no changes found
42 [1]
43 [1]
43
44
44 errors
45 errors
45
46
46 $ cat errors.log
47 $ cat errors.log
@@ -1,311 +1,310 b''
1
1
2
2
3 This test tries to exercise the ssh functionality with a dummy script
3 This test tries to exercise the ssh functionality with a dummy script
4
4
5 $ cat <<EOF > dummyssh
5 $ cat <<EOF > dummyssh
6 > import sys
6 > import sys
7 > import os
7 > import os
8 >
8 >
9 > os.chdir(os.path.dirname(sys.argv[0]))
9 > os.chdir(os.path.dirname(sys.argv[0]))
10 > if sys.argv[1] != "user@dummy":
10 > if sys.argv[1] != "user@dummy":
11 > sys.exit(-1)
11 > sys.exit(-1)
12 >
12 >
13 > if not os.path.exists("dummyssh"):
13 > if not os.path.exists("dummyssh"):
14 > sys.exit(-1)
14 > sys.exit(-1)
15 >
15 >
16 > os.environ["SSH_CLIENT"] = "127.0.0.1 1 2"
16 > os.environ["SSH_CLIENT"] = "127.0.0.1 1 2"
17 >
17 >
18 > log = open("dummylog", "ab")
18 > log = open("dummylog", "ab")
19 > log.write("Got arguments")
19 > log.write("Got arguments")
20 > for i, arg in enumerate(sys.argv[1:]):
20 > for i, arg in enumerate(sys.argv[1:]):
21 > log.write(" %d:%s" % (i+1, arg))
21 > log.write(" %d:%s" % (i+1, arg))
22 > log.write("\n")
22 > log.write("\n")
23 > log.close()
23 > log.close()
24 > r = os.system(sys.argv[2])
24 > r = os.system(sys.argv[2])
25 > sys.exit(bool(r))
25 > sys.exit(bool(r))
26 > EOF
26 > EOF
27 $ cat <<EOF > badhook
27 $ cat <<EOF > badhook
28 > import sys
28 > import sys
29 > sys.stdout.write("KABOOM\n")
29 > sys.stdout.write("KABOOM\n")
30 > EOF
30 > EOF
31
31
32 creating 'remote
32 creating 'remote
33
33
34 $ hg init remote
34 $ hg init remote
35 $ cd remote
35 $ cd remote
36 $ echo this > foo
36 $ echo this > foo
37 $ echo this > fooO
37 $ echo this > fooO
38 $ hg ci -A -m "init" foo fooO
38 $ hg ci -A -m "init" foo fooO
39 $ echo <<EOF > .hg/hgrc
39 $ echo <<EOF > .hg/hgrc
40 > [server]
40 > [server]
41 > uncompressed = True
41 > uncompressed = True
42 >
42 >
43 > [hooks]
43 > [hooks]
44 > changegroup = python "$TESTDIR"/printenv.py changegroup-in-remote 0 ../dummylog
44 > changegroup = python "$TESTDIR"/printenv.py changegroup-in-remote 0 ../dummylog
45 > EOF
45 > EOF
46 $ cd ..
46 $ cd ..
47
47
48 repo not found error
48 repo not found error
49
49
50 $ hg clone -e "python ./dummyssh" ssh://user@dummy/nonexistent local
50 $ hg clone -e "python ./dummyssh" ssh://user@dummy/nonexistent local
51 remote: abort: There is no Mercurial repository here (.hg not found)!
51 remote: abort: There is no Mercurial repository here (.hg not found)!
52 abort: no suitable response from remote hg!
52 abort: no suitable response from remote hg!
53 [255]
53 [255]
54
54
55 non-existent absolute path
55 non-existent absolute path
56
56
57 $ hg clone -e "python ./dummyssh" ssh://user@dummy//`pwd`/nonexistent local
57 $ hg clone -e "python ./dummyssh" ssh://user@dummy//`pwd`/nonexistent local
58 remote: abort: There is no Mercurial repository here (.hg not found)!
58 remote: abort: There is no Mercurial repository here (.hg not found)!
59 abort: no suitable response from remote hg!
59 abort: no suitable response from remote hg!
60 [255]
60 [255]
61
61
62 clone remote via stream
62 clone remote via stream
63
63
64 $ hg clone -e "python ./dummyssh" --uncompressed ssh://user@dummy/remote local-stream
64 $ hg clone -e "python ./dummyssh" --uncompressed ssh://user@dummy/remote local-stream
65 streaming all changes
65 streaming all changes
66 4 files to transfer, 392 bytes of data
66 4 files to transfer, 392 bytes of data
67 transferred 392 bytes in * seconds (*/sec) (glob)
67 transferred 392 bytes in * seconds (*/sec) (glob)
68 updating to branch default
68 updating to branch default
69 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
69 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
70 $ cd local-stream
70 $ cd local-stream
71 $ hg verify
71 $ hg verify
72 checking changesets
72 checking changesets
73 checking manifests
73 checking manifests
74 crosschecking files in changesets and manifests
74 crosschecking files in changesets and manifests
75 checking files
75 checking files
76 2 files, 1 changesets, 2 total revisions
76 2 files, 1 changesets, 2 total revisions
77 $ cd ..
77 $ cd ..
78
78
79 clone remote via pull
79 clone remote via pull
80
80
81 $ hg clone -e "python ./dummyssh" ssh://user@dummy/remote local
81 $ hg clone -e "python ./dummyssh" ssh://user@dummy/remote local
82 requesting all changes
82 requesting all changes
83 adding changesets
83 adding changesets
84 adding manifests
84 adding manifests
85 adding file changes
85 adding file changes
86 added 1 changesets with 2 changes to 2 files
86 added 1 changesets with 2 changes to 2 files
87 updating to branch default
87 updating to branch default
88 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
89
89
90 verify
90 verify
91
91
92 $ cd local
92 $ cd local
93 $ hg verify
93 $ hg verify
94 checking changesets
94 checking changesets
95 checking manifests
95 checking manifests
96 crosschecking files in changesets and manifests
96 crosschecking files in changesets and manifests
97 checking files
97 checking files
98 2 files, 1 changesets, 2 total revisions
98 2 files, 1 changesets, 2 total revisions
99 $ echo '[hooks]' >> .hg/hgrc
99 $ echo '[hooks]' >> .hg/hgrc
100 $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup-in-local 0 ../dummylog' >> .hg/hgrc
100 $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup-in-local 0 ../dummylog' >> .hg/hgrc
101
101
102 empty default pull
102 empty default pull
103
103
104 $ hg paths
104 $ hg paths
105 default = ssh://user@dummy/remote
105 default = ssh://user@dummy/remote
106 $ hg pull -e "python ../dummyssh"
106 $ hg pull -e "python ../dummyssh"
107 pulling from ssh://user@dummy/remote
107 pulling from ssh://user@dummy/remote
108 searching for changes
108 searching for changes
109 no changes found
109 no changes found
110
110
111 local change
111 local change
112
112
113 $ echo bleah > foo
113 $ echo bleah > foo
114 $ hg ci -m "add"
114 $ hg ci -m "add"
115
115
116 updating rc
116 updating rc
117
117
118 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
118 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
119 $ echo "[ui]" >> .hg/hgrc
119 $ echo "[ui]" >> .hg/hgrc
120 $ echo "ssh = python ../dummyssh" >> .hg/hgrc
120 $ echo "ssh = python ../dummyssh" >> .hg/hgrc
121
121
122 find outgoing
122 find outgoing
123
123
124 $ hg out ssh://user@dummy/remote
124 $ hg out ssh://user@dummy/remote
125 comparing with ssh://user@dummy/remote
125 comparing with ssh://user@dummy/remote
126 searching for changes
126 searching for changes
127 changeset: 1:a28a9d1a809c
127 changeset: 1:a28a9d1a809c
128 tag: tip
128 tag: tip
129 user: test
129 user: test
130 date: Thu Jan 01 00:00:00 1970 +0000
130 date: Thu Jan 01 00:00:00 1970 +0000
131 summary: add
131 summary: add
132
132
133
133
134 find incoming on the remote side
134 find incoming on the remote side
135
135
136 $ hg incoming -R ../remote -e "python ../dummyssh" ssh://user@dummy/local
136 $ hg incoming -R ../remote -e "python ../dummyssh" ssh://user@dummy/local
137 comparing with ssh://user@dummy/local
137 comparing with ssh://user@dummy/local
138 searching for changes
138 searching for changes
139 changeset: 1:a28a9d1a809c
139 changeset: 1:a28a9d1a809c
140 tag: tip
140 tag: tip
141 user: test
141 user: test
142 date: Thu Jan 01 00:00:00 1970 +0000
142 date: Thu Jan 01 00:00:00 1970 +0000
143 summary: add
143 summary: add
144
144
145
145
146 find incoming on the remote side (using absolute path)
146 find incoming on the remote side (using absolute path)
147
147
148 $ hg incoming -R ../remote -e "python ../dummyssh" "ssh://user@dummy/`pwd`"
148 $ hg incoming -R ../remote -e "python ../dummyssh" "ssh://user@dummy/`pwd`"
149 comparing with ssh://user@dummy/$TESTTMP/local
149 comparing with ssh://user@dummy/$TESTTMP/local
150 searching for changes
150 searching for changes
151 changeset: 1:a28a9d1a809c
151 changeset: 1:a28a9d1a809c
152 tag: tip
152 tag: tip
153 user: test
153 user: test
154 date: Thu Jan 01 00:00:00 1970 +0000
154 date: Thu Jan 01 00:00:00 1970 +0000
155 summary: add
155 summary: add
156
156
157
157
158 push
158 push
159
159
160 $ hg push
160 $ hg push
161 pushing to ssh://user@dummy/remote
161 pushing to ssh://user@dummy/remote
162 searching for changes
162 searching for changes
163 remote: adding changesets
163 remote: adding changesets
164 remote: adding manifests
164 remote: adding manifests
165 remote: adding file changes
165 remote: adding file changes
166 remote: added 1 changesets with 1 changes to 1 files
166 remote: added 1 changesets with 1 changes to 1 files
167 $ cd ../remote
167 $ cd ../remote
168
168
169 check remote tip
169 check remote tip
170
170
171 $ hg tip
171 $ hg tip
172 changeset: 1:a28a9d1a809c
172 changeset: 1:a28a9d1a809c
173 tag: tip
173 tag: tip
174 user: test
174 user: test
175 date: Thu Jan 01 00:00:00 1970 +0000
175 date: Thu Jan 01 00:00:00 1970 +0000
176 summary: add
176 summary: add
177
177
178 $ hg verify
178 $ hg verify
179 checking changesets
179 checking changesets
180 checking manifests
180 checking manifests
181 crosschecking files in changesets and manifests
181 crosschecking files in changesets and manifests
182 checking files
182 checking files
183 2 files, 2 changesets, 3 total revisions
183 2 files, 2 changesets, 3 total revisions
184 $ hg cat -r tip foo
184 $ hg cat -r tip foo
185 bleah
185 bleah
186 $ echo z > z
186 $ echo z > z
187 $ hg ci -A -m z z
187 $ hg ci -A -m z z
188 created new head
188 created new head
189
189
190 test pushkeys and bookmarks
190 test pushkeys and bookmarks
191
191
192 $ cd ../local
192 $ cd ../local
193 $ hg debugpushkey --config ui.ssh="python ../dummyssh" ssh://user@dummy/remote namespaces
193 $ hg debugpushkey --config ui.ssh="python ../dummyssh" ssh://user@dummy/remote namespaces
194 bookmarks
194 bookmarks
195 namespaces
195 namespaces
196 $ hg book foo -r 0
196 $ hg book foo -r 0
197 $ hg out -B
197 $ hg out -B
198 comparing with ssh://user@dummy/remote
198 comparing with ssh://user@dummy/remote
199 searching for changed bookmarks
199 searching for changed bookmarks
200 foo 1160648e36ce
200 foo 1160648e36ce
201 $ hg push -B foo
201 $ hg push -B foo
202 pushing to ssh://user@dummy/remote
202 pushing to ssh://user@dummy/remote
203 searching for changes
203 searching for changes
204 no changes found
204 no changes found
205 exporting bookmark foo
205 exporting bookmark foo
206 $ hg debugpushkey --config ui.ssh="python ../dummyssh" ssh://user@dummy/remote bookmarks
206 $ hg debugpushkey --config ui.ssh="python ../dummyssh" ssh://user@dummy/remote bookmarks
207 foo 1160648e36cec0054048a7edc4110c6f84fde594
207 foo 1160648e36cec0054048a7edc4110c6f84fde594
208 $ hg book -f foo
208 $ hg book -f foo
209 $ hg push --traceback
209 $ hg push --traceback
210 pushing to ssh://user@dummy/remote
210 pushing to ssh://user@dummy/remote
211 searching for changes
211 searching for changes
212 no changes found
212 no changes found
213 updating bookmark foo
213 updating bookmark foo
214 $ hg book -d foo
214 $ hg book -d foo
215 $ hg in -B
215 $ hg in -B
216 comparing with ssh://user@dummy/remote
216 comparing with ssh://user@dummy/remote
217 searching for changed bookmarks
217 searching for changed bookmarks
218 foo a28a9d1a809c
218 foo a28a9d1a809c
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
226 $ hg book -d foo
225 $ hg book -d foo
227 $ hg push -B foo
226 $ hg push -B foo
228 pushing to ssh://user@dummy/remote
227 pushing to ssh://user@dummy/remote
229 searching for changes
228 searching for changes
230 no changes found
229 no changes found
231 deleting remote bookmark foo
230 deleting remote bookmark foo
232
231
233 a bad, evil hook that prints to stdout
232 a bad, evil hook that prints to stdout
234
233
235 $ echo '[hooks]' >> ../remote/.hg/hgrc
234 $ echo '[hooks]' >> ../remote/.hg/hgrc
236 $ echo 'changegroup.stdout = python ../badhook' >> ../remote/.hg/hgrc
235 $ echo 'changegroup.stdout = python ../badhook' >> ../remote/.hg/hgrc
237 $ echo r > r
236 $ echo r > r
238 $ hg ci -A -m z r
237 $ hg ci -A -m z r
239
238
240 push should succeed even though it has an unexpected response
239 push should succeed even though it has an unexpected response
241
240
242 $ hg push
241 $ hg push
243 pushing to ssh://user@dummy/remote
242 pushing to ssh://user@dummy/remote
244 searching for changes
243 searching for changes
245 note: unsynced remote changes!
244 note: unsynced remote changes!
246 remote: adding changesets
245 remote: adding changesets
247 remote: adding manifests
246 remote: adding manifests
248 remote: adding file changes
247 remote: adding file changes
249 remote: added 1 changesets with 1 changes to 1 files
248 remote: added 1 changesets with 1 changes to 1 files
250 remote: KABOOM
249 remote: KABOOM
251 $ hg -R ../remote heads
250 $ hg -R ../remote heads
252 changeset: 3:1383141674ec
251 changeset: 3:1383141674ec
253 tag: tip
252 tag: tip
254 parent: 1:a28a9d1a809c
253 parent: 1:a28a9d1a809c
255 user: test
254 user: test
256 date: Thu Jan 01 00:00:00 1970 +0000
255 date: Thu Jan 01 00:00:00 1970 +0000
257 summary: z
256 summary: z
258
257
259 changeset: 2:6c0482d977a3
258 changeset: 2:6c0482d977a3
260 parent: 0:1160648e36ce
259 parent: 0:1160648e36ce
261 user: test
260 user: test
262 date: Thu Jan 01 00:00:00 1970 +0000
261 date: Thu Jan 01 00:00:00 1970 +0000
263 summary: z
262 summary: z
264
263
265
264
266 clone bookmarks
265 clone bookmarks
267
266
268 $ hg -R ../remote bookmark test
267 $ hg -R ../remote bookmark test
269 $ hg -R ../remote bookmarks
268 $ hg -R ../remote bookmarks
270 * test 2:6c0482d977a3
269 * test 2:6c0482d977a3
271 $ hg clone -e "python ../dummyssh" ssh://user@dummy/remote local-bookmarks
270 $ hg clone -e "python ../dummyssh" ssh://user@dummy/remote local-bookmarks
272 requesting all changes
271 requesting all changes
273 adding changesets
272 adding changesets
274 adding manifests
273 adding manifests
275 adding file changes
274 adding file changes
276 added 4 changesets with 5 changes to 4 files (+1 heads)
275 added 4 changesets with 5 changes to 4 files (+1 heads)
277 updating to branch default
276 updating to branch default
278 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
277 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
279 $ hg -R local-bookmarks bookmarks
278 $ hg -R local-bookmarks bookmarks
280 test 2:6c0482d977a3
279 test 2:6c0482d977a3
281
280
282 passwords in ssh urls are not supported
281 passwords in ssh urls are not supported
283 (we use a glob here because different Python versions give different
282 (we use a glob here because different Python versions give different
284 results here)
283 results here)
285
284
286 $ hg push ssh://user:erroneouspwd@dummy/remote
285 $ hg push ssh://user:erroneouspwd@dummy/remote
287 pushing to ssh://user:*@dummy/remote (glob)
286 pushing to ssh://user:*@dummy/remote (glob)
288 abort: password in URL not supported!
287 abort: password in URL not supported!
289 [255]
288 [255]
290
289
291 $ cd ..
290 $ cd ..
292 $ cat dummylog
291 $ cat dummylog
293 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
292 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
294 Got arguments 1:user@dummy 2:hg -R /$TESTTMP/nonexistent serve --stdio
293 Got arguments 1:user@dummy 2:hg -R /$TESTTMP/nonexistent serve --stdio
295 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
294 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
296 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
295 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
297 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
296 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
298 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
297 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
299 Got arguments 1:user@dummy 2:hg -R local serve --stdio
298 Got arguments 1:user@dummy 2:hg -R local serve --stdio
300 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
299 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
301 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
300 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
302 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
301 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
303 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
302 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
304 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
303 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
305 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
304 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
306 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
305 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
307 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
306 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
308 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
307 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
309 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
308 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
310 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
309 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
311 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
310 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
General Comments 0
You need to be logged in to leave comments. Login now