##// END OF EJS Templates
bookmarks: reuse @number bookmark, if it refers changeset referred remotely...
FUJIWARA Katsunori -
r24355:ca4b8968 default
parent child Browse files
Show More
@@ -1,474 +1,478 b''
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, remotenode):
366 '''Return appropriate diverged bookmark for specified ``path``
366 '''Return appropriate diverged bookmark for specified ``path``
367
367
368 This returns None, if it is failed to assign any divergent
368 This returns None, if it is failed to assign any divergent
369 bookmark name.
369 bookmark name.
370
371 This reuses already existing one with "@number" suffix, if it
372 refers ``remotenode``.
370 '''
373 '''
371 if b == '@':
374 if b == '@':
372 b = ''
375 b = ''
373 # try to use an @pathalias suffix
376 # try to use an @pathalias suffix
374 # if an @pathalias already exists, we overwrite (update) it
377 # if an @pathalias already exists, we overwrite (update) it
375 if path.startswith("file:"):
378 if path.startswith("file:"):
376 path = util.url(path).path
379 path = util.url(path).path
377 for p, u in ui.configitems("paths"):
380 for p, u in ui.configitems("paths"):
378 if u.startswith("file:"):
381 if u.startswith("file:"):
379 u = util.url(u).path
382 u = util.url(u).path
380 if path == u:
383 if path == u:
381 return '%s@%s' % (b, p)
384 return '%s@%s' % (b, p)
382
385
383 # assign a unique "@number" suffix newly
386 # assign a unique "@number" suffix newly
384 for x in range(1, 100):
387 for x in range(1, 100):
385 n = '%s@%d' % (b, x)
388 n = '%s@%d' % (b, x)
386 if n not in localmarks:
389 if n not in localmarks or localmarks[n] == remotenode:
387 return n
390 return n
388
391
389 return None
392 return None
390
393
391 def updatefromremote(ui, repo, remotemarks, path, trfunc, explicit=()):
394 def updatefromremote(ui, repo, remotemarks, path, trfunc, explicit=()):
392 ui.debug("checking for updated bookmarks\n")
395 ui.debug("checking for updated bookmarks\n")
393 localmarks = repo._bookmarks
396 localmarks = repo._bookmarks
394 (addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same
397 (addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same
395 ) = compare(repo, remotemarks, localmarks, dsthex=hex)
398 ) = compare(repo, remotemarks, localmarks, dsthex=hex)
396
399
397 status = ui.status
400 status = ui.status
398 warn = ui.warn
401 warn = ui.warn
399 if ui.configbool('ui', 'quietbookmarkmove', False):
402 if ui.configbool('ui', 'quietbookmarkmove', False):
400 status = warn = ui.debug
403 status = warn = ui.debug
401
404
402 explicit = set(explicit)
405 explicit = set(explicit)
403 changed = []
406 changed = []
404 for b, scid, dcid in addsrc:
407 for b, scid, dcid in addsrc:
405 if scid in repo: # add remote bookmarks for changes we already have
408 if scid in repo: # add remote bookmarks for changes we already have
406 changed.append((b, bin(scid), status,
409 changed.append((b, bin(scid), status,
407 _("adding remote bookmark %s\n") % (b)))
410 _("adding remote bookmark %s\n") % (b)))
408 for b, scid, dcid in advsrc:
411 for b, scid, dcid in advsrc:
409 changed.append((b, bin(scid), status,
412 changed.append((b, bin(scid), status,
410 _("updating bookmark %s\n") % (b)))
413 _("updating bookmark %s\n") % (b)))
411 # remove normal movement from explicit set
414 # remove normal movement from explicit set
412 explicit.difference_update(d[0] for d in changed)
415 explicit.difference_update(d[0] for d in changed)
413
416
414 for b, scid, dcid in diverge:
417 for b, scid, dcid in diverge:
415 if b in explicit:
418 if b in explicit:
416 explicit.discard(b)
419 explicit.discard(b)
417 changed.append((b, bin(scid), status,
420 changed.append((b, bin(scid), status,
418 _("importing bookmark %s\n") % (b)))
421 _("importing bookmark %s\n") % (b)))
419 else:
422 else:
420 db = _diverge(ui, b, path, localmarks)
423 snode = bin(scid)
424 db = _diverge(ui, b, path, localmarks, snode)
421 if db:
425 if db:
422 changed.append((db, bin(scid), warn,
426 changed.append((db, snode, warn,
423 _("divergent bookmark %s stored as %s\n") %
427 _("divergent bookmark %s stored as %s\n") %
424 (b, db)))
428 (b, db)))
425 else:
429 else:
426 warn(_("warning: failed to assign numbered name "
430 warn(_("warning: failed to assign numbered name "
427 "to divergent bookmark %s\n") % (b))
431 "to divergent bookmark %s\n") % (b))
428 for b, scid, dcid in adddst + advdst:
432 for b, scid, dcid in adddst + advdst:
429 if b in explicit:
433 if b in explicit:
430 explicit.discard(b)
434 explicit.discard(b)
431 changed.append((b, bin(scid), status,
435 changed.append((b, bin(scid), status,
432 _("importing bookmark %s\n") % (b)))
436 _("importing bookmark %s\n") % (b)))
433
437
434 if changed:
438 if changed:
435 tr = trfunc()
439 tr = trfunc()
436 for b, node, writer, msg in sorted(changed):
440 for b, node, writer, msg in sorted(changed):
437 localmarks[b] = node
441 localmarks[b] = node
438 writer(msg)
442 writer(msg)
439 localmarks.recordchange(tr)
443 localmarks.recordchange(tr)
440
444
441 def diff(ui, dst, src):
445 def diff(ui, dst, src):
442 ui.status(_("searching for changed bookmarks\n"))
446 ui.status(_("searching for changed bookmarks\n"))
443
447
444 smarks = src.listkeys('bookmarks')
448 smarks = src.listkeys('bookmarks')
445 dmarks = dst.listkeys('bookmarks')
449 dmarks = dst.listkeys('bookmarks')
446
450
447 diff = sorted(set(smarks) - set(dmarks))
451 diff = sorted(set(smarks) - set(dmarks))
448 for k in diff:
452 for k in diff:
449 if ui.debugflag:
453 if ui.debugflag:
450 mark = smarks[k]
454 mark = smarks[k]
451 else:
455 else:
452 mark = smarks[k][:12]
456 mark = smarks[k][:12]
453 ui.write(" %-25s %s\n" % (k, mark))
457 ui.write(" %-25s %s\n" % (k, mark))
454
458
455 if len(diff) <= 0:
459 if len(diff) <= 0:
456 ui.status(_("no changed bookmarks found\n"))
460 ui.status(_("no changed bookmarks found\n"))
457 return 1
461 return 1
458 return 0
462 return 0
459
463
460 def validdest(repo, old, new):
464 def validdest(repo, old, new):
461 """Is the new bookmark destination a valid update from the old one"""
465 """Is the new bookmark destination a valid update from the old one"""
462 repo = repo.unfiltered()
466 repo = repo.unfiltered()
463 if old == new:
467 if old == new:
464 # Old == new -> nothing to update.
468 # Old == new -> nothing to update.
465 return False
469 return False
466 elif not old:
470 elif not old:
467 # old is nullrev, anything is valid.
471 # old is nullrev, anything is valid.
468 # (new != nullrev has been excluded by the previous check)
472 # (new != nullrev has been excluded by the previous check)
469 return True
473 return True
470 elif repo.obsstore:
474 elif repo.obsstore:
471 return new.node() in obsolete.foreground(repo, [old.node()])
475 return new.node() in obsolete.foreground(repo, [old.node()])
472 else:
476 else:
473 # still an independent clause as it is lazier (and therefore faster)
477 # still an independent clause as it is lazier (and therefore faster)
474 return old.descendant(new)
478 return old.descendant(new)
@@ -1,583 +1,601 b''
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
167
168 (test that too many divergence of bookmark)
168 (test that too many divergence of bookmark)
169
169
170 $ cat > $TESTTMP/seq.py <<EOF
170 $ cat > $TESTTMP/seq.py <<EOF
171 > import sys
171 > import sys
172 > for i in xrange(*[int(a) for a in sys.argv[1:]]):
172 > for i in xrange(*[int(a) for a in sys.argv[1:]]):
173 > print i
173 > print i
174 > EOF
174 > EOF
175 $ python $TESTTMP/seq.py 1 100 | while read i; do hg bookmarks -r 000000000000 "X@${i}"; done
175 $ python $TESTTMP/seq.py 1 100 | while read i; do hg bookmarks -r 000000000000 "X@${i}"; done
176 $ hg pull ../a
176 $ hg pull ../a
177 pulling from ../a
177 pulling from ../a
178 searching for changes
178 searching for changes
179 no changes found
179 no changes found
180 warning: failed to assign numbered name to divergent bookmark X
180 warning: failed to assign numbered name to divergent bookmark X
181 divergent bookmark @ stored as @1
181 divergent bookmark @ stored as @1
182 $ hg bookmarks | grep '^ X' | grep -v ':000000000000'
182 $ hg bookmarks | grep '^ X' | grep -v ':000000000000'
183 X 1:9b140be10808
183 X 1:9b140be10808
184 X@foo 2:0d2164f0ce0d
184 X@foo 2:0d2164f0ce0d
185
186 (test that remotely diverged bookmarks are reused if they aren't changed)
187
188 $ hg bookmarks | grep '^ @'
189 @ 1:9b140be10808
190 @1 2:0d2164f0ce0d
191 @foo 2:0d2164f0ce0d
192 $ hg pull ../a
193 pulling from ../a
194 searching for changes
195 no changes found
196 warning: failed to assign numbered name to divergent bookmark X
197 divergent bookmark @ stored as @1
198 $ hg bookmarks | grep '^ @'
199 @ 1:9b140be10808
200 @1 2:0d2164f0ce0d
201 @foo 2:0d2164f0ce0d
202
185 $ python $TESTTMP/seq.py 1 100 | while read i; do hg bookmarks -d "X@${i}"; done
203 $ python $TESTTMP/seq.py 1 100 | while read i; do hg bookmarks -d "X@${i}"; done
186 $ hg bookmarks -d "@1"
204 $ hg bookmarks -d "@1"
187
205
188 $ hg push -f ../a
206 $ hg push -f ../a
189 pushing to ../a
207 pushing to ../a
190 searching for changes
208 searching for changes
191 adding changesets
209 adding changesets
192 adding manifests
210 adding manifests
193 adding file changes
211 adding file changes
194 added 1 changesets with 1 changes to 1 files (+1 heads)
212 added 1 changesets with 1 changes to 1 files (+1 heads)
195 $ hg -R ../a book
213 $ hg -R ../a book
196 @ 1:0d2164f0ce0d
214 @ 1:0d2164f0ce0d
197 * X 1:0d2164f0ce0d
215 * X 1:0d2164f0ce0d
198 Y 0:4e3505fd9583
216 Y 0:4e3505fd9583
199 Z 1:0d2164f0ce0d
217 Z 1:0d2164f0ce0d
200
218
201 explicit pull should overwrite the local version (issue4439)
219 explicit pull should overwrite the local version (issue4439)
202
220
203 $ hg pull --config paths.foo=../a foo -B X
221 $ hg pull --config paths.foo=../a foo -B X
204 pulling from $TESTTMP/a (glob)
222 pulling from $TESTTMP/a (glob)
205 no changes found
223 no changes found
206 divergent bookmark @ stored as @foo
224 divergent bookmark @ stored as @foo
207 importing bookmark X
225 importing bookmark X
208
226
209 reinstall state for further testing:
227 reinstall state for further testing:
210
228
211 $ hg book -fr 9b140be10808 X
229 $ hg book -fr 9b140be10808 X
212
230
213 revsets should not ignore divergent bookmarks
231 revsets should not ignore divergent bookmarks
214
232
215 $ hg bookmark -fr 1 Z
233 $ hg bookmark -fr 1 Z
216 $ hg log -r 'bookmark()' --template '{rev}:{node|short} {bookmarks}\n'
234 $ hg log -r 'bookmark()' --template '{rev}:{node|short} {bookmarks}\n'
217 0:4e3505fd9583 Y
235 0:4e3505fd9583 Y
218 1:9b140be10808 @ X Z foobar
236 1:9b140be10808 @ X Z foobar
219 2:0d2164f0ce0d @foo X@foo
237 2:0d2164f0ce0d @foo X@foo
220 $ hg log -r 'bookmark("X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
238 $ hg log -r 'bookmark("X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
221 2:0d2164f0ce0d @foo X@foo
239 2:0d2164f0ce0d @foo X@foo
222 $ hg log -r 'bookmark("re:X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
240 $ hg log -r 'bookmark("re:X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
223 2:0d2164f0ce0d @foo X@foo
241 2:0d2164f0ce0d @foo X@foo
224
242
225 update a remote bookmark from a non-head to a head
243 update a remote bookmark from a non-head to a head
226
244
227 $ hg up -q Y
245 $ hg up -q Y
228 $ echo c3 > f2
246 $ echo c3 > f2
229 $ hg ci -Am3
247 $ hg ci -Am3
230 adding f2
248 adding f2
231 created new head
249 created new head
232 $ hg push ../a
250 $ hg push ../a
233 pushing to ../a
251 pushing to ../a
234 searching for changes
252 searching for changes
235 adding changesets
253 adding changesets
236 adding manifests
254 adding manifests
237 adding file changes
255 adding file changes
238 added 1 changesets with 1 changes to 1 files (+1 heads)
256 added 1 changesets with 1 changes to 1 files (+1 heads)
239 updating bookmark Y
257 updating bookmark Y
240 $ hg -R ../a book
258 $ hg -R ../a book
241 @ 1:0d2164f0ce0d
259 @ 1:0d2164f0ce0d
242 * X 1:0d2164f0ce0d
260 * X 1:0d2164f0ce0d
243 Y 3:f6fc62dde3c0
261 Y 3:f6fc62dde3c0
244 Z 1:0d2164f0ce0d
262 Z 1:0d2164f0ce0d
245
263
246 update a bookmark in the middle of a client pulling changes
264 update a bookmark in the middle of a client pulling changes
247
265
248 $ cd ..
266 $ cd ..
249 $ hg clone -q a pull-race
267 $ hg clone -q a pull-race
250 $ hg clone -q pull-race pull-race2
268 $ hg clone -q pull-race pull-race2
251 $ cd pull-race
269 $ cd pull-race
252 $ hg up -q Y
270 $ hg up -q Y
253 $ echo c4 > f2
271 $ echo c4 > f2
254 $ hg ci -Am4
272 $ hg ci -Am4
255 $ echo c5 > f3
273 $ echo c5 > f3
256 $ cat <<EOF > .hg/hgrc
274 $ cat <<EOF > .hg/hgrc
257 > [hooks]
275 > [hooks]
258 > outgoing.makecommit = hg ci -Am5; echo committed in pull-race
276 > outgoing.makecommit = hg ci -Am5; echo committed in pull-race
259 > EOF
277 > EOF
260 $ cd ../pull-race2
278 $ cd ../pull-race2
261 $ hg pull
279 $ hg pull
262 pulling from $TESTTMP/pull-race (glob)
280 pulling from $TESTTMP/pull-race (glob)
263 searching for changes
281 searching for changes
264 adding changesets
282 adding changesets
265 adding f3
283 adding f3
266 committed in pull-race
284 committed in pull-race
267 adding manifests
285 adding manifests
268 adding file changes
286 adding file changes
269 added 1 changesets with 1 changes to 1 files
287 added 1 changesets with 1 changes to 1 files
270 updating bookmark Y
288 updating bookmark Y
271 (run 'hg update' to get a working copy)
289 (run 'hg update' to get a working copy)
272 $ hg book
290 $ hg book
273 * @ 1:0d2164f0ce0d
291 * @ 1:0d2164f0ce0d
274 X 1:0d2164f0ce0d
292 X 1:0d2164f0ce0d
275 Y 4:b0a5eff05604
293 Y 4:b0a5eff05604
276 Z 1:0d2164f0ce0d
294 Z 1:0d2164f0ce0d
277 $ cd ../b
295 $ cd ../b
278
296
279 diverging a remote bookmark fails
297 diverging a remote bookmark fails
280
298
281 $ hg up -q 4e3505fd9583
299 $ hg up -q 4e3505fd9583
282 $ echo c4 > f2
300 $ echo c4 > f2
283 $ hg ci -Am4
301 $ hg ci -Am4
284 adding f2
302 adding f2
285 created new head
303 created new head
286 $ echo c5 > f2
304 $ echo c5 > f2
287 $ hg ci -Am5
305 $ hg ci -Am5
288 $ hg log -G
306 $ hg log -G
289 @ 5:c922c0139ca0 5
307 @ 5:c922c0139ca0 5
290 |
308 |
291 o 4:4efff6d98829 4
309 o 4:4efff6d98829 4
292 |
310 |
293 | o 3:f6fc62dde3c0 3
311 | o 3:f6fc62dde3c0 3
294 |/
312 |/
295 | o 2:0d2164f0ce0d 1
313 | o 2:0d2164f0ce0d 1
296 |/
314 |/
297 | o 1:9b140be10808 2
315 | o 1:9b140be10808 2
298 |/
316 |/
299 o 0:4e3505fd9583 test
317 o 0:4e3505fd9583 test
300
318
301
319
302 $ hg book -f Y
320 $ hg book -f Y
303
321
304 $ cat <<EOF > ../a/.hg/hgrc
322 $ cat <<EOF > ../a/.hg/hgrc
305 > [web]
323 > [web]
306 > push_ssl = false
324 > push_ssl = false
307 > allow_push = *
325 > allow_push = *
308 > EOF
326 > EOF
309
327
310 $ hg -R ../a serve -p $HGPORT2 -d --pid-file=../hg2.pid
328 $ hg -R ../a serve -p $HGPORT2 -d --pid-file=../hg2.pid
311 $ cat ../hg2.pid >> $DAEMON_PIDS
329 $ cat ../hg2.pid >> $DAEMON_PIDS
312
330
313 $ hg push http://localhost:$HGPORT2/
331 $ hg push http://localhost:$HGPORT2/
314 pushing to http://localhost:$HGPORT2/
332 pushing to http://localhost:$HGPORT2/
315 searching for changes
333 searching for changes
316 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
334 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
317 (merge or see "hg help push" for details about pushing new heads)
335 (merge or see "hg help push" for details about pushing new heads)
318 [255]
336 [255]
319 $ hg -R ../a book
337 $ hg -R ../a book
320 @ 1:0d2164f0ce0d
338 @ 1:0d2164f0ce0d
321 * X 1:0d2164f0ce0d
339 * X 1:0d2164f0ce0d
322 Y 3:f6fc62dde3c0
340 Y 3:f6fc62dde3c0
323 Z 1:0d2164f0ce0d
341 Z 1:0d2164f0ce0d
324
342
325
343
326 Unrelated marker does not alter the decision
344 Unrelated marker does not alter the decision
327
345
328 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
346 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
329 $ hg push http://localhost:$HGPORT2/
347 $ hg push http://localhost:$HGPORT2/
330 pushing to http://localhost:$HGPORT2/
348 pushing to http://localhost:$HGPORT2/
331 searching for changes
349 searching for changes
332 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
350 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
333 (merge or see "hg help push" for details about pushing new heads)
351 (merge or see "hg help push" for details about pushing new heads)
334 [255]
352 [255]
335 $ hg -R ../a book
353 $ hg -R ../a book
336 @ 1:0d2164f0ce0d
354 @ 1:0d2164f0ce0d
337 * X 1:0d2164f0ce0d
355 * X 1:0d2164f0ce0d
338 Y 3:f6fc62dde3c0
356 Y 3:f6fc62dde3c0
339 Z 1:0d2164f0ce0d
357 Z 1:0d2164f0ce0d
340
358
341 Update to a successor works
359 Update to a successor works
342
360
343 $ hg id --debug -r 3
361 $ hg id --debug -r 3
344 f6fc62dde3c0771e29704af56ba4d8af77abcc2f
362 f6fc62dde3c0771e29704af56ba4d8af77abcc2f
345 $ hg id --debug -r 4
363 $ hg id --debug -r 4
346 4efff6d98829d9c824c621afd6e3f01865f5439f
364 4efff6d98829d9c824c621afd6e3f01865f5439f
347 $ hg id --debug -r 5
365 $ hg id --debug -r 5
348 c922c0139ca03858f655e4a2af4dd02796a63969 tip Y
366 c922c0139ca03858f655e4a2af4dd02796a63969 tip Y
349 $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f cccccccccccccccccccccccccccccccccccccccc
367 $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f cccccccccccccccccccccccccccccccccccccccc
350 $ hg debugobsolete cccccccccccccccccccccccccccccccccccccccc 4efff6d98829d9c824c621afd6e3f01865f5439f
368 $ hg debugobsolete cccccccccccccccccccccccccccccccccccccccc 4efff6d98829d9c824c621afd6e3f01865f5439f
351 $ hg push http://localhost:$HGPORT2/
369 $ hg push http://localhost:$HGPORT2/
352 pushing to http://localhost:$HGPORT2/
370 pushing to http://localhost:$HGPORT2/
353 searching for changes
371 searching for changes
354 remote: adding changesets
372 remote: adding changesets
355 remote: adding manifests
373 remote: adding manifests
356 remote: adding file changes
374 remote: adding file changes
357 remote: added 2 changesets with 2 changes to 1 files (+1 heads)
375 remote: added 2 changesets with 2 changes to 1 files (+1 heads)
358 updating bookmark Y
376 updating bookmark Y
359 $ hg -R ../a book
377 $ hg -R ../a book
360 @ 1:0d2164f0ce0d
378 @ 1:0d2164f0ce0d
361 * X 1:0d2164f0ce0d
379 * X 1:0d2164f0ce0d
362 Y 5:c922c0139ca0
380 Y 5:c922c0139ca0
363 Z 1:0d2164f0ce0d
381 Z 1:0d2164f0ce0d
364
382
365 hgweb
383 hgweb
366
384
367 $ cat <<EOF > .hg/hgrc
385 $ cat <<EOF > .hg/hgrc
368 > [web]
386 > [web]
369 > push_ssl = false
387 > push_ssl = false
370 > allow_push = *
388 > allow_push = *
371 > EOF
389 > EOF
372
390
373 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
391 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
374 $ cat ../hg.pid >> $DAEMON_PIDS
392 $ cat ../hg.pid >> $DAEMON_PIDS
375 $ cd ../a
393 $ cd ../a
376
394
377 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
395 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
378 bookmarks
396 bookmarks
379 namespaces
397 namespaces
380 obsolete
398 obsolete
381 phases
399 phases
382 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
400 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
383 @ 9b140be1080824d768c5a4691a564088eede71f9
401 @ 9b140be1080824d768c5a4691a564088eede71f9
384 X 9b140be1080824d768c5a4691a564088eede71f9
402 X 9b140be1080824d768c5a4691a564088eede71f9
385 Y c922c0139ca03858f655e4a2af4dd02796a63969
403 Y c922c0139ca03858f655e4a2af4dd02796a63969
386 Z 9b140be1080824d768c5a4691a564088eede71f9
404 Z 9b140be1080824d768c5a4691a564088eede71f9
387 foo 0000000000000000000000000000000000000000
405 foo 0000000000000000000000000000000000000000
388 foobar 9b140be1080824d768c5a4691a564088eede71f9
406 foobar 9b140be1080824d768c5a4691a564088eede71f9
389 $ hg out -B http://localhost:$HGPORT/
407 $ hg out -B http://localhost:$HGPORT/
390 comparing with http://localhost:$HGPORT/
408 comparing with http://localhost:$HGPORT/
391 searching for changed bookmarks
409 searching for changed bookmarks
392 no changed bookmarks found
410 no changed bookmarks found
393 [1]
411 [1]
394 $ hg push -B Z http://localhost:$HGPORT/
412 $ hg push -B Z http://localhost:$HGPORT/
395 pushing to http://localhost:$HGPORT/
413 pushing to http://localhost:$HGPORT/
396 searching for changes
414 searching for changes
397 no changes found
415 no changes found
398 updating bookmark Z
416 updating bookmark Z
399 [1]
417 [1]
400 $ hg book -d Z
418 $ hg book -d Z
401 $ hg in -B http://localhost:$HGPORT/
419 $ hg in -B http://localhost:$HGPORT/
402 comparing with http://localhost:$HGPORT/
420 comparing with http://localhost:$HGPORT/
403 searching for changed bookmarks
421 searching for changed bookmarks
404 Z 0d2164f0ce0d
422 Z 0d2164f0ce0d
405 foo 000000000000
423 foo 000000000000
406 foobar 9b140be10808
424 foobar 9b140be10808
407 $ hg pull -B Z http://localhost:$HGPORT/
425 $ hg pull -B Z http://localhost:$HGPORT/
408 pulling from http://localhost:$HGPORT/
426 pulling from http://localhost:$HGPORT/
409 no changes found
427 no changes found
410 divergent bookmark @ stored as @1
428 divergent bookmark @ stored as @1
411 divergent bookmark X stored as X@1
429 divergent bookmark X stored as X@1
412 adding remote bookmark Z
430 adding remote bookmark Z
413 adding remote bookmark foo
431 adding remote bookmark foo
414 adding remote bookmark foobar
432 adding remote bookmark foobar
415 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
433 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
416 requesting all changes
434 requesting all changes
417 adding changesets
435 adding changesets
418 adding manifests
436 adding manifests
419 adding file changes
437 adding file changes
420 added 5 changesets with 5 changes to 3 files (+2 heads)
438 added 5 changesets with 5 changes to 3 files (+2 heads)
421 updating to bookmark @
439 updating to bookmark @
422 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
440 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
423 $ hg -R cloned-bookmarks bookmarks
441 $ hg -R cloned-bookmarks bookmarks
424 * @ 1:9b140be10808
442 * @ 1:9b140be10808
425 X 1:9b140be10808
443 X 1:9b140be10808
426 Y 4:c922c0139ca0
444 Y 4:c922c0139ca0
427 Z 2:0d2164f0ce0d
445 Z 2:0d2164f0ce0d
428 foo -1:000000000000
446 foo -1:000000000000
429 foobar 1:9b140be10808
447 foobar 1:9b140be10808
430
448
431 $ cd ..
449 $ cd ..
432
450
433 Pushing a bookmark should only push the changes required by that
451 Pushing a bookmark should only push the changes required by that
434 bookmark, not all outgoing changes:
452 bookmark, not all outgoing changes:
435 $ hg clone http://localhost:$HGPORT/ addmarks
453 $ hg clone http://localhost:$HGPORT/ addmarks
436 requesting all changes
454 requesting all changes
437 adding changesets
455 adding changesets
438 adding manifests
456 adding manifests
439 adding file changes
457 adding file changes
440 added 5 changesets with 5 changes to 3 files (+2 heads)
458 added 5 changesets with 5 changes to 3 files (+2 heads)
441 updating to bookmark @
459 updating to bookmark @
442 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
460 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
443 $ cd addmarks
461 $ cd addmarks
444 $ echo foo > foo
462 $ echo foo > foo
445 $ hg add foo
463 $ hg add foo
446 $ hg commit -m 'add foo'
464 $ hg commit -m 'add foo'
447 $ echo bar > bar
465 $ echo bar > bar
448 $ hg add bar
466 $ hg add bar
449 $ hg commit -m 'add bar'
467 $ hg commit -m 'add bar'
450 $ hg co "tip^"
468 $ hg co "tip^"
451 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
469 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
452 (leaving bookmark @)
470 (leaving bookmark @)
453 $ hg book add-foo
471 $ hg book add-foo
454 $ hg book -r tip add-bar
472 $ hg book -r tip add-bar
455 Note: this push *must* push only a single changeset, as that's the point
473 Note: this push *must* push only a single changeset, as that's the point
456 of this test.
474 of this test.
457 $ hg push -B add-foo --traceback
475 $ hg push -B add-foo --traceback
458 pushing to http://localhost:$HGPORT/
476 pushing to http://localhost:$HGPORT/
459 searching for changes
477 searching for changes
460 remote: adding changesets
478 remote: adding changesets
461 remote: adding manifests
479 remote: adding manifests
462 remote: adding file changes
480 remote: adding file changes
463 remote: added 1 changesets with 1 changes to 1 files
481 remote: added 1 changesets with 1 changes to 1 files
464 exporting bookmark add-foo
482 exporting bookmark add-foo
465
483
466 pushing a new bookmark on a new head does not require -f if -B is specified
484 pushing a new bookmark on a new head does not require -f if -B is specified
467
485
468 $ hg up -q X
486 $ hg up -q X
469 $ hg book W
487 $ hg book W
470 $ echo c5 > f2
488 $ echo c5 > f2
471 $ hg ci -Am5
489 $ hg ci -Am5
472 created new head
490 created new head
473 $ hg push -B W
491 $ hg push -B W
474 pushing to http://localhost:$HGPORT/
492 pushing to http://localhost:$HGPORT/
475 searching for changes
493 searching for changes
476 remote: adding changesets
494 remote: adding changesets
477 remote: adding manifests
495 remote: adding manifests
478 remote: adding file changes
496 remote: adding file changes
479 remote: added 1 changesets with 1 changes to 1 files (+1 heads)
497 remote: added 1 changesets with 1 changes to 1 files (+1 heads)
480 exporting bookmark W
498 exporting bookmark W
481 $ hg -R ../b id -r W
499 $ hg -R ../b id -r W
482 cc978a373a53 tip W
500 cc978a373a53 tip W
483
501
484 $ cd ..
502 $ cd ..
485
503
486 pushing an unchanged bookmark should result in no changes
504 pushing an unchanged bookmark should result in no changes
487
505
488 $ hg init unchanged-a
506 $ hg init unchanged-a
489 $ hg init unchanged-b
507 $ hg init unchanged-b
490 $ cd unchanged-a
508 $ cd unchanged-a
491 $ echo initial > foo
509 $ echo initial > foo
492 $ hg commit -A -m initial
510 $ hg commit -A -m initial
493 adding foo
511 adding foo
494 $ hg bookmark @
512 $ hg bookmark @
495 $ hg push -B @ ../unchanged-b
513 $ hg push -B @ ../unchanged-b
496 pushing to ../unchanged-b
514 pushing to ../unchanged-b
497 searching for changes
515 searching for changes
498 adding changesets
516 adding changesets
499 adding manifests
517 adding manifests
500 adding file changes
518 adding file changes
501 added 1 changesets with 1 changes to 1 files
519 added 1 changesets with 1 changes to 1 files
502 exporting bookmark @
520 exporting bookmark @
503
521
504 $ hg push -B @ ../unchanged-b
522 $ hg push -B @ ../unchanged-b
505 pushing to ../unchanged-b
523 pushing to ../unchanged-b
506 searching for changes
524 searching for changes
507 no changes found
525 no changes found
508 [1]
526 [1]
509
527
510
528
511 Check hook preventing push (issue4455)
529 Check hook preventing push (issue4455)
512 ======================================
530 ======================================
513
531
514 $ hg bookmarks
532 $ hg bookmarks
515 * @ 0:55482a6fb4b1
533 * @ 0:55482a6fb4b1
516 $ hg log -G
534 $ hg log -G
517 @ 0:55482a6fb4b1 initial
535 @ 0:55482a6fb4b1 initial
518
536
519 $ hg init ../issue4455-dest
537 $ hg init ../issue4455-dest
520 $ hg push ../issue4455-dest # changesets only
538 $ hg push ../issue4455-dest # changesets only
521 pushing to ../issue4455-dest
539 pushing to ../issue4455-dest
522 searching for changes
540 searching for changes
523 adding changesets
541 adding changesets
524 adding manifests
542 adding manifests
525 adding file changes
543 adding file changes
526 added 1 changesets with 1 changes to 1 files
544 added 1 changesets with 1 changes to 1 files
527 $ cat >> .hg/hgrc << EOF
545 $ cat >> .hg/hgrc << EOF
528 > [paths]
546 > [paths]
529 > local=../issue4455-dest/
547 > local=../issue4455-dest/
530 > ssh=ssh://user@dummy/issue4455-dest
548 > ssh=ssh://user@dummy/issue4455-dest
531 > http=http://localhost:$HGPORT/
549 > http=http://localhost:$HGPORT/
532 > [ui]
550 > [ui]
533 > ssh=python "$TESTDIR/dummyssh"
551 > ssh=python "$TESTDIR/dummyssh"
534 > EOF
552 > EOF
535 $ cat >> ../issue4455-dest/.hg/hgrc << EOF
553 $ cat >> ../issue4455-dest/.hg/hgrc << EOF
536 > [hooks]
554 > [hooks]
537 > prepushkey=false
555 > prepushkey=false
538 > [web]
556 > [web]
539 > push_ssl = false
557 > push_ssl = false
540 > allow_push = *
558 > allow_push = *
541 > EOF
559 > EOF
542 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
560 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
543 $ hg -R ../issue4455-dest serve -p $HGPORT -d --pid-file=../issue4455.pid -E ../issue4455-error.log
561 $ hg -R ../issue4455-dest serve -p $HGPORT -d --pid-file=../issue4455.pid -E ../issue4455-error.log
544 $ cat ../issue4455.pid >> $DAEMON_PIDS
562 $ cat ../issue4455.pid >> $DAEMON_PIDS
545
563
546 Local push
564 Local push
547 ----------
565 ----------
548
566
549 $ hg push -B @ local
567 $ hg push -B @ local
550 pushing to $TESTTMP/issue4455-dest (glob)
568 pushing to $TESTTMP/issue4455-dest (glob)
551 searching for changes
569 searching for changes
552 no changes found
570 no changes found
553 pushkey-abort: prepushkey hook exited with status 1
571 pushkey-abort: prepushkey hook exited with status 1
554 exporting bookmark @ failed!
572 exporting bookmark @ failed!
555 [1]
573 [1]
556 $ hg -R ../issue4455-dest/ bookmarks
574 $ hg -R ../issue4455-dest/ bookmarks
557 no bookmarks set
575 no bookmarks set
558
576
559 Using ssh
577 Using ssh
560 ---------
578 ---------
561
579
562 $ hg push -B @ ssh
580 $ hg push -B @ ssh
563 pushing to ssh://user@dummy/issue4455-dest
581 pushing to ssh://user@dummy/issue4455-dest
564 searching for changes
582 searching for changes
565 no changes found
583 no changes found
566 remote: pushkey-abort: prepushkey hook exited with status 1
584 remote: pushkey-abort: prepushkey hook exited with status 1
567 exporting bookmark @ failed!
585 exporting bookmark @ failed!
568 [1]
586 [1]
569 $ hg -R ../issue4455-dest/ bookmarks
587 $ hg -R ../issue4455-dest/ bookmarks
570 no bookmarks set
588 no bookmarks set
571
589
572 Using http
590 Using http
573 ----------
591 ----------
574
592
575 $ hg push -B @ http
593 $ hg push -B @ http
576 pushing to http://localhost:$HGPORT/
594 pushing to http://localhost:$HGPORT/
577 searching for changes
595 searching for changes
578 no changes found
596 no changes found
579 remote: pushkey-abort: prepushkey hook exited with status 1
597 remote: pushkey-abort: prepushkey hook exited with status 1
580 exporting bookmark @ failed!
598 exporting bookmark @ failed!
581 [1]
599 [1]
582 $ hg -R ../issue4455-dest/ bookmarks
600 $ hg -R ../issue4455-dest/ bookmarks
583 no bookmarks set
601 no bookmarks set
General Comments 0
You need to be logged in to leave comments. Login now