##// END OF EJS Templates
discovery: improve "note: unsynced remote changes!" warning...
Mads Kiilerich -
r20398:2bc520bd default
parent child Browse files
Show More
@@ -1,356 +1,358 b''
1 1 # discovery.py - protocol changeset discovery functions
2 2 #
3 3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import nullid, short
9 9 from i18n import _
10 10 import util, setdiscovery, treediscovery, phases, obsolete, bookmarks
11 11 import branchmap
12 12
13 13 def findcommonincoming(repo, remote, heads=None, force=False):
14 14 """Return a tuple (common, anyincoming, heads) used to identify the common
15 15 subset of nodes between repo and remote.
16 16
17 17 "common" is a list of (at least) the heads of the common subset.
18 18 "anyincoming" is testable as a boolean indicating if any nodes are missing
19 19 locally. If remote does not support getbundle, this actually is a list of
20 20 roots of the nodes that would be incoming, to be supplied to
21 21 changegroupsubset. No code except for pull should be relying on this fact
22 22 any longer.
23 23 "heads" is either the supplied heads, or else the remote's heads.
24 24
25 25 If you pass heads and they are all known locally, the response lists just
26 26 these heads in "common" and in "heads".
27 27
28 28 Please use findcommonoutgoing to compute the set of outgoing nodes to give
29 29 extensions a good hook into outgoing.
30 30 """
31 31
32 32 if not remote.capable('getbundle'):
33 33 return treediscovery.findcommonincoming(repo, remote, heads, force)
34 34
35 35 if heads:
36 36 allknown = True
37 37 knownnode = repo.changelog.hasnode # no nodemap until it is filtered
38 38 for h in heads:
39 39 if not knownnode(h):
40 40 allknown = False
41 41 break
42 42 if allknown:
43 43 return (heads, False, heads)
44 44
45 45 res = setdiscovery.findcommonheads(repo.ui, repo, remote,
46 46 abortwhenunrelated=not force)
47 47 common, anyinc, srvheads = res
48 48 return (list(common), anyinc, heads or list(srvheads))
49 49
50 50 class outgoing(object):
51 51 '''Represents the set of nodes present in a local repo but not in a
52 52 (possibly) remote one.
53 53
54 54 Members:
55 55
56 56 missing is a list of all nodes present in local but not in remote.
57 57 common is a list of all nodes shared between the two repos.
58 58 excluded is the list of missing changeset that shouldn't be sent remotely.
59 59 missingheads is the list of heads of missing.
60 60 commonheads is the list of heads of common.
61 61
62 62 The sets are computed on demand from the heads, unless provided upfront
63 63 by discovery.'''
64 64
65 65 def __init__(self, revlog, commonheads, missingheads):
66 66 self.commonheads = commonheads
67 67 self.missingheads = missingheads
68 68 self._revlog = revlog
69 69 self._common = None
70 70 self._missing = None
71 71 self.excluded = []
72 72
73 73 def _computecommonmissing(self):
74 74 sets = self._revlog.findcommonmissing(self.commonheads,
75 75 self.missingheads)
76 76 self._common, self._missing = sets
77 77
78 78 @util.propertycache
79 79 def common(self):
80 80 if self._common is None:
81 81 self._computecommonmissing()
82 82 return self._common
83 83
84 84 @util.propertycache
85 85 def missing(self):
86 86 if self._missing is None:
87 87 self._computecommonmissing()
88 88 return self._missing
89 89
90 90 def findcommonoutgoing(repo, other, onlyheads=None, force=False,
91 91 commoninc=None, portable=False):
92 92 '''Return an outgoing instance to identify the nodes present in repo but
93 93 not in other.
94 94
95 95 If onlyheads is given, only nodes ancestral to nodes in onlyheads
96 96 (inclusive) are included. If you already know the local repo's heads,
97 97 passing them in onlyheads is faster than letting them be recomputed here.
98 98
99 99 If commoninc is given, it must be the result of a prior call to
100 100 findcommonincoming(repo, other, force) to avoid recomputing it here.
101 101
102 102 If portable is given, compute more conservative common and missingheads,
103 103 to make bundles created from the instance more portable.'''
104 104 # declare an empty outgoing object to be filled later
105 105 og = outgoing(repo.changelog, None, None)
106 106
107 107 # get common set if not provided
108 108 if commoninc is None:
109 109 commoninc = findcommonincoming(repo, other, force=force)
110 110 og.commonheads, _any, _hds = commoninc
111 111
112 112 # compute outgoing
113 113 mayexclude = (repo._phasecache.phaseroots[phases.secret] or repo.obsstore)
114 114 if not mayexclude:
115 115 og.missingheads = onlyheads or repo.heads()
116 116 elif onlyheads is None:
117 117 # use visible heads as it should be cached
118 118 og.missingheads = repo.filtered("served").heads()
119 119 og.excluded = [ctx.node() for ctx in repo.set('secret() or extinct()')]
120 120 else:
121 121 # compute common, missing and exclude secret stuff
122 122 sets = repo.changelog.findcommonmissing(og.commonheads, onlyheads)
123 123 og._common, allmissing = sets
124 124 og._missing = missing = []
125 125 og.excluded = excluded = []
126 126 for node in allmissing:
127 127 ctx = repo[node]
128 128 if ctx.phase() >= phases.secret or ctx.extinct():
129 129 excluded.append(node)
130 130 else:
131 131 missing.append(node)
132 132 if len(missing) == len(allmissing):
133 133 missingheads = onlyheads
134 134 else: # update missing heads
135 135 missingheads = phases.newheads(repo, onlyheads, excluded)
136 136 og.missingheads = missingheads
137 137 if portable:
138 138 # recompute common and missingheads as if -r<rev> had been given for
139 139 # each head of missing, and --base <rev> for each head of the proper
140 140 # ancestors of missing
141 141 og._computecommonmissing()
142 142 cl = repo.changelog
143 143 missingrevs = set(cl.rev(n) for n in og._missing)
144 144 og._common = set(cl.ancestors(missingrevs)) - missingrevs
145 145 commonheads = set(og.commonheads)
146 146 og.missingheads = [h for h in og.missingheads if h not in commonheads]
147 147
148 148 return og
149 149
150 150 def _headssummary(repo, remote, outgoing):
151 151 """compute a summary of branch and heads status before and after push
152 152
153 153 return {'branch': ([remoteheads], [newheads], [unsyncedheads])} mapping
154 154
155 155 - branch: the branch name
156 156 - remoteheads: the list of remote heads known locally
157 157 None if the branch is new
158 158 - newheads: the new remote heads (known locally) with outgoing pushed
159 159 - unsyncedheads: the list of remote heads unknown locally.
160 160 """
161 161 cl = repo.changelog
162 162 headssum = {}
163 163 # A. Create set of branches involved in the push.
164 164 branches = set(repo[n].branch() for n in outgoing.missing)
165 165 remotemap = remote.branchmap()
166 166 newbranches = branches - set(remotemap)
167 167 branches.difference_update(newbranches)
168 168
169 169 # A. register remote heads
170 170 remotebranches = set()
171 171 for branch, heads in remote.branchmap().iteritems():
172 172 remotebranches.add(branch)
173 173 known = []
174 174 unsynced = []
175 175 knownnode = cl.hasnode # do not use nodemap until it is filtered
176 176 for h in heads:
177 177 if knownnode(h):
178 178 known.append(h)
179 179 else:
180 180 unsynced.append(h)
181 181 headssum[branch] = (known, list(known), unsynced)
182 182 # B. add new branch data
183 183 missingctx = list(repo[n] for n in outgoing.missing)
184 184 touchedbranches = set()
185 185 for ctx in missingctx:
186 186 branch = ctx.branch()
187 187 touchedbranches.add(branch)
188 188 if branch not in headssum:
189 189 headssum[branch] = (None, [], [])
190 190
191 191 # C drop data about untouched branches:
192 192 for branch in remotebranches - touchedbranches:
193 193 del headssum[branch]
194 194
195 195 # D. Update newmap with outgoing changes.
196 196 # This will possibly add new heads and remove existing ones.
197 197 newmap = branchmap.branchcache((branch, heads[1])
198 198 for branch, heads in headssum.iteritems()
199 199 if heads[0] is not None)
200 200 newmap.update(repo, (ctx.rev() for ctx in missingctx))
201 201 for branch, newheads in newmap.iteritems():
202 202 headssum[branch][1][:] = newheads
203 203 return headssum
204 204
205 205 def _oldheadssummary(repo, remoteheads, outgoing, inc=False):
206 206 """Compute branchmapsummary for repo without branchmap support"""
207 207
208 208 # 1-4b. old servers: Check for new topological heads.
209 209 # Construct {old,new}map with branch = None (topological branch).
210 210 # (code based on update)
211 211 knownnode = repo.changelog.hasnode # no nodemap until it is filtered
212 212 oldheads = set(h for h in remoteheads if knownnode(h))
213 213 # all nodes in outgoing.missing are children of either:
214 214 # - an element of oldheads
215 215 # - another element of outgoing.missing
216 216 # - nullrev
217 217 # This explains why the new head are very simple to compute.
218 218 r = repo.set('heads(%ln + %ln)', oldheads, outgoing.missing)
219 219 newheads = list(c.node() for c in r)
220 220 unsynced = inc and set([None]) or set()
221 221 return {None: (oldheads, newheads, unsynced)}
222 222
223 223 def checkheads(repo, remote, outgoing, remoteheads, newbranch=False, inc=False,
224 224 newbookmarks=[]):
225 225 """Check that a push won't add any outgoing head
226 226
227 227 raise Abort error and display ui message as needed.
228 228 """
229 229 # Check for each named branch if we're creating new remote heads.
230 230 # To be a remote head after push, node must be either:
231 231 # - unknown locally
232 232 # - a local outgoing head descended from update
233 233 # - a remote head that's known locally and not
234 234 # ancestral to an outgoing head
235 235 if remoteheads == [nullid]:
236 236 # remote is empty, nothing to check.
237 237 return
238 238
239 239 if remote.capable('branchmap'):
240 240 headssum = _headssummary(repo, remote, outgoing)
241 241 else:
242 242 headssum = _oldheadssummary(repo, remoteheads, outgoing, inc)
243 243 newbranches = [branch for branch, heads in headssum.iteritems()
244 244 if heads[0] is None]
245 245 # 1. Check for new branches on the remote.
246 246 if newbranches and not newbranch: # new branch requires --new-branch
247 247 branchnames = ', '.join(sorted(newbranches))
248 248 raise util.Abort(_("push creates new remote branches: %s!")
249 249 % branchnames,
250 250 hint=_("use 'hg push --new-branch' to create"
251 251 " new remote branches"))
252 252
253 253 # 2. Compute newly pushed bookmarks. We don't warn about bookmarked heads.
254 254 localbookmarks = repo._bookmarks
255 255 remotebookmarks = remote.listkeys('bookmarks')
256 256 bookmarkedheads = set()
257 257 for bm in localbookmarks:
258 258 rnode = remotebookmarks.get(bm)
259 259 if rnode and rnode in repo:
260 260 lctx, rctx = repo[bm], repo[rnode]
261 261 if bookmarks.validdest(repo, rctx, lctx):
262 262 bookmarkedheads.add(lctx.node())
263 263 else:
264 264 if bm in newbookmarks:
265 265 bookmarkedheads.add(repo[bm].node())
266 266
267 267 # 3. Check for new heads.
268 268 # If there are more heads after the push than before, a suitable
269 269 # error message, depending on unsynced status, is displayed.
270 270 error = None
271 unsynced = False
272 271 allmissing = set(outgoing.missing)
273 272 allfuturecommon = set(c.node() for c in repo.set('%ld', outgoing.common))
274 273 allfuturecommon.update(allmissing)
275 274 for branch, heads in sorted(headssum.iteritems()):
276 275 remoteheads, newheads, unsyncedheads = heads
277 276 candidate_newhs = set(newheads)
278 277 # add unsynced data
279 278 if remoteheads is None:
280 279 oldhs = set()
281 280 else:
282 281 oldhs = set(remoteheads)
283 282 oldhs.update(unsyncedheads)
284 283 candidate_newhs.update(unsyncedheads)
285 284 dhs = None # delta heads, the new heads on branch
286 285 discardedheads = set()
287 286 if repo.obsstore:
288 287 # remove future heads which are actually obsoleted by another
289 288 # pushed element:
290 289 #
291 290 # XXX as above, There are several cases this case does not handle
292 291 # XXX properly
293 292 #
294 293 # (1) if <nh> is public, it won't be affected by obsolete marker
295 294 # and a new is created
296 295 #
297 296 # (2) if the new heads have ancestors which are not obsolete and
298 297 # not ancestors of any other heads we will have a new head too.
299 298 #
300 299 # These two cases will be easy to handle for known changeset but
301 300 # much more tricky for unsynced changes.
302 301 newhs = set()
303 302 for nh in candidate_newhs:
304 303 if nh in repo and repo[nh].phase() <= phases.public:
305 304 newhs.add(nh)
306 305 else:
307 306 for suc in obsolete.allsuccessors(repo.obsstore, [nh]):
308 307 if suc != nh and suc in allfuturecommon:
309 308 discardedheads.add(nh)
310 309 break
311 310 else:
312 311 newhs.add(nh)
313 312 else:
314 313 newhs = candidate_newhs
315 if [h for h in unsyncedheads if h not in discardedheads]:
316 unsynced = True
314 unsynced = sorted(h for h in unsyncedheads if h not in discardedheads)
315 if unsynced:
316 heads = ' '.join(short(h) for h in unsynced)
317 if branch is None:
318 repo.ui.warn(_("remote has heads that are not known locally: "
319 "%s\n") % heads)
320 else:
321 repo.ui.warn(_("remote has heads on branch '%s' that are "
322 "not known locally: %s\n") % (branch, heads))
317 323 if remoteheads is None:
318 324 if len(newhs) > 1:
319 325 dhs = list(newhs)
320 326 if error is None:
321 327 error = (_("push creates new branch '%s' "
322 328 "with multiple heads") % (branch))
323 329 hint = _("merge or"
324 330 " see \"hg help push\" for details about"
325 331 " pushing new heads")
326 332 elif len(newhs) > len(oldhs):
327 333 # remove bookmarked or existing remote heads from the new heads list
328 334 dhs = sorted(newhs - bookmarkedheads - oldhs)
329 335 if dhs:
330 336 if error is None:
331 337 if branch not in ('default', None):
332 338 error = _("push creates new remote head %s "
333 339 "on branch '%s'!") % (short(dhs[0]), branch)
334 340 else:
335 341 error = _("push creates new remote head %s!"
336 342 ) % short(dhs[0])
337 343 if unsyncedheads:
338 344 hint = _("pull and merge or"
339 345 " see \"hg help push\" for details about"
340 346 " pushing new heads")
341 347 else:
342 348 hint = _("merge or"
343 349 " see \"hg help push\" for details about"
344 350 " pushing new heads")
345 351 if branch is None:
346 352 repo.ui.note(_("new remote heads:\n"))
347 353 else:
348 354 repo.ui.note(_("new remote heads on branch '%s':\n") % branch)
349 355 for h in dhs:
350 356 repo.ui.note((" %s\n") % short(h))
351 357 if error:
352 358 raise util.Abort(error, hint=hint)
353
354 # 6. Check for unsynced changes on involved branches.
355 if unsynced:
356 repo.ui.warn(_("note: unsynced remote changes!\n"))
@@ -1,384 +1,384 b''
1 1
2 2
3 3 This test tries to exercise the ssh functionality with a dummy script
4 4
5 5 creating 'remote' repo
6 6
7 7 $ hg init remote
8 8 $ cd remote
9 9 $ echo this > foo
10 10 $ echo this > fooO
11 11 $ hg ci -A -m "init" foo fooO
12 12 $ cat <<EOF > .hg/hgrc
13 13 > [server]
14 14 > uncompressed = True
15 15 >
16 16 > [hooks]
17 17 > changegroup = python "$TESTDIR/printenv.py" changegroup-in-remote 0 ../dummylog
18 18 > EOF
19 19 $ cd ..
20 20
21 21 repo not found error
22 22
23 23 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local
24 24 remote: abort: there is no Mercurial repository here (.hg not found)!
25 25 abort: no suitable response from remote hg!
26 26 [255]
27 27
28 28 non-existent absolute path
29 29
30 30 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy//`pwd`/nonexistent local
31 31 remote: abort: there is no Mercurial repository here (.hg not found)!
32 32 abort: no suitable response from remote hg!
33 33 [255]
34 34
35 35 clone remote via stream
36 36
37 37 $ hg clone -e "python \"$TESTDIR/dummyssh\"" --uncompressed ssh://user@dummy/remote local-stream
38 38 streaming all changes
39 39 4 files to transfer, 392 bytes of data
40 40 transferred 392 bytes in * seconds (*/sec) (glob)
41 41 updating to branch default
42 42 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
43 43 $ cd local-stream
44 44 $ hg verify
45 45 checking changesets
46 46 checking manifests
47 47 crosschecking files in changesets and manifests
48 48 checking files
49 49 2 files, 1 changesets, 2 total revisions
50 50 $ cd ..
51 51
52 52 clone remote via pull
53 53
54 54 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local
55 55 requesting all changes
56 56 adding changesets
57 57 adding manifests
58 58 adding file changes
59 59 added 1 changesets with 2 changes to 2 files
60 60 updating to branch default
61 61 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
62 62
63 63 verify
64 64
65 65 $ cd local
66 66 $ hg verify
67 67 checking changesets
68 68 checking manifests
69 69 crosschecking files in changesets and manifests
70 70 checking files
71 71 2 files, 1 changesets, 2 total revisions
72 72 $ echo '[hooks]' >> .hg/hgrc
73 73 $ echo "changegroup = python \"$TESTDIR/printenv.py\" changegroup-in-local 0 ../dummylog" >> .hg/hgrc
74 74
75 75 empty default pull
76 76
77 77 $ hg paths
78 78 default = ssh://user@dummy/remote
79 79 $ hg pull -e "python \"$TESTDIR/dummyssh\""
80 80 pulling from ssh://user@dummy/remote
81 81 searching for changes
82 82 no changes found
83 83
84 84 local change
85 85
86 86 $ echo bleah > foo
87 87 $ hg ci -m "add"
88 88
89 89 updating rc
90 90
91 91 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
92 92 $ echo "[ui]" >> .hg/hgrc
93 93 $ echo "ssh = python \"$TESTDIR/dummyssh\"" >> .hg/hgrc
94 94
95 95 find outgoing
96 96
97 97 $ hg out ssh://user@dummy/remote
98 98 comparing with ssh://user@dummy/remote
99 99 searching for changes
100 100 changeset: 1:a28a9d1a809c
101 101 tag: tip
102 102 user: test
103 103 date: Thu Jan 01 00:00:00 1970 +0000
104 104 summary: add
105 105
106 106
107 107 find incoming on the remote side
108 108
109 109 $ hg incoming -R ../remote -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/local
110 110 comparing with ssh://user@dummy/local
111 111 searching for changes
112 112 changeset: 1:a28a9d1a809c
113 113 tag: tip
114 114 user: test
115 115 date: Thu Jan 01 00:00:00 1970 +0000
116 116 summary: add
117 117
118 118
119 119 find incoming on the remote side (using absolute path)
120 120
121 121 $ hg incoming -R ../remote -e "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/`pwd`"
122 122 comparing with ssh://user@dummy/$TESTTMP/local
123 123 searching for changes
124 124 changeset: 1:a28a9d1a809c
125 125 tag: tip
126 126 user: test
127 127 date: Thu Jan 01 00:00:00 1970 +0000
128 128 summary: add
129 129
130 130
131 131 push
132 132
133 133 $ hg push
134 134 pushing to ssh://user@dummy/remote
135 135 searching for changes
136 136 remote: adding changesets
137 137 remote: adding manifests
138 138 remote: adding file changes
139 139 remote: added 1 changesets with 1 changes to 1 files
140 140 $ cd ../remote
141 141
142 142 check remote tip
143 143
144 144 $ hg tip
145 145 changeset: 1:a28a9d1a809c
146 146 tag: tip
147 147 user: test
148 148 date: Thu Jan 01 00:00:00 1970 +0000
149 149 summary: add
150 150
151 151 $ hg verify
152 152 checking changesets
153 153 checking manifests
154 154 crosschecking files in changesets and manifests
155 155 checking files
156 156 2 files, 2 changesets, 3 total revisions
157 157 $ hg cat -r tip foo
158 158 bleah
159 159 $ echo z > z
160 160 $ hg ci -A -m z z
161 161 created new head
162 162
163 163 test pushkeys and bookmarks
164 164
165 165 $ cd ../local
166 166 $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote namespaces
167 167 bookmarks
168 168 namespaces
169 169 phases
170 170 $ hg book foo -r 0
171 171 $ hg out -B
172 172 comparing with ssh://user@dummy/remote
173 173 searching for changed bookmarks
174 174 foo 1160648e36ce
175 175 $ hg push -B foo
176 176 pushing to ssh://user@dummy/remote
177 177 searching for changes
178 178 no changes found
179 179 exporting bookmark foo
180 180 [1]
181 181 $ hg debugpushkey --config ui.ssh="python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote bookmarks
182 182 foo 1160648e36cec0054048a7edc4110c6f84fde594
183 183 $ hg book -f foo
184 184 $ hg push --traceback
185 185 pushing to ssh://user@dummy/remote
186 186 searching for changes
187 187 no changes found
188 188 updating bookmark foo
189 189 [1]
190 190 $ hg book -d foo
191 191 $ hg in -B
192 192 comparing with ssh://user@dummy/remote
193 193 searching for changed bookmarks
194 194 foo a28a9d1a809c
195 195 $ hg book -f -r 0 foo
196 196 $ hg pull -B foo
197 197 pulling from ssh://user@dummy/remote
198 198 no changes found
199 199 updating bookmark foo
200 200 importing bookmark foo
201 201 $ hg book -d foo
202 202 $ hg push -B foo
203 203 pushing to ssh://user@dummy/remote
204 204 searching for changes
205 205 no changes found
206 206 deleting remote bookmark foo
207 207 [1]
208 208
209 209 a bad, evil hook that prints to stdout
210 210
211 211 $ cat <<EOF > $TESTTMP/badhook
212 212 > import sys
213 213 > sys.stdout.write("KABOOM\n")
214 214 > EOF
215 215
216 216 $ echo '[hooks]' >> ../remote/.hg/hgrc
217 217 $ echo "changegroup.stdout = python $TESTTMP/badhook" >> ../remote/.hg/hgrc
218 218 $ echo r > r
219 219 $ hg ci -A -m z r
220 220
221 221 push should succeed even though it has an unexpected response
222 222
223 223 $ hg push
224 224 pushing to ssh://user@dummy/remote
225 225 searching for changes
226 note: unsynced remote changes!
226 remote has heads on branch 'default' that are not known locally: 6c0482d977a3
227 227 remote: adding changesets
228 228 remote: adding manifests
229 229 remote: adding file changes
230 230 remote: added 1 changesets with 1 changes to 1 files
231 231 remote: KABOOM
232 232 $ hg -R ../remote heads
233 233 changeset: 3:1383141674ec
234 234 tag: tip
235 235 parent: 1:a28a9d1a809c
236 236 user: test
237 237 date: Thu Jan 01 00:00:00 1970 +0000
238 238 summary: z
239 239
240 240 changeset: 2:6c0482d977a3
241 241 parent: 0:1160648e36ce
242 242 user: test
243 243 date: Thu Jan 01 00:00:00 1970 +0000
244 244 summary: z
245 245
246 246
247 247 clone bookmarks
248 248
249 249 $ hg -R ../remote bookmark test
250 250 $ hg -R ../remote bookmarks
251 251 * test 2:6c0482d977a3
252 252 $ hg clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local-bookmarks
253 253 requesting all changes
254 254 adding changesets
255 255 adding manifests
256 256 adding file changes
257 257 added 4 changesets with 5 changes to 4 files (+1 heads)
258 258 updating to branch default
259 259 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
260 260 $ hg -R local-bookmarks bookmarks
261 261 test 2:6c0482d977a3
262 262
263 263 passwords in ssh urls are not supported
264 264 (we use a glob here because different Python versions give different
265 265 results here)
266 266
267 267 $ hg push ssh://user:erroneouspwd@dummy/remote
268 268 pushing to ssh://user:*@dummy/remote (glob)
269 269 abort: password in URL not supported!
270 270 [255]
271 271
272 272 $ cd ..
273 273
274 274 hide outer repo
275 275 $ hg init
276 276
277 277 Test remote paths with spaces (issue2983):
278 278
279 279 $ hg init --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
280 280 $ touch "$TESTTMP/a repo/test"
281 281 $ hg -R 'a repo' commit -A -m "test"
282 282 adding test
283 283 $ hg -R 'a repo' tag tag
284 284 $ hg id --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
285 285 73649e48688a
286 286
287 287 Test (non-)escaping of remote paths with spaces when cloning (issue3145):
288 288
289 289 $ hg clone --ssh "python \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
290 290 destination directory: a repo
291 291 abort: destination 'a repo' is not empty
292 292 [255]
293 293
294 294 Test hg-ssh using a helper script that will restore PYTHONPATH (which might
295 295 have been cleared by a hg.exe wrapper) and invoke hg-ssh with the right
296 296 parameters:
297 297
298 298 $ cat > ssh.sh << EOF
299 299 > userhost="\$1"
300 300 > SSH_ORIGINAL_COMMAND="\$2"
301 301 > export SSH_ORIGINAL_COMMAND
302 302 > PYTHONPATH="$PYTHONPATH"
303 303 > export PYTHONPATH
304 304 > python "$TESTDIR/../contrib/hg-ssh" "$TESTTMP/a repo"
305 305 > EOF
306 306
307 307 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a repo"
308 308 73649e48688a
309 309
310 310 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a'repo"
311 311 remote: Illegal repository "$TESTTMP/a'repo" (glob)
312 312 abort: no suitable response from remote hg!
313 313 [255]
314 314
315 315 $ hg id --ssh "sh ssh.sh" --remotecmd hacking "ssh://user@dummy/a'repo"
316 316 remote: Illegal command "hacking -R 'a'\''repo' serve --stdio"
317 317 abort: no suitable response from remote hg!
318 318 [255]
319 319
320 320 $ SSH_ORIGINAL_COMMAND="'hg' -R 'a'repo' serve --stdio" python "$TESTDIR/../contrib/hg-ssh"
321 321 Illegal command "'hg' -R 'a'repo' serve --stdio": No closing quotation
322 322 [255]
323 323
324 324 Test hg-ssh in read-only mode:
325 325
326 326 $ cat > ssh.sh << EOF
327 327 > userhost="\$1"
328 328 > SSH_ORIGINAL_COMMAND="\$2"
329 329 > export SSH_ORIGINAL_COMMAND
330 330 > PYTHONPATH="$PYTHONPATH"
331 331 > export PYTHONPATH
332 332 > python "$TESTDIR/../contrib/hg-ssh" --read-only "$TESTTMP/remote"
333 333 > EOF
334 334
335 335 $ hg clone --ssh "sh ssh.sh" "ssh://user@dummy/$TESTTMP/remote" read-only-local
336 336 requesting all changes
337 337 adding changesets
338 338 adding manifests
339 339 adding file changes
340 340 added 4 changesets with 5 changes to 4 files (+1 heads)
341 341 updating to branch default
342 342 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
343 343
344 344 $ cd read-only-local
345 345 $ echo "baz" > bar
346 346 $ hg ci -A -m "unpushable commit" bar
347 347 $ hg push --ssh "sh ../ssh.sh"
348 348 pushing to ssh://user@dummy/*/remote (glob)
349 349 searching for changes
350 350 remote: Permission denied
351 351 remote: abort: prechangegroup.hg-ssh hook failed
352 352 remote: Permission denied
353 353 remote: abort: prepushkey.hg-ssh hook failed
354 354 abort: unexpected response: empty string
355 355 [255]
356 356
357 357 $ cd ..
358 358
359 359 $ cat dummylog
360 360 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
361 361 Got arguments 1:user@dummy 2:hg -R /$TESTTMP/nonexistent serve --stdio
362 362 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
363 363 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
364 364 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
365 365 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
366 366 Got arguments 1:user@dummy 2:hg -R local serve --stdio
367 367 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
368 368 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
369 369 changegroup-in-remote hook: HG_NODE=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_SOURCE=serve HG_URL=remote:ssh:127.0.0.1
370 370 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
371 371 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
372 372 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
373 373 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
374 374 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
375 375 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
376 376 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
377 377 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
378 378 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
379 379 changegroup-in-remote hook: HG_NODE=1383141674ec756a6056f6a9097618482fe0f4a6 HG_SOURCE=serve HG_URL=remote:ssh:127.0.0.1
380 380 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
381 381 Got arguments 1:user@dummy 2:hg init 'a repo'
382 382 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
383 383 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
384 384 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
General Comments 0
You need to be logged in to leave comments. Login now