##// END OF EJS Templates
bookmarks: prevent divergent bookmark from being updated unexpectedly...
FUJIWARA Katsunori -
r24353:3f6bf9f2 default
parent child Browse files
Show More
@@ -1,463 +1,474
1 # Mercurial bookmark support code
1 # Mercurial bookmark support code
2 #
2 #
3 # Copyright 2008 David Soria Parra <dsp@php.net>
3 # Copyright 2008 David Soria Parra <dsp@php.net>
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 import os
8 import os
9 from mercurial.i18n import _
9 from mercurial.i18n import _
10 from mercurial.node import hex, bin
10 from mercurial.node import hex, bin
11 from mercurial import encoding, error, util, obsolete, lock as lockmod
11 from mercurial import encoding, error, util, obsolete, lock as lockmod
12 import errno
12 import errno
13
13
14 class bmstore(dict):
14 class bmstore(dict):
15 """Storage for bookmarks.
15 """Storage for bookmarks.
16
16
17 This object should do all bookmark reads and writes, so that it's
17 This object should do all bookmark reads and writes, so that it's
18 fairly simple to replace the storage underlying bookmarks without
18 fairly simple to replace the storage underlying bookmarks without
19 having to clone the logic surrounding bookmarks.
19 having to clone the logic surrounding bookmarks.
20
20
21 This particular bmstore implementation stores bookmarks as
21 This particular bmstore implementation stores bookmarks as
22 {hash}\s{name}\n (the same format as localtags) in
22 {hash}\s{name}\n (the same format as localtags) in
23 .hg/bookmarks. The mapping is stored as {name: nodeid}.
23 .hg/bookmarks. The mapping is stored as {name: nodeid}.
24
24
25 This class does NOT handle the "current" bookmark state at this
25 This class does NOT handle the "current" bookmark state at this
26 time.
26 time.
27 """
27 """
28
28
29 def __init__(self, repo):
29 def __init__(self, repo):
30 dict.__init__(self)
30 dict.__init__(self)
31 self._repo = repo
31 self._repo = repo
32 try:
32 try:
33 bkfile = self.getbkfile(repo)
33 bkfile = self.getbkfile(repo)
34 for line in bkfile:
34 for line in bkfile:
35 line = line.strip()
35 line = line.strip()
36 if not line:
36 if not line:
37 continue
37 continue
38 if ' ' not in line:
38 if ' ' not in line:
39 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n')
39 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n')
40 % line)
40 % line)
41 continue
41 continue
42 sha, refspec = line.split(' ', 1)
42 sha, refspec = line.split(' ', 1)
43 refspec = encoding.tolocal(refspec)
43 refspec = encoding.tolocal(refspec)
44 try:
44 try:
45 self[refspec] = repo.changelog.lookup(sha)
45 self[refspec] = repo.changelog.lookup(sha)
46 except LookupError:
46 except LookupError:
47 pass
47 pass
48 except IOError, inst:
48 except IOError, inst:
49 if inst.errno != errno.ENOENT:
49 if inst.errno != errno.ENOENT:
50 raise
50 raise
51
51
52 def getbkfile(self, repo):
52 def getbkfile(self, repo):
53 bkfile = None
53 bkfile = None
54 if 'HG_PENDING' in os.environ:
54 if 'HG_PENDING' in os.environ:
55 try:
55 try:
56 bkfile = repo.vfs('bookmarks.pending')
56 bkfile = repo.vfs('bookmarks.pending')
57 except IOError, inst:
57 except IOError, inst:
58 if inst.errno != errno.ENOENT:
58 if inst.errno != errno.ENOENT:
59 raise
59 raise
60 if bkfile is None:
60 if bkfile is None:
61 bkfile = repo.vfs('bookmarks')
61 bkfile = repo.vfs('bookmarks')
62 return bkfile
62 return bkfile
63
63
64 def recordchange(self, tr):
64 def recordchange(self, tr):
65 """record that bookmarks have been changed in a transaction
65 """record that bookmarks have been changed in a transaction
66
66
67 The transaction is then responsible for updating the file content."""
67 The transaction is then responsible for updating the file content."""
68 tr.addfilegenerator('bookmarks', ('bookmarks',), self._write,
68 tr.addfilegenerator('bookmarks', ('bookmarks',), self._write,
69 location='plain')
69 location='plain')
70 tr.hookargs['bookmark_moved'] = '1'
70 tr.hookargs['bookmark_moved'] = '1'
71
71
72 def write(self):
72 def write(self):
73 '''Write bookmarks
73 '''Write bookmarks
74
74
75 Write the given bookmark => hash dictionary to the .hg/bookmarks file
75 Write the given bookmark => hash dictionary to the .hg/bookmarks file
76 in a format equal to those of localtags.
76 in a format equal to those of localtags.
77
77
78 We also store a backup of the previous state in undo.bookmarks that
78 We also store a backup of the previous state in undo.bookmarks that
79 can be copied back on rollback.
79 can be copied back on rollback.
80 '''
80 '''
81 repo = self._repo
81 repo = self._repo
82 self._writerepo(repo)
82 self._writerepo(repo)
83
83
84 def _writerepo(self, repo):
84 def _writerepo(self, repo):
85 """Factored out for extensibility"""
85 """Factored out for extensibility"""
86 if repo._bookmarkcurrent not in self:
86 if repo._bookmarkcurrent not in self:
87 unsetcurrent(repo)
87 unsetcurrent(repo)
88
88
89 wlock = repo.wlock()
89 wlock = repo.wlock()
90 try:
90 try:
91
91
92 file = repo.vfs('bookmarks', 'w', atomictemp=True)
92 file = repo.vfs('bookmarks', 'w', atomictemp=True)
93 self._write(file)
93 self._write(file)
94 file.close()
94 file.close()
95
95
96 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
96 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
97 try:
97 try:
98 repo.svfs.utime('00changelog.i', None)
98 repo.svfs.utime('00changelog.i', None)
99 except OSError:
99 except OSError:
100 pass
100 pass
101
101
102 finally:
102 finally:
103 wlock.release()
103 wlock.release()
104
104
105 def _write(self, fp):
105 def _write(self, fp):
106 for name, node in self.iteritems():
106 for name, node in self.iteritems():
107 fp.write("%s %s\n" % (hex(node), encoding.fromlocal(name)))
107 fp.write("%s %s\n" % (hex(node), encoding.fromlocal(name)))
108
108
109 def readcurrent(repo):
109 def readcurrent(repo):
110 '''Get the current bookmark
110 '''Get the current bookmark
111
111
112 If we use gittish branches we have a current bookmark that
112 If we use gittish branches we have a current bookmark that
113 we are on. This function returns the name of the bookmark. It
113 we are on. This function returns the name of the bookmark. It
114 is stored in .hg/bookmarks.current
114 is stored in .hg/bookmarks.current
115 '''
115 '''
116 mark = None
116 mark = None
117 try:
117 try:
118 file = repo.vfs('bookmarks.current')
118 file = repo.vfs('bookmarks.current')
119 except IOError, inst:
119 except IOError, inst:
120 if inst.errno != errno.ENOENT:
120 if inst.errno != errno.ENOENT:
121 raise
121 raise
122 return None
122 return None
123 try:
123 try:
124 # No readline() in osutil.posixfile, reading everything is cheap
124 # No readline() in osutil.posixfile, reading everything is cheap
125 mark = encoding.tolocal((file.readlines() or [''])[0])
125 mark = encoding.tolocal((file.readlines() or [''])[0])
126 if mark == '' or mark not in repo._bookmarks:
126 if mark == '' or mark not in repo._bookmarks:
127 mark = None
127 mark = None
128 finally:
128 finally:
129 file.close()
129 file.close()
130 return mark
130 return mark
131
131
132 def setcurrent(repo, mark):
132 def setcurrent(repo, mark):
133 '''Set the name of the bookmark that we are currently on
133 '''Set the name of the bookmark that we are currently on
134
134
135 Set the name of the bookmark that we are on (hg update <bookmark>).
135 Set the name of the bookmark that we are on (hg update <bookmark>).
136 The name is recorded in .hg/bookmarks.current
136 The name is recorded in .hg/bookmarks.current
137 '''
137 '''
138 if mark not in repo._bookmarks:
138 if mark not in repo._bookmarks:
139 raise AssertionError('bookmark %s does not exist!' % mark)
139 raise AssertionError('bookmark %s does not exist!' % mark)
140
140
141 current = repo._bookmarkcurrent
141 current = repo._bookmarkcurrent
142 if current == mark:
142 if current == mark:
143 return
143 return
144
144
145 wlock = repo.wlock()
145 wlock = repo.wlock()
146 try:
146 try:
147 file = repo.vfs('bookmarks.current', 'w', atomictemp=True)
147 file = repo.vfs('bookmarks.current', 'w', atomictemp=True)
148 file.write(encoding.fromlocal(mark))
148 file.write(encoding.fromlocal(mark))
149 file.close()
149 file.close()
150 finally:
150 finally:
151 wlock.release()
151 wlock.release()
152 repo._bookmarkcurrent = mark
152 repo._bookmarkcurrent = mark
153
153
154 def unsetcurrent(repo):
154 def unsetcurrent(repo):
155 wlock = repo.wlock()
155 wlock = repo.wlock()
156 try:
156 try:
157 try:
157 try:
158 repo.vfs.unlink('bookmarks.current')
158 repo.vfs.unlink('bookmarks.current')
159 repo._bookmarkcurrent = None
159 repo._bookmarkcurrent = None
160 except OSError, inst:
160 except OSError, inst:
161 if inst.errno != errno.ENOENT:
161 if inst.errno != errno.ENOENT:
162 raise
162 raise
163 finally:
163 finally:
164 wlock.release()
164 wlock.release()
165
165
166 def iscurrent(repo, mark=None, parents=None):
166 def iscurrent(repo, mark=None, parents=None):
167 '''Tell whether the current bookmark is also active
167 '''Tell whether the current bookmark is also active
168
168
169 I.e., the bookmark listed in .hg/bookmarks.current also points to a
169 I.e., the bookmark listed in .hg/bookmarks.current also points to a
170 parent of the working directory.
170 parent of the working directory.
171 '''
171 '''
172 if not mark:
172 if not mark:
173 mark = repo._bookmarkcurrent
173 mark = repo._bookmarkcurrent
174 if not parents:
174 if not parents:
175 parents = [p.node() for p in repo[None].parents()]
175 parents = [p.node() for p in repo[None].parents()]
176 marks = repo._bookmarks
176 marks = repo._bookmarks
177 return (mark in marks and marks[mark] in parents)
177 return (mark in marks and marks[mark] in parents)
178
178
179 def updatecurrentbookmark(repo, oldnode, curbranch):
179 def updatecurrentbookmark(repo, oldnode, curbranch):
180 try:
180 try:
181 return update(repo, oldnode, repo.branchtip(curbranch))
181 return update(repo, oldnode, repo.branchtip(curbranch))
182 except error.RepoLookupError:
182 except error.RepoLookupError:
183 if curbranch == "default": # no default branch!
183 if curbranch == "default": # no default branch!
184 return update(repo, oldnode, repo.lookup("tip"))
184 return update(repo, oldnode, repo.lookup("tip"))
185 else:
185 else:
186 raise util.Abort(_("branch %s not found") % curbranch)
186 raise util.Abort(_("branch %s not found") % curbranch)
187
187
188 def deletedivergent(repo, deletefrom, bm):
188 def deletedivergent(repo, deletefrom, bm):
189 '''Delete divergent versions of bm on nodes in deletefrom.
189 '''Delete divergent versions of bm on nodes in deletefrom.
190
190
191 Return True if at least one bookmark was deleted, False otherwise.'''
191 Return True if at least one bookmark was deleted, False otherwise.'''
192 deleted = False
192 deleted = False
193 marks = repo._bookmarks
193 marks = repo._bookmarks
194 divergent = [b for b in marks if b.split('@', 1)[0] == bm.split('@', 1)[0]]
194 divergent = [b for b in marks if b.split('@', 1)[0] == bm.split('@', 1)[0]]
195 for mark in divergent:
195 for mark in divergent:
196 if mark == '@' or '@' not in mark:
196 if mark == '@' or '@' not in mark:
197 # can't be divergent by definition
197 # can't be divergent by definition
198 continue
198 continue
199 if mark and marks[mark] in deletefrom:
199 if mark and marks[mark] in deletefrom:
200 if mark != bm:
200 if mark != bm:
201 del marks[mark]
201 del marks[mark]
202 deleted = True
202 deleted = True
203 return deleted
203 return deleted
204
204
205 def calculateupdate(ui, repo, checkout):
205 def calculateupdate(ui, repo, checkout):
206 '''Return a tuple (targetrev, movemarkfrom) indicating the rev to
206 '''Return a tuple (targetrev, movemarkfrom) indicating the rev to
207 check out and where to move the active bookmark from, if needed.'''
207 check out and where to move the active bookmark from, if needed.'''
208 movemarkfrom = None
208 movemarkfrom = None
209 if checkout is None:
209 if checkout is None:
210 curmark = repo._bookmarkcurrent
210 curmark = repo._bookmarkcurrent
211 if iscurrent(repo):
211 if iscurrent(repo):
212 movemarkfrom = repo['.'].node()
212 movemarkfrom = repo['.'].node()
213 elif curmark:
213 elif curmark:
214 ui.status(_("updating to active bookmark %s\n") % curmark)
214 ui.status(_("updating to active bookmark %s\n") % curmark)
215 checkout = curmark
215 checkout = curmark
216 return (checkout, movemarkfrom)
216 return (checkout, movemarkfrom)
217
217
218 def update(repo, parents, node):
218 def update(repo, parents, node):
219 deletefrom = parents
219 deletefrom = parents
220 marks = repo._bookmarks
220 marks = repo._bookmarks
221 update = False
221 update = False
222 cur = repo._bookmarkcurrent
222 cur = repo._bookmarkcurrent
223 if not cur:
223 if not cur:
224 return False
224 return False
225
225
226 if marks[cur] in parents:
226 if marks[cur] in parents:
227 new = repo[node]
227 new = repo[node]
228 divs = [repo[b] for b in marks
228 divs = [repo[b] for b in marks
229 if b.split('@', 1)[0] == cur.split('@', 1)[0]]
229 if b.split('@', 1)[0] == cur.split('@', 1)[0]]
230 anc = repo.changelog.ancestors([new.rev()])
230 anc = repo.changelog.ancestors([new.rev()])
231 deletefrom = [b.node() for b in divs if b.rev() in anc or b == new]
231 deletefrom = [b.node() for b in divs if b.rev() in anc or b == new]
232 if validdest(repo, repo[marks[cur]], new):
232 if validdest(repo, repo[marks[cur]], new):
233 marks[cur] = new.node()
233 marks[cur] = new.node()
234 update = True
234 update = True
235
235
236 if deletedivergent(repo, deletefrom, cur):
236 if deletedivergent(repo, deletefrom, cur):
237 update = True
237 update = True
238
238
239 if update:
239 if update:
240 marks.write()
240 marks.write()
241 return update
241 return update
242
242
243 def listbookmarks(repo):
243 def listbookmarks(repo):
244 # We may try to list bookmarks on a repo type that does not
244 # We may try to list bookmarks on a repo type that does not
245 # support it (e.g., statichttprepository).
245 # support it (e.g., statichttprepository).
246 marks = getattr(repo, '_bookmarks', {})
246 marks = getattr(repo, '_bookmarks', {})
247
247
248 d = {}
248 d = {}
249 hasnode = repo.changelog.hasnode
249 hasnode = repo.changelog.hasnode
250 for k, v in marks.iteritems():
250 for k, v in marks.iteritems():
251 # don't expose local divergent bookmarks
251 # don't expose local divergent bookmarks
252 if hasnode(v) and ('@' not in k or k.endswith('@')):
252 if hasnode(v) and ('@' not in k or k.endswith('@')):
253 d[k] = hex(v)
253 d[k] = hex(v)
254 return d
254 return d
255
255
256 def pushbookmark(repo, key, old, new):
256 def pushbookmark(repo, key, old, new):
257 w = l = tr = None
257 w = l = tr = None
258 try:
258 try:
259 w = repo.wlock()
259 w = repo.wlock()
260 l = repo.lock()
260 l = repo.lock()
261 tr = repo.transaction('bookmarks')
261 tr = repo.transaction('bookmarks')
262 marks = repo._bookmarks
262 marks = repo._bookmarks
263 existing = hex(marks.get(key, ''))
263 existing = hex(marks.get(key, ''))
264 if existing != old and existing != new:
264 if existing != old and existing != new:
265 return False
265 return False
266 if new == '':
266 if new == '':
267 del marks[key]
267 del marks[key]
268 else:
268 else:
269 if new not in repo:
269 if new not in repo:
270 return False
270 return False
271 marks[key] = repo[new].node()
271 marks[key] = repo[new].node()
272 marks.recordchange(tr)
272 marks.recordchange(tr)
273 tr.close()
273 tr.close()
274 return True
274 return True
275 finally:
275 finally:
276 lockmod.release(tr, l, w)
276 lockmod.release(tr, l, w)
277
277
278 def compare(repo, srcmarks, dstmarks,
278 def compare(repo, srcmarks, dstmarks,
279 srchex=None, dsthex=None, targets=None):
279 srchex=None, dsthex=None, targets=None):
280 '''Compare bookmarks between srcmarks and dstmarks
280 '''Compare bookmarks between srcmarks and dstmarks
281
281
282 This returns tuple "(addsrc, adddst, advsrc, advdst, diverge,
282 This returns tuple "(addsrc, adddst, advsrc, advdst, diverge,
283 differ, invalid)", each are list of bookmarks below:
283 differ, invalid)", each are list of bookmarks below:
284
284
285 :addsrc: added on src side (removed on dst side, perhaps)
285 :addsrc: added on src side (removed on dst side, perhaps)
286 :adddst: added on dst side (removed on src side, perhaps)
286 :adddst: added on dst side (removed on src side, perhaps)
287 :advsrc: advanced on src side
287 :advsrc: advanced on src side
288 :advdst: advanced on dst side
288 :advdst: advanced on dst side
289 :diverge: diverge
289 :diverge: diverge
290 :differ: changed, but changeset referred on src is unknown on dst
290 :differ: changed, but changeset referred on src is unknown on dst
291 :invalid: unknown on both side
291 :invalid: unknown on both side
292 :same: same on both side
292 :same: same on both side
293
293
294 Each elements of lists in result tuple is tuple "(bookmark name,
294 Each elements of lists in result tuple is tuple "(bookmark name,
295 changeset ID on source side, changeset ID on destination
295 changeset ID on source side, changeset ID on destination
296 side)". Each changeset IDs are 40 hexadecimal digit string or
296 side)". Each changeset IDs are 40 hexadecimal digit string or
297 None.
297 None.
298
298
299 Changeset IDs of tuples in "addsrc", "adddst", "differ" or
299 Changeset IDs of tuples in "addsrc", "adddst", "differ" or
300 "invalid" list may be unknown for repo.
300 "invalid" list may be unknown for repo.
301
301
302 This function expects that "srcmarks" and "dstmarks" return
302 This function expects that "srcmarks" and "dstmarks" return
303 changeset ID in 40 hexadecimal digit string for specified
303 changeset ID in 40 hexadecimal digit string for specified
304 bookmark. If not so (e.g. bmstore "repo._bookmarks" returning
304 bookmark. If not so (e.g. bmstore "repo._bookmarks" returning
305 binary value), "srchex" or "dsthex" should be specified to convert
305 binary value), "srchex" or "dsthex" should be specified to convert
306 into such form.
306 into such form.
307
307
308 If "targets" is specified, only bookmarks listed in it are
308 If "targets" is specified, only bookmarks listed in it are
309 examined.
309 examined.
310 '''
310 '''
311 if not srchex:
311 if not srchex:
312 srchex = lambda x: x
312 srchex = lambda x: x
313 if not dsthex:
313 if not dsthex:
314 dsthex = lambda x: x
314 dsthex = lambda x: x
315
315
316 if targets:
316 if targets:
317 bset = set(targets)
317 bset = set(targets)
318 else:
318 else:
319 srcmarkset = set(srcmarks)
319 srcmarkset = set(srcmarks)
320 dstmarkset = set(dstmarks)
320 dstmarkset = set(dstmarks)
321 bset = srcmarkset | dstmarkset
321 bset = srcmarkset | dstmarkset
322
322
323 results = ([], [], [], [], [], [], [], [])
323 results = ([], [], [], [], [], [], [], [])
324 addsrc = results[0].append
324 addsrc = results[0].append
325 adddst = results[1].append
325 adddst = results[1].append
326 advsrc = results[2].append
326 advsrc = results[2].append
327 advdst = results[3].append
327 advdst = results[3].append
328 diverge = results[4].append
328 diverge = results[4].append
329 differ = results[5].append
329 differ = results[5].append
330 invalid = results[6].append
330 invalid = results[6].append
331 same = results[7].append
331 same = results[7].append
332
332
333 for b in sorted(bset):
333 for b in sorted(bset):
334 if b not in srcmarks:
334 if b not in srcmarks:
335 if b in dstmarks:
335 if b in dstmarks:
336 adddst((b, None, dsthex(dstmarks[b])))
336 adddst((b, None, dsthex(dstmarks[b])))
337 else:
337 else:
338 invalid((b, None, None))
338 invalid((b, None, None))
339 elif b not in dstmarks:
339 elif b not in dstmarks:
340 addsrc((b, srchex(srcmarks[b]), None))
340 addsrc((b, srchex(srcmarks[b]), None))
341 else:
341 else:
342 scid = srchex(srcmarks[b])
342 scid = srchex(srcmarks[b])
343 dcid = dsthex(dstmarks[b])
343 dcid = dsthex(dstmarks[b])
344 if scid == dcid:
344 if scid == dcid:
345 same((b, scid, dcid))
345 same((b, scid, dcid))
346 elif scid in repo and dcid in repo:
346 elif scid in repo and dcid in repo:
347 sctx = repo[scid]
347 sctx = repo[scid]
348 dctx = repo[dcid]
348 dctx = repo[dcid]
349 if sctx.rev() < dctx.rev():
349 if sctx.rev() < dctx.rev():
350 if validdest(repo, sctx, dctx):
350 if validdest(repo, sctx, dctx):
351 advdst((b, scid, dcid))
351 advdst((b, scid, dcid))
352 else:
352 else:
353 diverge((b, scid, dcid))
353 diverge((b, scid, dcid))
354 else:
354 else:
355 if validdest(repo, dctx, sctx):
355 if validdest(repo, dctx, sctx):
356 advsrc((b, scid, dcid))
356 advsrc((b, scid, dcid))
357 else:
357 else:
358 diverge((b, scid, dcid))
358 diverge((b, scid, dcid))
359 else:
359 else:
360 # it is too expensive to examine in detail, in this case
360 # it is too expensive to examine in detail, in this case
361 differ((b, scid, dcid))
361 differ((b, scid, dcid))
362
362
363 return results
363 return results
364
364
365 def _diverge(ui, b, path, localmarks):
365 def _diverge(ui, b, path, localmarks):
366 '''Return appropriate diverged bookmark for specified ``path``
367
368 This returns None, if it is failed to assign any divergent
369 bookmark name.
370 '''
366 if b == '@':
371 if b == '@':
367 b = ''
372 b = ''
368 # find a unique @ suffix
373 # find a unique @ suffix
369 for x in range(1, 100):
374 for x in range(1, 100):
370 n = '%s@%d' % (b, x)
375 n = '%s@%d' % (b, x)
371 if n not in localmarks:
376 if n not in localmarks:
372 break
377 break
378 else:
379 n = None
373 # try to use an @pathalias suffix
380 # try to use an @pathalias suffix
374 # if an @pathalias already exists, we overwrite (update) it
381 # if an @pathalias already exists, we overwrite (update) it
375 if path.startswith("file:"):
382 if path.startswith("file:"):
376 path = util.url(path).path
383 path = util.url(path).path
377 for p, u in ui.configitems("paths"):
384 for p, u in ui.configitems("paths"):
378 if u.startswith("file:"):
385 if u.startswith("file:"):
379 u = util.url(u).path
386 u = util.url(u).path
380 if path == u:
387 if path == u:
381 n = '%s@%s' % (b, p)
388 n = '%s@%s' % (b, p)
382 return n
389 return n
383
390
384 def updatefromremote(ui, repo, remotemarks, path, trfunc, explicit=()):
391 def updatefromremote(ui, repo, remotemarks, path, trfunc, explicit=()):
385 ui.debug("checking for updated bookmarks\n")
392 ui.debug("checking for updated bookmarks\n")
386 localmarks = repo._bookmarks
393 localmarks = repo._bookmarks
387 (addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same
394 (addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same
388 ) = compare(repo, remotemarks, localmarks, dsthex=hex)
395 ) = compare(repo, remotemarks, localmarks, dsthex=hex)
389
396
390 status = ui.status
397 status = ui.status
391 warn = ui.warn
398 warn = ui.warn
392 if ui.configbool('ui', 'quietbookmarkmove', False):
399 if ui.configbool('ui', 'quietbookmarkmove', False):
393 status = warn = ui.debug
400 status = warn = ui.debug
394
401
395 explicit = set(explicit)
402 explicit = set(explicit)
396 changed = []
403 changed = []
397 for b, scid, dcid in addsrc:
404 for b, scid, dcid in addsrc:
398 if scid in repo: # add remote bookmarks for changes we already have
405 if scid in repo: # add remote bookmarks for changes we already have
399 changed.append((b, bin(scid), status,
406 changed.append((b, bin(scid), status,
400 _("adding remote bookmark %s\n") % (b)))
407 _("adding remote bookmark %s\n") % (b)))
401 for b, scid, dcid in advsrc:
408 for b, scid, dcid in advsrc:
402 changed.append((b, bin(scid), status,
409 changed.append((b, bin(scid), status,
403 _("updating bookmark %s\n") % (b)))
410 _("updating bookmark %s\n") % (b)))
404 # remove normal movement from explicit set
411 # remove normal movement from explicit set
405 explicit.difference_update(d[0] for d in changed)
412 explicit.difference_update(d[0] for d in changed)
406
413
407 for b, scid, dcid in diverge:
414 for b, scid, dcid in diverge:
408 if b in explicit:
415 if b in explicit:
409 explicit.discard(b)
416 explicit.discard(b)
410 changed.append((b, bin(scid), status,
417 changed.append((b, bin(scid), status,
411 _("importing bookmark %s\n") % (b)))
418 _("importing bookmark %s\n") % (b)))
412 else:
419 else:
413 db = _diverge(ui, b, path, localmarks)
420 db = _diverge(ui, b, path, localmarks)
414 changed.append((db, bin(scid), warn,
421 if db:
415 _("divergent bookmark %s stored as %s\n")
422 changed.append((db, bin(scid), warn,
416 % (b, db)))
423 _("divergent bookmark %s stored as %s\n") %
424 (b, db)))
425 else:
426 warn(_("warning: failed to assign numbered name "
427 "to divergent bookmark %s\n") % (b))
417 for b, scid, dcid in adddst + advdst:
428 for b, scid, dcid in adddst + advdst:
418 if b in explicit:
429 if b in explicit:
419 explicit.discard(b)
430 explicit.discard(b)
420 changed.append((b, bin(scid), status,
431 changed.append((b, bin(scid), status,
421 _("importing bookmark %s\n") % (b)))
432 _("importing bookmark %s\n") % (b)))
422
433
423 if changed:
434 if changed:
424 tr = trfunc()
435 tr = trfunc()
425 for b, node, writer, msg in sorted(changed):
436 for b, node, writer, msg in sorted(changed):
426 localmarks[b] = node
437 localmarks[b] = node
427 writer(msg)
438 writer(msg)
428 localmarks.recordchange(tr)
439 localmarks.recordchange(tr)
429
440
430 def diff(ui, dst, src):
441 def diff(ui, dst, src):
431 ui.status(_("searching for changed bookmarks\n"))
442 ui.status(_("searching for changed bookmarks\n"))
432
443
433 smarks = src.listkeys('bookmarks')
444 smarks = src.listkeys('bookmarks')
434 dmarks = dst.listkeys('bookmarks')
445 dmarks = dst.listkeys('bookmarks')
435
446
436 diff = sorted(set(smarks) - set(dmarks))
447 diff = sorted(set(smarks) - set(dmarks))
437 for k in diff:
448 for k in diff:
438 if ui.debugflag:
449 if ui.debugflag:
439 mark = smarks[k]
450 mark = smarks[k]
440 else:
451 else:
441 mark = smarks[k][:12]
452 mark = smarks[k][:12]
442 ui.write(" %-25s %s\n" % (k, mark))
453 ui.write(" %-25s %s\n" % (k, mark))
443
454
444 if len(diff) <= 0:
455 if len(diff) <= 0:
445 ui.status(_("no changed bookmarks found\n"))
456 ui.status(_("no changed bookmarks found\n"))
446 return 1
457 return 1
447 return 0
458 return 0
448
459
449 def validdest(repo, old, new):
460 def validdest(repo, old, new):
450 """Is the new bookmark destination a valid update from the old one"""
461 """Is the new bookmark destination a valid update from the old one"""
451 repo = repo.unfiltered()
462 repo = repo.unfiltered()
452 if old == new:
463 if old == new:
453 # Old == new -> nothing to update.
464 # Old == new -> nothing to update.
454 return False
465 return False
455 elif not old:
466 elif not old:
456 # old is nullrev, anything is valid.
467 # old is nullrev, anything is valid.
457 # (new != nullrev has been excluded by the previous check)
468 # (new != nullrev has been excluded by the previous check)
458 return True
469 return True
459 elif repo.obsstore:
470 elif repo.obsstore:
460 return new.node() in obsolete.foreground(repo, [old.node()])
471 return new.node() in obsolete.foreground(repo, [old.node()])
461 else:
472 else:
462 # still an independent clause as it is lazier (and therefore faster)
473 # still an independent clause as it is lazier (and therefore faster)
463 return old.descendant(new)
474 return old.descendant(new)
@@ -1,562 +1,583
1 #require serve
1 #require serve
2
2
3 $ cat << EOF >> $HGRCPATH
3 $ cat << EOF >> $HGRCPATH
4 > [ui]
4 > [ui]
5 > logtemplate={rev}:{node|short} {desc|firstline}
5 > logtemplate={rev}:{node|short} {desc|firstline}
6 > [phases]
6 > [phases]
7 > publish=False
7 > publish=False
8 > [experimental]
8 > [experimental]
9 > evolution=createmarkers,exchange
9 > evolution=createmarkers,exchange
10 > EOF
10 > EOF
11
11
12 initialize
12 initialize
13
13
14 $ hg init a
14 $ hg init a
15 $ cd a
15 $ cd a
16 $ echo 'test' > test
16 $ echo 'test' > test
17 $ hg commit -Am'test'
17 $ hg commit -Am'test'
18 adding test
18 adding test
19
19
20 set bookmarks
20 set bookmarks
21
21
22 $ hg bookmark X
22 $ hg bookmark X
23 $ hg bookmark Y
23 $ hg bookmark Y
24 $ hg bookmark Z
24 $ hg bookmark Z
25
25
26 import bookmark by name
26 import bookmark by name
27
27
28 $ hg init ../b
28 $ hg init ../b
29 $ cd ../b
29 $ cd ../b
30 $ hg book Y
30 $ hg book Y
31 $ hg book
31 $ hg book
32 * Y -1:000000000000
32 * Y -1:000000000000
33 $ hg pull ../a
33 $ hg pull ../a
34 pulling from ../a
34 pulling from ../a
35 requesting all changes
35 requesting all changes
36 adding changesets
36 adding changesets
37 adding manifests
37 adding manifests
38 adding file changes
38 adding file changes
39 added 1 changesets with 1 changes to 1 files
39 added 1 changesets with 1 changes to 1 files
40 adding remote bookmark X
40 adding remote bookmark X
41 updating bookmark Y
41 updating bookmark Y
42 adding remote bookmark Z
42 adding remote bookmark Z
43 (run 'hg update' to get a working copy)
43 (run 'hg update' to get a working copy)
44 $ hg bookmarks
44 $ hg bookmarks
45 X 0:4e3505fd9583
45 X 0:4e3505fd9583
46 * Y 0:4e3505fd9583
46 * Y 0:4e3505fd9583
47 Z 0:4e3505fd9583
47 Z 0:4e3505fd9583
48 $ hg debugpushkey ../a namespaces
48 $ hg debugpushkey ../a namespaces
49 bookmarks
49 bookmarks
50 namespaces
50 namespaces
51 obsolete
51 obsolete
52 phases
52 phases
53 $ hg debugpushkey ../a bookmarks
53 $ hg debugpushkey ../a bookmarks
54 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
54 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
55 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
55 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
56 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
56 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
57
57
58 delete the bookmark to re-pull it
58 delete the bookmark to re-pull it
59
59
60 $ hg book -d X
60 $ hg book -d X
61 $ hg pull -B X ../a
61 $ hg pull -B X ../a
62 pulling from ../a
62 pulling from ../a
63 no changes found
63 no changes found
64 adding remote bookmark X
64 adding remote bookmark X
65
65
66 finally no-op pull
66 finally no-op pull
67
67
68 $ hg pull -B X ../a
68 $ hg pull -B X ../a
69 pulling from ../a
69 pulling from ../a
70 no changes found
70 no changes found
71 $ hg bookmark
71 $ hg bookmark
72 X 0:4e3505fd9583
72 X 0:4e3505fd9583
73 * Y 0:4e3505fd9583
73 * Y 0:4e3505fd9583
74 Z 0:4e3505fd9583
74 Z 0:4e3505fd9583
75
75
76 export bookmark by name
76 export bookmark by name
77
77
78 $ hg bookmark W
78 $ hg bookmark W
79 $ hg bookmark foo
79 $ hg bookmark foo
80 $ hg bookmark foobar
80 $ hg bookmark foobar
81 $ hg push -B W ../a
81 $ hg push -B W ../a
82 pushing to ../a
82 pushing to ../a
83 searching for changes
83 searching for changes
84 no changes found
84 no changes found
85 exporting bookmark W
85 exporting bookmark W
86 [1]
86 [1]
87 $ hg -R ../a bookmarks
87 $ hg -R ../a bookmarks
88 W -1:000000000000
88 W -1:000000000000
89 X 0:4e3505fd9583
89 X 0:4e3505fd9583
90 Y 0:4e3505fd9583
90 Y 0:4e3505fd9583
91 * Z 0:4e3505fd9583
91 * Z 0:4e3505fd9583
92
92
93 delete a remote bookmark
93 delete a remote bookmark
94
94
95 $ hg book -d W
95 $ hg book -d W
96 $ hg push -B W ../a
96 $ hg push -B W ../a
97 pushing to ../a
97 pushing to ../a
98 searching for changes
98 searching for changes
99 no changes found
99 no changes found
100 deleting remote bookmark W
100 deleting remote bookmark W
101 [1]
101 [1]
102
102
103 push/pull name that doesn't exist
103 push/pull name that doesn't exist
104
104
105 $ hg push -B badname ../a
105 $ hg push -B badname ../a
106 pushing to ../a
106 pushing to ../a
107 searching for changes
107 searching for changes
108 bookmark badname does not exist on the local or remote repository!
108 bookmark badname does not exist on the local or remote repository!
109 no changes found
109 no changes found
110 [2]
110 [2]
111 $ hg pull -B anotherbadname ../a
111 $ hg pull -B anotherbadname ../a
112 pulling from ../a
112 pulling from ../a
113 abort: remote bookmark anotherbadname not found!
113 abort: remote bookmark anotherbadname not found!
114 [255]
114 [255]
115
115
116 divergent bookmarks
116 divergent bookmarks
117
117
118 $ cd ../a
118 $ cd ../a
119 $ echo c1 > f1
119 $ echo c1 > f1
120 $ hg ci -Am1
120 $ hg ci -Am1
121 adding f1
121 adding f1
122 $ hg book -f @
122 $ hg book -f @
123 $ hg book -f X
123 $ hg book -f X
124 $ hg book
124 $ hg book
125 @ 1:0d2164f0ce0d
125 @ 1:0d2164f0ce0d
126 * X 1:0d2164f0ce0d
126 * X 1:0d2164f0ce0d
127 Y 0:4e3505fd9583
127 Y 0:4e3505fd9583
128 Z 1:0d2164f0ce0d
128 Z 1:0d2164f0ce0d
129
129
130 $ cd ../b
130 $ cd ../b
131 $ hg up
131 $ hg up
132 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
132 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
133 updating bookmark foobar
133 updating bookmark foobar
134 $ echo c2 > f2
134 $ echo c2 > f2
135 $ hg ci -Am2
135 $ hg ci -Am2
136 adding f2
136 adding f2
137 $ hg book -if @
137 $ hg book -if @
138 $ hg book -if X
138 $ hg book -if X
139 $ hg book
139 $ hg book
140 @ 1:9b140be10808
140 @ 1:9b140be10808
141 X 1:9b140be10808
141 X 1:9b140be10808
142 Y 0:4e3505fd9583
142 Y 0:4e3505fd9583
143 Z 0:4e3505fd9583
143 Z 0:4e3505fd9583
144 foo -1:000000000000
144 foo -1:000000000000
145 * foobar 1:9b140be10808
145 * foobar 1:9b140be10808
146
146
147 $ hg pull --config paths.foo=../a foo
147 $ hg pull --config paths.foo=../a foo
148 pulling from $TESTTMP/a (glob)
148 pulling from $TESTTMP/a (glob)
149 searching for changes
149 searching for changes
150 adding changesets
150 adding changesets
151 adding manifests
151 adding manifests
152 adding file changes
152 adding file changes
153 added 1 changesets with 1 changes to 1 files (+1 heads)
153 added 1 changesets with 1 changes to 1 files (+1 heads)
154 divergent bookmark @ stored as @foo
154 divergent bookmark @ stored as @foo
155 divergent bookmark X stored as X@foo
155 divergent bookmark X stored as X@foo
156 updating bookmark Z
156 updating bookmark Z
157 (run 'hg heads' to see heads, 'hg merge' to merge)
157 (run 'hg heads' to see heads, 'hg merge' to merge)
158 $ hg book
158 $ hg book
159 @ 1:9b140be10808
159 @ 1:9b140be10808
160 @foo 2:0d2164f0ce0d
160 @foo 2:0d2164f0ce0d
161 X 1:9b140be10808
161 X 1:9b140be10808
162 X@foo 2:0d2164f0ce0d
162 X@foo 2:0d2164f0ce0d
163 Y 0:4e3505fd9583
163 Y 0:4e3505fd9583
164 Z 2:0d2164f0ce0d
164 Z 2:0d2164f0ce0d
165 foo -1:000000000000
165 foo -1:000000000000
166 * foobar 1:9b140be10808
166 * foobar 1:9b140be10808
167
168 (test that too many divergence of bookmark)
169
170 $ cat > $TESTTMP/seq.py <<EOF
171 > import sys
172 > for i in xrange(*[int(a) for a in sys.argv[1:]]):
173 > print i
174 > EOF
175 $ python $TESTTMP/seq.py 1 100 | while read i; do hg bookmarks -r 000000000000 "X@${i}"; done
176 $ hg pull ../a
177 pulling from ../a
178 searching for changes
179 no changes found
180 warning: failed to assign numbered name to divergent bookmark X
181 divergent bookmark @ stored as @1
182 $ hg bookmarks | grep '^ X' | grep -v ':000000000000'
183 X 1:9b140be10808
184 X@foo 2:0d2164f0ce0d
185 $ python $TESTTMP/seq.py 1 100 | while read i; do hg bookmarks -d "X@${i}"; done
186 $ hg bookmarks -d "@1"
187
167 $ hg push -f ../a
188 $ hg push -f ../a
168 pushing to ../a
189 pushing to ../a
169 searching for changes
190 searching for changes
170 adding changesets
191 adding changesets
171 adding manifests
192 adding manifests
172 adding file changes
193 adding file changes
173 added 1 changesets with 1 changes to 1 files (+1 heads)
194 added 1 changesets with 1 changes to 1 files (+1 heads)
174 $ hg -R ../a book
195 $ hg -R ../a book
175 @ 1:0d2164f0ce0d
196 @ 1:0d2164f0ce0d
176 * X 1:0d2164f0ce0d
197 * X 1:0d2164f0ce0d
177 Y 0:4e3505fd9583
198 Y 0:4e3505fd9583
178 Z 1:0d2164f0ce0d
199 Z 1:0d2164f0ce0d
179
200
180 explicit pull should overwrite the local version (issue4439)
201 explicit pull should overwrite the local version (issue4439)
181
202
182 $ hg pull --config paths.foo=../a foo -B X
203 $ hg pull --config paths.foo=../a foo -B X
183 pulling from $TESTTMP/a (glob)
204 pulling from $TESTTMP/a (glob)
184 no changes found
205 no changes found
185 divergent bookmark @ stored as @foo
206 divergent bookmark @ stored as @foo
186 importing bookmark X
207 importing bookmark X
187
208
188 reinstall state for further testing:
209 reinstall state for further testing:
189
210
190 $ hg book -fr 9b140be10808 X
211 $ hg book -fr 9b140be10808 X
191
212
192 revsets should not ignore divergent bookmarks
213 revsets should not ignore divergent bookmarks
193
214
194 $ hg bookmark -fr 1 Z
215 $ hg bookmark -fr 1 Z
195 $ hg log -r 'bookmark()' --template '{rev}:{node|short} {bookmarks}\n'
216 $ hg log -r 'bookmark()' --template '{rev}:{node|short} {bookmarks}\n'
196 0:4e3505fd9583 Y
217 0:4e3505fd9583 Y
197 1:9b140be10808 @ X Z foobar
218 1:9b140be10808 @ X Z foobar
198 2:0d2164f0ce0d @foo X@foo
219 2:0d2164f0ce0d @foo X@foo
199 $ hg log -r 'bookmark("X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
220 $ hg log -r 'bookmark("X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
200 2:0d2164f0ce0d @foo X@foo
221 2:0d2164f0ce0d @foo X@foo
201 $ hg log -r 'bookmark("re:X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
222 $ hg log -r 'bookmark("re:X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
202 2:0d2164f0ce0d @foo X@foo
223 2:0d2164f0ce0d @foo X@foo
203
224
204 update a remote bookmark from a non-head to a head
225 update a remote bookmark from a non-head to a head
205
226
206 $ hg up -q Y
227 $ hg up -q Y
207 $ echo c3 > f2
228 $ echo c3 > f2
208 $ hg ci -Am3
229 $ hg ci -Am3
209 adding f2
230 adding f2
210 created new head
231 created new head
211 $ hg push ../a
232 $ hg push ../a
212 pushing to ../a
233 pushing to ../a
213 searching for changes
234 searching for changes
214 adding changesets
235 adding changesets
215 adding manifests
236 adding manifests
216 adding file changes
237 adding file changes
217 added 1 changesets with 1 changes to 1 files (+1 heads)
238 added 1 changesets with 1 changes to 1 files (+1 heads)
218 updating bookmark Y
239 updating bookmark Y
219 $ hg -R ../a book
240 $ hg -R ../a book
220 @ 1:0d2164f0ce0d
241 @ 1:0d2164f0ce0d
221 * X 1:0d2164f0ce0d
242 * X 1:0d2164f0ce0d
222 Y 3:f6fc62dde3c0
243 Y 3:f6fc62dde3c0
223 Z 1:0d2164f0ce0d
244 Z 1:0d2164f0ce0d
224
245
225 update a bookmark in the middle of a client pulling changes
246 update a bookmark in the middle of a client pulling changes
226
247
227 $ cd ..
248 $ cd ..
228 $ hg clone -q a pull-race
249 $ hg clone -q a pull-race
229 $ hg clone -q pull-race pull-race2
250 $ hg clone -q pull-race pull-race2
230 $ cd pull-race
251 $ cd pull-race
231 $ hg up -q Y
252 $ hg up -q Y
232 $ echo c4 > f2
253 $ echo c4 > f2
233 $ hg ci -Am4
254 $ hg ci -Am4
234 $ echo c5 > f3
255 $ echo c5 > f3
235 $ cat <<EOF > .hg/hgrc
256 $ cat <<EOF > .hg/hgrc
236 > [hooks]
257 > [hooks]
237 > outgoing.makecommit = hg ci -Am5; echo committed in pull-race
258 > outgoing.makecommit = hg ci -Am5; echo committed in pull-race
238 > EOF
259 > EOF
239 $ cd ../pull-race2
260 $ cd ../pull-race2
240 $ hg pull
261 $ hg pull
241 pulling from $TESTTMP/pull-race (glob)
262 pulling from $TESTTMP/pull-race (glob)
242 searching for changes
263 searching for changes
243 adding changesets
264 adding changesets
244 adding f3
265 adding f3
245 committed in pull-race
266 committed in pull-race
246 adding manifests
267 adding manifests
247 adding file changes
268 adding file changes
248 added 1 changesets with 1 changes to 1 files
269 added 1 changesets with 1 changes to 1 files
249 updating bookmark Y
270 updating bookmark Y
250 (run 'hg update' to get a working copy)
271 (run 'hg update' to get a working copy)
251 $ hg book
272 $ hg book
252 * @ 1:0d2164f0ce0d
273 * @ 1:0d2164f0ce0d
253 X 1:0d2164f0ce0d
274 X 1:0d2164f0ce0d
254 Y 4:b0a5eff05604
275 Y 4:b0a5eff05604
255 Z 1:0d2164f0ce0d
276 Z 1:0d2164f0ce0d
256 $ cd ../b
277 $ cd ../b
257
278
258 diverging a remote bookmark fails
279 diverging a remote bookmark fails
259
280
260 $ hg up -q 4e3505fd9583
281 $ hg up -q 4e3505fd9583
261 $ echo c4 > f2
282 $ echo c4 > f2
262 $ hg ci -Am4
283 $ hg ci -Am4
263 adding f2
284 adding f2
264 created new head
285 created new head
265 $ echo c5 > f2
286 $ echo c5 > f2
266 $ hg ci -Am5
287 $ hg ci -Am5
267 $ hg log -G
288 $ hg log -G
268 @ 5:c922c0139ca0 5
289 @ 5:c922c0139ca0 5
269 |
290 |
270 o 4:4efff6d98829 4
291 o 4:4efff6d98829 4
271 |
292 |
272 | o 3:f6fc62dde3c0 3
293 | o 3:f6fc62dde3c0 3
273 |/
294 |/
274 | o 2:0d2164f0ce0d 1
295 | o 2:0d2164f0ce0d 1
275 |/
296 |/
276 | o 1:9b140be10808 2
297 | o 1:9b140be10808 2
277 |/
298 |/
278 o 0:4e3505fd9583 test
299 o 0:4e3505fd9583 test
279
300
280
301
281 $ hg book -f Y
302 $ hg book -f Y
282
303
283 $ cat <<EOF > ../a/.hg/hgrc
304 $ cat <<EOF > ../a/.hg/hgrc
284 > [web]
305 > [web]
285 > push_ssl = false
306 > push_ssl = false
286 > allow_push = *
307 > allow_push = *
287 > EOF
308 > EOF
288
309
289 $ hg -R ../a serve -p $HGPORT2 -d --pid-file=../hg2.pid
310 $ hg -R ../a serve -p $HGPORT2 -d --pid-file=../hg2.pid
290 $ cat ../hg2.pid >> $DAEMON_PIDS
311 $ cat ../hg2.pid >> $DAEMON_PIDS
291
312
292 $ hg push http://localhost:$HGPORT2/
313 $ hg push http://localhost:$HGPORT2/
293 pushing to http://localhost:$HGPORT2/
314 pushing to http://localhost:$HGPORT2/
294 searching for changes
315 searching for changes
295 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
316 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
296 (merge or see "hg help push" for details about pushing new heads)
317 (merge or see "hg help push" for details about pushing new heads)
297 [255]
318 [255]
298 $ hg -R ../a book
319 $ hg -R ../a book
299 @ 1:0d2164f0ce0d
320 @ 1:0d2164f0ce0d
300 * X 1:0d2164f0ce0d
321 * X 1:0d2164f0ce0d
301 Y 3:f6fc62dde3c0
322 Y 3:f6fc62dde3c0
302 Z 1:0d2164f0ce0d
323 Z 1:0d2164f0ce0d
303
324
304
325
305 Unrelated marker does not alter the decision
326 Unrelated marker does not alter the decision
306
327
307 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
328 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
308 $ hg push http://localhost:$HGPORT2/
329 $ hg push http://localhost:$HGPORT2/
309 pushing to http://localhost:$HGPORT2/
330 pushing to http://localhost:$HGPORT2/
310 searching for changes
331 searching for changes
311 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
332 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
312 (merge or see "hg help push" for details about pushing new heads)
333 (merge or see "hg help push" for details about pushing new heads)
313 [255]
334 [255]
314 $ hg -R ../a book
335 $ hg -R ../a book
315 @ 1:0d2164f0ce0d
336 @ 1:0d2164f0ce0d
316 * X 1:0d2164f0ce0d
337 * X 1:0d2164f0ce0d
317 Y 3:f6fc62dde3c0
338 Y 3:f6fc62dde3c0
318 Z 1:0d2164f0ce0d
339 Z 1:0d2164f0ce0d
319
340
320 Update to a successor works
341 Update to a successor works
321
342
322 $ hg id --debug -r 3
343 $ hg id --debug -r 3
323 f6fc62dde3c0771e29704af56ba4d8af77abcc2f
344 f6fc62dde3c0771e29704af56ba4d8af77abcc2f
324 $ hg id --debug -r 4
345 $ hg id --debug -r 4
325 4efff6d98829d9c824c621afd6e3f01865f5439f
346 4efff6d98829d9c824c621afd6e3f01865f5439f
326 $ hg id --debug -r 5
347 $ hg id --debug -r 5
327 c922c0139ca03858f655e4a2af4dd02796a63969 tip Y
348 c922c0139ca03858f655e4a2af4dd02796a63969 tip Y
328 $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f cccccccccccccccccccccccccccccccccccccccc
349 $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f cccccccccccccccccccccccccccccccccccccccc
329 $ hg debugobsolete cccccccccccccccccccccccccccccccccccccccc 4efff6d98829d9c824c621afd6e3f01865f5439f
350 $ hg debugobsolete cccccccccccccccccccccccccccccccccccccccc 4efff6d98829d9c824c621afd6e3f01865f5439f
330 $ hg push http://localhost:$HGPORT2/
351 $ hg push http://localhost:$HGPORT2/
331 pushing to http://localhost:$HGPORT2/
352 pushing to http://localhost:$HGPORT2/
332 searching for changes
353 searching for changes
333 remote: adding changesets
354 remote: adding changesets
334 remote: adding manifests
355 remote: adding manifests
335 remote: adding file changes
356 remote: adding file changes
336 remote: added 2 changesets with 2 changes to 1 files (+1 heads)
357 remote: added 2 changesets with 2 changes to 1 files (+1 heads)
337 updating bookmark Y
358 updating bookmark Y
338 $ hg -R ../a book
359 $ hg -R ../a book
339 @ 1:0d2164f0ce0d
360 @ 1:0d2164f0ce0d
340 * X 1:0d2164f0ce0d
361 * X 1:0d2164f0ce0d
341 Y 5:c922c0139ca0
362 Y 5:c922c0139ca0
342 Z 1:0d2164f0ce0d
363 Z 1:0d2164f0ce0d
343
364
344 hgweb
365 hgweb
345
366
346 $ cat <<EOF > .hg/hgrc
367 $ cat <<EOF > .hg/hgrc
347 > [web]
368 > [web]
348 > push_ssl = false
369 > push_ssl = false
349 > allow_push = *
370 > allow_push = *
350 > EOF
371 > EOF
351
372
352 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
373 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
353 $ cat ../hg.pid >> $DAEMON_PIDS
374 $ cat ../hg.pid >> $DAEMON_PIDS
354 $ cd ../a
375 $ cd ../a
355
376
356 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
377 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
357 bookmarks
378 bookmarks
358 namespaces
379 namespaces
359 obsolete
380 obsolete
360 phases
381 phases
361 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
382 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
362 @ 9b140be1080824d768c5a4691a564088eede71f9
383 @ 9b140be1080824d768c5a4691a564088eede71f9
363 X 9b140be1080824d768c5a4691a564088eede71f9
384 X 9b140be1080824d768c5a4691a564088eede71f9
364 Y c922c0139ca03858f655e4a2af4dd02796a63969
385 Y c922c0139ca03858f655e4a2af4dd02796a63969
365 Z 9b140be1080824d768c5a4691a564088eede71f9
386 Z 9b140be1080824d768c5a4691a564088eede71f9
366 foo 0000000000000000000000000000000000000000
387 foo 0000000000000000000000000000000000000000
367 foobar 9b140be1080824d768c5a4691a564088eede71f9
388 foobar 9b140be1080824d768c5a4691a564088eede71f9
368 $ hg out -B http://localhost:$HGPORT/
389 $ hg out -B http://localhost:$HGPORT/
369 comparing with http://localhost:$HGPORT/
390 comparing with http://localhost:$HGPORT/
370 searching for changed bookmarks
391 searching for changed bookmarks
371 no changed bookmarks found
392 no changed bookmarks found
372 [1]
393 [1]
373 $ hg push -B Z http://localhost:$HGPORT/
394 $ hg push -B Z http://localhost:$HGPORT/
374 pushing to http://localhost:$HGPORT/
395 pushing to http://localhost:$HGPORT/
375 searching for changes
396 searching for changes
376 no changes found
397 no changes found
377 updating bookmark Z
398 updating bookmark Z
378 [1]
399 [1]
379 $ hg book -d Z
400 $ hg book -d Z
380 $ hg in -B http://localhost:$HGPORT/
401 $ hg in -B http://localhost:$HGPORT/
381 comparing with http://localhost:$HGPORT/
402 comparing with http://localhost:$HGPORT/
382 searching for changed bookmarks
403 searching for changed bookmarks
383 Z 0d2164f0ce0d
404 Z 0d2164f0ce0d
384 foo 000000000000
405 foo 000000000000
385 foobar 9b140be10808
406 foobar 9b140be10808
386 $ hg pull -B Z http://localhost:$HGPORT/
407 $ hg pull -B Z http://localhost:$HGPORT/
387 pulling from http://localhost:$HGPORT/
408 pulling from http://localhost:$HGPORT/
388 no changes found
409 no changes found
389 divergent bookmark @ stored as @1
410 divergent bookmark @ stored as @1
390 divergent bookmark X stored as X@1
411 divergent bookmark X stored as X@1
391 adding remote bookmark Z
412 adding remote bookmark Z
392 adding remote bookmark foo
413 adding remote bookmark foo
393 adding remote bookmark foobar
414 adding remote bookmark foobar
394 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
415 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
395 requesting all changes
416 requesting all changes
396 adding changesets
417 adding changesets
397 adding manifests
418 adding manifests
398 adding file changes
419 adding file changes
399 added 5 changesets with 5 changes to 3 files (+2 heads)
420 added 5 changesets with 5 changes to 3 files (+2 heads)
400 updating to bookmark @
421 updating to bookmark @
401 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
422 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
402 $ hg -R cloned-bookmarks bookmarks
423 $ hg -R cloned-bookmarks bookmarks
403 * @ 1:9b140be10808
424 * @ 1:9b140be10808
404 X 1:9b140be10808
425 X 1:9b140be10808
405 Y 4:c922c0139ca0
426 Y 4:c922c0139ca0
406 Z 2:0d2164f0ce0d
427 Z 2:0d2164f0ce0d
407 foo -1:000000000000
428 foo -1:000000000000
408 foobar 1:9b140be10808
429 foobar 1:9b140be10808
409
430
410 $ cd ..
431 $ cd ..
411
432
412 Pushing a bookmark should only push the changes required by that
433 Pushing a bookmark should only push the changes required by that
413 bookmark, not all outgoing changes:
434 bookmark, not all outgoing changes:
414 $ hg clone http://localhost:$HGPORT/ addmarks
435 $ hg clone http://localhost:$HGPORT/ addmarks
415 requesting all changes
436 requesting all changes
416 adding changesets
437 adding changesets
417 adding manifests
438 adding manifests
418 adding file changes
439 adding file changes
419 added 5 changesets with 5 changes to 3 files (+2 heads)
440 added 5 changesets with 5 changes to 3 files (+2 heads)
420 updating to bookmark @
441 updating to bookmark @
421 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
442 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
422 $ cd addmarks
443 $ cd addmarks
423 $ echo foo > foo
444 $ echo foo > foo
424 $ hg add foo
445 $ hg add foo
425 $ hg commit -m 'add foo'
446 $ hg commit -m 'add foo'
426 $ echo bar > bar
447 $ echo bar > bar
427 $ hg add bar
448 $ hg add bar
428 $ hg commit -m 'add bar'
449 $ hg commit -m 'add bar'
429 $ hg co "tip^"
450 $ hg co "tip^"
430 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
451 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
431 (leaving bookmark @)
452 (leaving bookmark @)
432 $ hg book add-foo
453 $ hg book add-foo
433 $ hg book -r tip add-bar
454 $ hg book -r tip add-bar
434 Note: this push *must* push only a single changeset, as that's the point
455 Note: this push *must* push only a single changeset, as that's the point
435 of this test.
456 of this test.
436 $ hg push -B add-foo --traceback
457 $ hg push -B add-foo --traceback
437 pushing to http://localhost:$HGPORT/
458 pushing to http://localhost:$HGPORT/
438 searching for changes
459 searching for changes
439 remote: adding changesets
460 remote: adding changesets
440 remote: adding manifests
461 remote: adding manifests
441 remote: adding file changes
462 remote: adding file changes
442 remote: added 1 changesets with 1 changes to 1 files
463 remote: added 1 changesets with 1 changes to 1 files
443 exporting bookmark add-foo
464 exporting bookmark add-foo
444
465
445 pushing a new bookmark on a new head does not require -f if -B is specified
466 pushing a new bookmark on a new head does not require -f if -B is specified
446
467
447 $ hg up -q X
468 $ hg up -q X
448 $ hg book W
469 $ hg book W
449 $ echo c5 > f2
470 $ echo c5 > f2
450 $ hg ci -Am5
471 $ hg ci -Am5
451 created new head
472 created new head
452 $ hg push -B W
473 $ hg push -B W
453 pushing to http://localhost:$HGPORT/
474 pushing to http://localhost:$HGPORT/
454 searching for changes
475 searching for changes
455 remote: adding changesets
476 remote: adding changesets
456 remote: adding manifests
477 remote: adding manifests
457 remote: adding file changes
478 remote: adding file changes
458 remote: added 1 changesets with 1 changes to 1 files (+1 heads)
479 remote: added 1 changesets with 1 changes to 1 files (+1 heads)
459 exporting bookmark W
480 exporting bookmark W
460 $ hg -R ../b id -r W
481 $ hg -R ../b id -r W
461 cc978a373a53 tip W
482 cc978a373a53 tip W
462
483
463 $ cd ..
484 $ cd ..
464
485
465 pushing an unchanged bookmark should result in no changes
486 pushing an unchanged bookmark should result in no changes
466
487
467 $ hg init unchanged-a
488 $ hg init unchanged-a
468 $ hg init unchanged-b
489 $ hg init unchanged-b
469 $ cd unchanged-a
490 $ cd unchanged-a
470 $ echo initial > foo
491 $ echo initial > foo
471 $ hg commit -A -m initial
492 $ hg commit -A -m initial
472 adding foo
493 adding foo
473 $ hg bookmark @
494 $ hg bookmark @
474 $ hg push -B @ ../unchanged-b
495 $ hg push -B @ ../unchanged-b
475 pushing to ../unchanged-b
496 pushing to ../unchanged-b
476 searching for changes
497 searching for changes
477 adding changesets
498 adding changesets
478 adding manifests
499 adding manifests
479 adding file changes
500 adding file changes
480 added 1 changesets with 1 changes to 1 files
501 added 1 changesets with 1 changes to 1 files
481 exporting bookmark @
502 exporting bookmark @
482
503
483 $ hg push -B @ ../unchanged-b
504 $ hg push -B @ ../unchanged-b
484 pushing to ../unchanged-b
505 pushing to ../unchanged-b
485 searching for changes
506 searching for changes
486 no changes found
507 no changes found
487 [1]
508 [1]
488
509
489
510
490 Check hook preventing push (issue4455)
511 Check hook preventing push (issue4455)
491 ======================================
512 ======================================
492
513
493 $ hg bookmarks
514 $ hg bookmarks
494 * @ 0:55482a6fb4b1
515 * @ 0:55482a6fb4b1
495 $ hg log -G
516 $ hg log -G
496 @ 0:55482a6fb4b1 initial
517 @ 0:55482a6fb4b1 initial
497
518
498 $ hg init ../issue4455-dest
519 $ hg init ../issue4455-dest
499 $ hg push ../issue4455-dest # changesets only
520 $ hg push ../issue4455-dest # changesets only
500 pushing to ../issue4455-dest
521 pushing to ../issue4455-dest
501 searching for changes
522 searching for changes
502 adding changesets
523 adding changesets
503 adding manifests
524 adding manifests
504 adding file changes
525 adding file changes
505 added 1 changesets with 1 changes to 1 files
526 added 1 changesets with 1 changes to 1 files
506 $ cat >> .hg/hgrc << EOF
527 $ cat >> .hg/hgrc << EOF
507 > [paths]
528 > [paths]
508 > local=../issue4455-dest/
529 > local=../issue4455-dest/
509 > ssh=ssh://user@dummy/issue4455-dest
530 > ssh=ssh://user@dummy/issue4455-dest
510 > http=http://localhost:$HGPORT/
531 > http=http://localhost:$HGPORT/
511 > [ui]
532 > [ui]
512 > ssh=python "$TESTDIR/dummyssh"
533 > ssh=python "$TESTDIR/dummyssh"
513 > EOF
534 > EOF
514 $ cat >> ../issue4455-dest/.hg/hgrc << EOF
535 $ cat >> ../issue4455-dest/.hg/hgrc << EOF
515 > [hooks]
536 > [hooks]
516 > prepushkey=false
537 > prepushkey=false
517 > [web]
538 > [web]
518 > push_ssl = false
539 > push_ssl = false
519 > allow_push = *
540 > allow_push = *
520 > EOF
541 > EOF
521 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
542 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
522 $ hg -R ../issue4455-dest serve -p $HGPORT -d --pid-file=../issue4455.pid -E ../issue4455-error.log
543 $ hg -R ../issue4455-dest serve -p $HGPORT -d --pid-file=../issue4455.pid -E ../issue4455-error.log
523 $ cat ../issue4455.pid >> $DAEMON_PIDS
544 $ cat ../issue4455.pid >> $DAEMON_PIDS
524
545
525 Local push
546 Local push
526 ----------
547 ----------
527
548
528 $ hg push -B @ local
549 $ hg push -B @ local
529 pushing to $TESTTMP/issue4455-dest (glob)
550 pushing to $TESTTMP/issue4455-dest (glob)
530 searching for changes
551 searching for changes
531 no changes found
552 no changes found
532 pushkey-abort: prepushkey hook exited with status 1
553 pushkey-abort: prepushkey hook exited with status 1
533 exporting bookmark @ failed!
554 exporting bookmark @ failed!
534 [1]
555 [1]
535 $ hg -R ../issue4455-dest/ bookmarks
556 $ hg -R ../issue4455-dest/ bookmarks
536 no bookmarks set
557 no bookmarks set
537
558
538 Using ssh
559 Using ssh
539 ---------
560 ---------
540
561
541 $ hg push -B @ ssh
562 $ hg push -B @ ssh
542 pushing to ssh://user@dummy/issue4455-dest
563 pushing to ssh://user@dummy/issue4455-dest
543 searching for changes
564 searching for changes
544 no changes found
565 no changes found
545 remote: pushkey-abort: prepushkey hook exited with status 1
566 remote: pushkey-abort: prepushkey hook exited with status 1
546 exporting bookmark @ failed!
567 exporting bookmark @ failed!
547 [1]
568 [1]
548 $ hg -R ../issue4455-dest/ bookmarks
569 $ hg -R ../issue4455-dest/ bookmarks
549 no bookmarks set
570 no bookmarks set
550
571
551 Using http
572 Using http
552 ----------
573 ----------
553
574
554 $ hg push -B @ http
575 $ hg push -B @ http
555 pushing to http://localhost:$HGPORT/
576 pushing to http://localhost:$HGPORT/
556 searching for changes
577 searching for changes
557 no changes found
578 no changes found
558 remote: pushkey-abort: prepushkey hook exited with status 1
579 remote: pushkey-abort: prepushkey hook exited with status 1
559 exporting bookmark @ failed!
580 exporting bookmark @ failed!
560 [1]
581 [1]
561 $ hg -R ../issue4455-dest/ bookmarks
582 $ hg -R ../issue4455-dest/ bookmarks
562 no bookmarks set
583 no bookmarks set
General Comments 0
You need to be logged in to leave comments. Login now