##// END OF EJS Templates
i18n, bookmarks: add comments for translators
Martin Geisler -
r12881:161fe487 stable
parent child Browse files
Show More
@@ -1,571 +1,573 b''
1 # Mercurial extension to provide the 'hg bookmark' command
1 # Mercurial extension to provide the 'hg bookmark' command
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 '''track a line of development with movable markers
8 '''track a line of development with movable markers
9
9
10 Bookmarks are local movable markers to changesets. Every bookmark
10 Bookmarks are local movable markers to changesets. Every bookmark
11 points to a changeset identified by its hash. If you commit a
11 points to a changeset identified by its hash. If you commit a
12 changeset that is based on a changeset that has a bookmark on it, the
12 changeset that is based on a changeset that has a bookmark on it, the
13 bookmark shifts to the new changeset.
13 bookmark shifts to the new changeset.
14
14
15 It is possible to use bookmark names in every revision lookup (e.g.
15 It is possible to use bookmark names in every revision lookup (e.g.
16 :hg:`merge`, :hg:`update`).
16 :hg:`merge`, :hg:`update`).
17
17
18 By default, when several bookmarks point to the same changeset, they
18 By default, when several bookmarks point to the same changeset, they
19 will all move forward together. It is possible to obtain a more
19 will all move forward together. It is possible to obtain a more
20 git-like experience by adding the following configuration option to
20 git-like experience by adding the following configuration option to
21 your configuration file::
21 your configuration file::
22
22
23 [bookmarks]
23 [bookmarks]
24 track.current = True
24 track.current = True
25
25
26 This will cause Mercurial to track the bookmark that you are currently
26 This will cause Mercurial to track the bookmark that you are currently
27 using, and only update it. This is similar to git's approach to
27 using, and only update it. This is similar to git's approach to
28 branching.
28 branching.
29 '''
29 '''
30
30
31 from mercurial.i18n import _
31 from mercurial.i18n import _
32 from mercurial.node import nullid, nullrev, bin, hex, short
32 from mercurial.node import nullid, nullrev, bin, hex, short
33 from mercurial import util, commands, repair, extensions, pushkey, hg, url
33 from mercurial import util, commands, repair, extensions, pushkey, hg, url
34 from mercurial import revset
34 from mercurial import revset
35 import os
35 import os
36
36
37 def write(repo):
37 def write(repo):
38 '''Write bookmarks
38 '''Write bookmarks
39
39
40 Write the given bookmark => hash dictionary to the .hg/bookmarks file
40 Write the given bookmark => hash dictionary to the .hg/bookmarks file
41 in a format equal to those of localtags.
41 in a format equal to those of localtags.
42
42
43 We also store a backup of the previous state in undo.bookmarks that
43 We also store a backup of the previous state in undo.bookmarks that
44 can be copied back on rollback.
44 can be copied back on rollback.
45 '''
45 '''
46 refs = repo._bookmarks
46 refs = repo._bookmarks
47 if os.path.exists(repo.join('bookmarks')):
47 if os.path.exists(repo.join('bookmarks')):
48 util.copyfile(repo.join('bookmarks'), repo.join('undo.bookmarks'))
48 util.copyfile(repo.join('bookmarks'), repo.join('undo.bookmarks'))
49 if repo._bookmarkcurrent not in refs:
49 if repo._bookmarkcurrent not in refs:
50 setcurrent(repo, None)
50 setcurrent(repo, None)
51 wlock = repo.wlock()
51 wlock = repo.wlock()
52 try:
52 try:
53 file = repo.opener('bookmarks', 'w', atomictemp=True)
53 file = repo.opener('bookmarks', 'w', atomictemp=True)
54 for refspec, node in refs.iteritems():
54 for refspec, node in refs.iteritems():
55 file.write("%s %s\n" % (hex(node), refspec))
55 file.write("%s %s\n" % (hex(node), refspec))
56 file.rename()
56 file.rename()
57
57
58 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
58 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
59 try:
59 try:
60 os.utime(repo.sjoin('00changelog.i'), None)
60 os.utime(repo.sjoin('00changelog.i'), None)
61 except OSError:
61 except OSError:
62 pass
62 pass
63
63
64 finally:
64 finally:
65 wlock.release()
65 wlock.release()
66
66
67 def setcurrent(repo, mark):
67 def setcurrent(repo, mark):
68 '''Set the name of the bookmark that we are currently on
68 '''Set the name of the bookmark that we are currently on
69
69
70 Set the name of the bookmark that we are on (hg update <bookmark>).
70 Set the name of the bookmark that we are on (hg update <bookmark>).
71 The name is recorded in .hg/bookmarks.current
71 The name is recorded in .hg/bookmarks.current
72 '''
72 '''
73 current = repo._bookmarkcurrent
73 current = repo._bookmarkcurrent
74 if current == mark:
74 if current == mark:
75 return
75 return
76
76
77 refs = repo._bookmarks
77 refs = repo._bookmarks
78
78
79 # do not update if we do update to a rev equal to the current bookmark
79 # do not update if we do update to a rev equal to the current bookmark
80 if (mark and mark not in refs and
80 if (mark and mark not in refs and
81 current and refs[current] == repo.changectx('.').node()):
81 current and refs[current] == repo.changectx('.').node()):
82 return
82 return
83 if mark not in refs:
83 if mark not in refs:
84 mark = ''
84 mark = ''
85 wlock = repo.wlock()
85 wlock = repo.wlock()
86 try:
86 try:
87 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
87 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
88 file.write(mark)
88 file.write(mark)
89 file.rename()
89 file.rename()
90 finally:
90 finally:
91 wlock.release()
91 wlock.release()
92 repo._bookmarkcurrent = mark
92 repo._bookmarkcurrent = mark
93
93
94 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
94 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
95 '''track a line of development with movable markers
95 '''track a line of development with movable markers
96
96
97 Bookmarks are pointers to certain commits that move when
97 Bookmarks are pointers to certain commits that move when
98 committing. Bookmarks are local. They can be renamed, copied and
98 committing. Bookmarks are local. They can be renamed, copied and
99 deleted. It is possible to use bookmark names in :hg:`merge` and
99 deleted. It is possible to use bookmark names in :hg:`merge` and
100 :hg:`update` to merge and update respectively to a given bookmark.
100 :hg:`update` to merge and update respectively to a given bookmark.
101
101
102 You can use :hg:`bookmark NAME` to set a bookmark on the working
102 You can use :hg:`bookmark NAME` to set a bookmark on the working
103 directory's parent revision with the given name. If you specify
103 directory's parent revision with the given name. If you specify
104 a revision using -r REV (where REV may be an existing bookmark),
104 a revision using -r REV (where REV may be an existing bookmark),
105 the bookmark is assigned to that revision.
105 the bookmark is assigned to that revision.
106
106
107 Bookmarks can be pushed and pulled between repositories (see :hg:`help
107 Bookmarks can be pushed and pulled between repositories (see :hg:`help
108 push` and :hg:`help pull`). This requires the bookmark extension to be
108 push` and :hg:`help pull`). This requires the bookmark extension to be
109 enabled for both the local and remote repositories.
109 enabled for both the local and remote repositories.
110 '''
110 '''
111 hexfn = ui.debugflag and hex or short
111 hexfn = ui.debugflag and hex or short
112 marks = repo._bookmarks
112 marks = repo._bookmarks
113 cur = repo.changectx('.').node()
113 cur = repo.changectx('.').node()
114
114
115 if rename:
115 if rename:
116 if rename not in marks:
116 if rename not in marks:
117 raise util.Abort(_("a bookmark of this name does not exist"))
117 raise util.Abort(_("a bookmark of this name does not exist"))
118 if mark in marks and not force:
118 if mark in marks and not force:
119 raise util.Abort(_("a bookmark of the same name already exists"))
119 raise util.Abort(_("a bookmark of the same name already exists"))
120 if mark is None:
120 if mark is None:
121 raise util.Abort(_("new bookmark name required"))
121 raise util.Abort(_("new bookmark name required"))
122 marks[mark] = marks[rename]
122 marks[mark] = marks[rename]
123 del marks[rename]
123 del marks[rename]
124 if repo._bookmarkcurrent == rename:
124 if repo._bookmarkcurrent == rename:
125 setcurrent(repo, mark)
125 setcurrent(repo, mark)
126 write(repo)
126 write(repo)
127 return
127 return
128
128
129 if delete:
129 if delete:
130 if mark is None:
130 if mark is None:
131 raise util.Abort(_("bookmark name required"))
131 raise util.Abort(_("bookmark name required"))
132 if mark not in marks:
132 if mark not in marks:
133 raise util.Abort(_("a bookmark of this name does not exist"))
133 raise util.Abort(_("a bookmark of this name does not exist"))
134 if mark == repo._bookmarkcurrent:
134 if mark == repo._bookmarkcurrent:
135 setcurrent(repo, None)
135 setcurrent(repo, None)
136 del marks[mark]
136 del marks[mark]
137 write(repo)
137 write(repo)
138 return
138 return
139
139
140 if mark != None:
140 if mark != None:
141 if "\n" in mark:
141 if "\n" in mark:
142 raise util.Abort(_("bookmark name cannot contain newlines"))
142 raise util.Abort(_("bookmark name cannot contain newlines"))
143 mark = mark.strip()
143 mark = mark.strip()
144 if not mark:
144 if not mark:
145 raise util.Abort(_("bookmark names cannot consist entirely of "
145 raise util.Abort(_("bookmark names cannot consist entirely of "
146 "whitespace"))
146 "whitespace"))
147 if mark in marks and not force:
147 if mark in marks and not force:
148 raise util.Abort(_("a bookmark of the same name already exists"))
148 raise util.Abort(_("a bookmark of the same name already exists"))
149 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
149 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
150 and not force):
150 and not force):
151 raise util.Abort(
151 raise util.Abort(
152 _("a bookmark cannot have the name of an existing branch"))
152 _("a bookmark cannot have the name of an existing branch"))
153 if rev:
153 if rev:
154 marks[mark] = repo.lookup(rev)
154 marks[mark] = repo.lookup(rev)
155 else:
155 else:
156 marks[mark] = repo.changectx('.').node()
156 marks[mark] = repo.changectx('.').node()
157 setcurrent(repo, mark)
157 setcurrent(repo, mark)
158 write(repo)
158 write(repo)
159 return
159 return
160
160
161 if mark is None:
161 if mark is None:
162 if rev:
162 if rev:
163 raise util.Abort(_("bookmark name required"))
163 raise util.Abort(_("bookmark name required"))
164 if len(marks) == 0:
164 if len(marks) == 0:
165 ui.status(_("no bookmarks set\n"))
165 ui.status(_("no bookmarks set\n"))
166 else:
166 else:
167 for bmark, n in marks.iteritems():
167 for bmark, n in marks.iteritems():
168 if ui.configbool('bookmarks', 'track.current'):
168 if ui.configbool('bookmarks', 'track.current'):
169 current = repo._bookmarkcurrent
169 current = repo._bookmarkcurrent
170 if bmark == current and n == cur:
170 if bmark == current and n == cur:
171 prefix, label = '*', 'bookmarks.current'
171 prefix, label = '*', 'bookmarks.current'
172 else:
172 else:
173 prefix, label = ' ', ''
173 prefix, label = ' ', ''
174 else:
174 else:
175 if n == cur:
175 if n == cur:
176 prefix, label = '*', 'bookmarks.current'
176 prefix, label = '*', 'bookmarks.current'
177 else:
177 else:
178 prefix, label = ' ', ''
178 prefix, label = ' ', ''
179
179
180 if ui.quiet:
180 if ui.quiet:
181 ui.write("%s\n" % bmark, label=label)
181 ui.write("%s\n" % bmark, label=label)
182 else:
182 else:
183 ui.write(" %s %-25s %d:%s\n" % (
183 ui.write(" %s %-25s %d:%s\n" % (
184 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
184 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
185 label=label)
185 label=label)
186 return
186 return
187
187
188 def _revstostrip(changelog, node):
188 def _revstostrip(changelog, node):
189 srev = changelog.rev(node)
189 srev = changelog.rev(node)
190 tostrip = [srev]
190 tostrip = [srev]
191 saveheads = []
191 saveheads = []
192 for r in xrange(srev, len(changelog)):
192 for r in xrange(srev, len(changelog)):
193 parents = changelog.parentrevs(r)
193 parents = changelog.parentrevs(r)
194 if parents[0] in tostrip or parents[1] in tostrip:
194 if parents[0] in tostrip or parents[1] in tostrip:
195 tostrip.append(r)
195 tostrip.append(r)
196 if parents[1] != nullrev:
196 if parents[1] != nullrev:
197 for p in parents:
197 for p in parents:
198 if p not in tostrip and p > srev:
198 if p not in tostrip and p > srev:
199 saveheads.append(p)
199 saveheads.append(p)
200 return [r for r in tostrip if r not in saveheads]
200 return [r for r in tostrip if r not in saveheads]
201
201
202 def strip(oldstrip, ui, repo, node, backup="all"):
202 def strip(oldstrip, ui, repo, node, backup="all"):
203 """Strip bookmarks if revisions are stripped using
203 """Strip bookmarks if revisions are stripped using
204 the mercurial.strip method. This usually happens during
204 the mercurial.strip method. This usually happens during
205 qpush and qpop"""
205 qpush and qpop"""
206 revisions = _revstostrip(repo.changelog, node)
206 revisions = _revstostrip(repo.changelog, node)
207 marks = repo._bookmarks
207 marks = repo._bookmarks
208 update = []
208 update = []
209 for mark, n in marks.iteritems():
209 for mark, n in marks.iteritems():
210 if repo.changelog.rev(n) in revisions:
210 if repo.changelog.rev(n) in revisions:
211 update.append(mark)
211 update.append(mark)
212 oldstrip(ui, repo, node, backup)
212 oldstrip(ui, repo, node, backup)
213 if len(update) > 0:
213 if len(update) > 0:
214 for m in update:
214 for m in update:
215 marks[m] = repo.changectx('.').node()
215 marks[m] = repo.changectx('.').node()
216 write(repo)
216 write(repo)
217
217
218 def reposetup(ui, repo):
218 def reposetup(ui, repo):
219 if not repo.local():
219 if not repo.local():
220 return
220 return
221
221
222 class bookmark_repo(repo.__class__):
222 class bookmark_repo(repo.__class__):
223
223
224 @util.propertycache
224 @util.propertycache
225 def _bookmarks(self):
225 def _bookmarks(self):
226 '''Parse .hg/bookmarks file and return a dictionary
226 '''Parse .hg/bookmarks file and return a dictionary
227
227
228 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
228 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
229 in the .hg/bookmarks file.
229 in the .hg/bookmarks file.
230 Read the file and return a (name=>nodeid) dictionary
230 Read the file and return a (name=>nodeid) dictionary
231 '''
231 '''
232 try:
232 try:
233 bookmarks = {}
233 bookmarks = {}
234 for line in self.opener('bookmarks'):
234 for line in self.opener('bookmarks'):
235 sha, refspec = line.strip().split(' ', 1)
235 sha, refspec = line.strip().split(' ', 1)
236 bookmarks[refspec] = self.changelog.lookup(sha)
236 bookmarks[refspec] = self.changelog.lookup(sha)
237 except:
237 except:
238 pass
238 pass
239 return bookmarks
239 return bookmarks
240
240
241 @util.propertycache
241 @util.propertycache
242 def _bookmarkcurrent(self):
242 def _bookmarkcurrent(self):
243 '''Get the current bookmark
243 '''Get the current bookmark
244
244
245 If we use gittishsh branches we have a current bookmark that
245 If we use gittishsh branches we have a current bookmark that
246 we are on. This function returns the name of the bookmark. It
246 we are on. This function returns the name of the bookmark. It
247 is stored in .hg/bookmarks.current
247 is stored in .hg/bookmarks.current
248 '''
248 '''
249 mark = None
249 mark = None
250 if os.path.exists(self.join('bookmarks.current')):
250 if os.path.exists(self.join('bookmarks.current')):
251 file = self.opener('bookmarks.current')
251 file = self.opener('bookmarks.current')
252 # No readline() in posixfile_nt, reading everything is cheap
252 # No readline() in posixfile_nt, reading everything is cheap
253 mark = (file.readlines() or [''])[0]
253 mark = (file.readlines() or [''])[0]
254 if mark == '':
254 if mark == '':
255 mark = None
255 mark = None
256 file.close()
256 file.close()
257 return mark
257 return mark
258
258
259 def rollback(self, *args):
259 def rollback(self, *args):
260 if os.path.exists(self.join('undo.bookmarks')):
260 if os.path.exists(self.join('undo.bookmarks')):
261 util.rename(self.join('undo.bookmarks'), self.join('bookmarks'))
261 util.rename(self.join('undo.bookmarks'), self.join('bookmarks'))
262 return super(bookmark_repo, self).rollback(*args)
262 return super(bookmark_repo, self).rollback(*args)
263
263
264 def lookup(self, key):
264 def lookup(self, key):
265 if key in self._bookmarks:
265 if key in self._bookmarks:
266 key = self._bookmarks[key]
266 key = self._bookmarks[key]
267 return super(bookmark_repo, self).lookup(key)
267 return super(bookmark_repo, self).lookup(key)
268
268
269 def _bookmarksupdate(self, parents, node):
269 def _bookmarksupdate(self, parents, node):
270 marks = self._bookmarks
270 marks = self._bookmarks
271 update = False
271 update = False
272 if ui.configbool('bookmarks', 'track.current'):
272 if ui.configbool('bookmarks', 'track.current'):
273 mark = self._bookmarkcurrent
273 mark = self._bookmarkcurrent
274 if mark and marks[mark] in parents:
274 if mark and marks[mark] in parents:
275 marks[mark] = node
275 marks[mark] = node
276 update = True
276 update = True
277 else:
277 else:
278 for mark, n in marks.items():
278 for mark, n in marks.items():
279 if n in parents:
279 if n in parents:
280 marks[mark] = node
280 marks[mark] = node
281 update = True
281 update = True
282 if update:
282 if update:
283 write(self)
283 write(self)
284
284
285 def commitctx(self, ctx, error=False):
285 def commitctx(self, ctx, error=False):
286 """Add a revision to the repository and
286 """Add a revision to the repository and
287 move the bookmark"""
287 move the bookmark"""
288 wlock = self.wlock() # do both commit and bookmark with lock held
288 wlock = self.wlock() # do both commit and bookmark with lock held
289 try:
289 try:
290 node = super(bookmark_repo, self).commitctx(ctx, error)
290 node = super(bookmark_repo, self).commitctx(ctx, error)
291 if node is None:
291 if node is None:
292 return None
292 return None
293 parents = self.changelog.parents(node)
293 parents = self.changelog.parents(node)
294 if parents[1] == nullid:
294 if parents[1] == nullid:
295 parents = (parents[0],)
295 parents = (parents[0],)
296
296
297 self._bookmarksupdate(parents, node)
297 self._bookmarksupdate(parents, node)
298 return node
298 return node
299 finally:
299 finally:
300 wlock.release()
300 wlock.release()
301
301
302 def pull(self, remote, heads=None, force=False):
302 def pull(self, remote, heads=None, force=False):
303 result = super(bookmark_repo, self).pull(remote, heads, force)
303 result = super(bookmark_repo, self).pull(remote, heads, force)
304
304
305 self.ui.debug("checking for updated bookmarks\n")
305 self.ui.debug("checking for updated bookmarks\n")
306 rb = remote.listkeys('bookmarks')
306 rb = remote.listkeys('bookmarks')
307 changed = False
307 changed = False
308 for k in rb.keys():
308 for k in rb.keys():
309 if k in self._bookmarks:
309 if k in self._bookmarks:
310 nr, nl = rb[k], self._bookmarks[k]
310 nr, nl = rb[k], self._bookmarks[k]
311 if nr in self:
311 if nr in self:
312 cr = self[nr]
312 cr = self[nr]
313 cl = self[nl]
313 cl = self[nl]
314 if cl.rev() >= cr.rev():
314 if cl.rev() >= cr.rev():
315 continue
315 continue
316 if cr in cl.descendants():
316 if cr in cl.descendants():
317 self._bookmarks[k] = cr.node()
317 self._bookmarks[k] = cr.node()
318 changed = True
318 changed = True
319 self.ui.status(_("updating bookmark %s\n") % k)
319 self.ui.status(_("updating bookmark %s\n") % k)
320 else:
320 else:
321 self.ui.warn(_("not updating divergent"
321 self.ui.warn(_("not updating divergent"
322 " bookmark %s\n") % k)
322 " bookmark %s\n") % k)
323 if changed:
323 if changed:
324 write(repo)
324 write(repo)
325
325
326 return result
326 return result
327
327
328 def push(self, remote, force=False, revs=None, newbranch=False):
328 def push(self, remote, force=False, revs=None, newbranch=False):
329 result = super(bookmark_repo, self).push(remote, force, revs,
329 result = super(bookmark_repo, self).push(remote, force, revs,
330 newbranch)
330 newbranch)
331
331
332 self.ui.debug("checking for updated bookmarks\n")
332 self.ui.debug("checking for updated bookmarks\n")
333 rb = remote.listkeys('bookmarks')
333 rb = remote.listkeys('bookmarks')
334 for k in rb.keys():
334 for k in rb.keys():
335 if k in self._bookmarks:
335 if k in self._bookmarks:
336 nr, nl = rb[k], self._bookmarks[k]
336 nr, nl = rb[k], self._bookmarks[k]
337 if nr in self:
337 if nr in self:
338 cr = self[nr]
338 cr = self[nr]
339 cl = self[nl]
339 cl = self[nl]
340 if cl in cr.descendants():
340 if cl in cr.descendants():
341 r = remote.pushkey('bookmarks', k, nr, nl)
341 r = remote.pushkey('bookmarks', k, nr, nl)
342 if r:
342 if r:
343 self.ui.status(_("updating bookmark %s\n") % k)
343 self.ui.status(_("updating bookmark %s\n") % k)
344 else:
344 else:
345 self.ui.warn(_('updating bookmark %s'
345 self.ui.warn(_('updating bookmark %s'
346 ' failed!\n') % k)
346 ' failed!\n') % k)
347
347
348 return result
348 return result
349
349
350 def addchangegroup(self, *args, **kwargs):
350 def addchangegroup(self, *args, **kwargs):
351 parents = self.dirstate.parents()
351 parents = self.dirstate.parents()
352
352
353 result = super(bookmark_repo, self).addchangegroup(*args, **kwargs)
353 result = super(bookmark_repo, self).addchangegroup(*args, **kwargs)
354 if result > 1:
354 if result > 1:
355 # We have more heads than before
355 # We have more heads than before
356 return result
356 return result
357 node = self.changelog.tip()
357 node = self.changelog.tip()
358
358
359 self._bookmarksupdate(parents, node)
359 self._bookmarksupdate(parents, node)
360 return result
360 return result
361
361
362 def _findtags(self):
362 def _findtags(self):
363 """Merge bookmarks with normal tags"""
363 """Merge bookmarks with normal tags"""
364 (tags, tagtypes) = super(bookmark_repo, self)._findtags()
364 (tags, tagtypes) = super(bookmark_repo, self)._findtags()
365 tags.update(self._bookmarks)
365 tags.update(self._bookmarks)
366 return (tags, tagtypes)
366 return (tags, tagtypes)
367
367
368 if hasattr(repo, 'invalidate'):
368 if hasattr(repo, 'invalidate'):
369 def invalidate(self):
369 def invalidate(self):
370 super(bookmark_repo, self).invalidate()
370 super(bookmark_repo, self).invalidate()
371 for attr in ('_bookmarks', '_bookmarkcurrent'):
371 for attr in ('_bookmarks', '_bookmarkcurrent'):
372 if attr in self.__dict__:
372 if attr in self.__dict__:
373 delattr(self, attr)
373 delattr(self, attr)
374
374
375 repo.__class__ = bookmark_repo
375 repo.__class__ = bookmark_repo
376
376
377 def listbookmarks(repo):
377 def listbookmarks(repo):
378 # We may try to list bookmarks on a repo type that does not
378 # We may try to list bookmarks on a repo type that does not
379 # support it (e.g., statichttprepository).
379 # support it (e.g., statichttprepository).
380 if not hasattr(repo, '_bookmarks'):
380 if not hasattr(repo, '_bookmarks'):
381 return {}
381 return {}
382
382
383 d = {}
383 d = {}
384 for k, v in repo._bookmarks.iteritems():
384 for k, v in repo._bookmarks.iteritems():
385 d[k] = hex(v)
385 d[k] = hex(v)
386 return d
386 return d
387
387
388 def pushbookmark(repo, key, old, new):
388 def pushbookmark(repo, key, old, new):
389 w = repo.wlock()
389 w = repo.wlock()
390 try:
390 try:
391 marks = repo._bookmarks
391 marks = repo._bookmarks
392 if hex(marks.get(key, '')) != old:
392 if hex(marks.get(key, '')) != old:
393 return False
393 return False
394 if new == '':
394 if new == '':
395 del marks[key]
395 del marks[key]
396 else:
396 else:
397 if new not in repo:
397 if new not in repo:
398 return False
398 return False
399 marks[key] = repo[new].node()
399 marks[key] = repo[new].node()
400 write(repo)
400 write(repo)
401 return True
401 return True
402 finally:
402 finally:
403 w.release()
403 w.release()
404
404
405 def pull(oldpull, ui, repo, source="default", **opts):
405 def pull(oldpull, ui, repo, source="default", **opts):
406 # translate bookmark args to rev args for actual pull
406 # translate bookmark args to rev args for actual pull
407 if opts.get('bookmark'):
407 if opts.get('bookmark'):
408 # this is an unpleasant hack as pull will do this internally
408 # this is an unpleasant hack as pull will do this internally
409 source, branches = hg.parseurl(ui.expandpath(source),
409 source, branches = hg.parseurl(ui.expandpath(source),
410 opts.get('branch'))
410 opts.get('branch'))
411 other = hg.repository(hg.remoteui(repo, opts), source)
411 other = hg.repository(hg.remoteui(repo, opts), source)
412 rb = other.listkeys('bookmarks')
412 rb = other.listkeys('bookmarks')
413
413
414 for b in opts['bookmark']:
414 for b in opts['bookmark']:
415 if b not in rb:
415 if b not in rb:
416 raise util.Abort(_('remote bookmark %s not found!') % b)
416 raise util.Abort(_('remote bookmark %s not found!') % b)
417 opts.setdefault('rev', []).append(b)
417 opts.setdefault('rev', []).append(b)
418
418
419 result = oldpull(ui, repo, source, **opts)
419 result = oldpull(ui, repo, source, **opts)
420
420
421 # update specified bookmarks
421 # update specified bookmarks
422 if opts.get('bookmark'):
422 if opts.get('bookmark'):
423 for b in opts['bookmark']:
423 for b in opts['bookmark']:
424 # explicit pull overrides local bookmark if any
424 # explicit pull overrides local bookmark if any
425 ui.status(_("importing bookmark %s\n") % b)
425 ui.status(_("importing bookmark %s\n") % b)
426 repo._bookmarks[b] = repo[rb[b]].node()
426 repo._bookmarks[b] = repo[rb[b]].node()
427 write(repo)
427 write(repo)
428
428
429 return result
429 return result
430
430
431 def push(oldpush, ui, repo, dest=None, **opts):
431 def push(oldpush, ui, repo, dest=None, **opts):
432 dopush = True
432 dopush = True
433 if opts.get('bookmark'):
433 if opts.get('bookmark'):
434 dopush = False
434 dopush = False
435 for b in opts['bookmark']:
435 for b in opts['bookmark']:
436 if b in repo._bookmarks:
436 if b in repo._bookmarks:
437 dopush = True
437 dopush = True
438 opts.setdefault('rev', []).append(b)
438 opts.setdefault('rev', []).append(b)
439
439
440 result = 0
440 result = 0
441 if dopush:
441 if dopush:
442 result = oldpush(ui, repo, dest, **opts)
442 result = oldpush(ui, repo, dest, **opts)
443
443
444 if opts.get('bookmark'):
444 if opts.get('bookmark'):
445 # this is an unpleasant hack as push will do this internally
445 # this is an unpleasant hack as push will do this internally
446 dest = ui.expandpath(dest or 'default-push', dest or 'default')
446 dest = ui.expandpath(dest or 'default-push', dest or 'default')
447 dest, branches = hg.parseurl(dest, opts.get('branch'))
447 dest, branches = hg.parseurl(dest, opts.get('branch'))
448 other = hg.repository(hg.remoteui(repo, opts), dest)
448 other = hg.repository(hg.remoteui(repo, opts), dest)
449 rb = other.listkeys('bookmarks')
449 rb = other.listkeys('bookmarks')
450 for b in opts['bookmark']:
450 for b in opts['bookmark']:
451 # explicit push overrides remote bookmark if any
451 # explicit push overrides remote bookmark if any
452 if b in repo._bookmarks:
452 if b in repo._bookmarks:
453 ui.status(_("exporting bookmark %s\n") % b)
453 ui.status(_("exporting bookmark %s\n") % b)
454 new = repo[b].hex()
454 new = repo[b].hex()
455 elif b in rb:
455 elif b in rb:
456 ui.status(_("deleting remote bookmark %s\n") % b)
456 ui.status(_("deleting remote bookmark %s\n") % b)
457 new = '' # delete
457 new = '' # delete
458 else:
458 else:
459 ui.warn(_('bookmark %s does not exist on the local '
459 ui.warn(_('bookmark %s does not exist on the local '
460 'or remote repository!\n') % b)
460 'or remote repository!\n') % b)
461 return 2
461 return 2
462 old = rb.get(b, '')
462 old = rb.get(b, '')
463 r = other.pushkey('bookmarks', b, old, new)
463 r = other.pushkey('bookmarks', b, old, new)
464 if not r:
464 if not r:
465 ui.warn(_('updating bookmark %s failed!\n') % b)
465 ui.warn(_('updating bookmark %s failed!\n') % b)
466 if not result:
466 if not result:
467 result = 2
467 result = 2
468
468
469 return result
469 return result
470
470
471 def diffbookmarks(ui, repo, remote):
471 def diffbookmarks(ui, repo, remote):
472 ui.status(_("searching for changed bookmarks\n"))
472 ui.status(_("searching for changed bookmarks\n"))
473
473
474 lmarks = repo.listkeys('bookmarks')
474 lmarks = repo.listkeys('bookmarks')
475 rmarks = remote.listkeys('bookmarks')
475 rmarks = remote.listkeys('bookmarks')
476
476
477 diff = sorted(set(rmarks) - set(lmarks))
477 diff = sorted(set(rmarks) - set(lmarks))
478 for k in diff:
478 for k in diff:
479 ui.write(" %-25s %s\n" % (k, rmarks[k][:12]))
479 ui.write(" %-25s %s\n" % (k, rmarks[k][:12]))
480
480
481 if len(diff) <= 0:
481 if len(diff) <= 0:
482 ui.status(_("no changed bookmarks found\n"))
482 ui.status(_("no changed bookmarks found\n"))
483 return 1
483 return 1
484 return 0
484 return 0
485
485
486 def incoming(oldincoming, ui, repo, source="default", **opts):
486 def incoming(oldincoming, ui, repo, source="default", **opts):
487 if opts.get('bookmarks'):
487 if opts.get('bookmarks'):
488 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
488 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
489 other = hg.repository(hg.remoteui(repo, opts), source)
489 other = hg.repository(hg.remoteui(repo, opts), source)
490 ui.status(_('comparing with %s\n') % url.hidepassword(source))
490 ui.status(_('comparing with %s\n') % url.hidepassword(source))
491 return diffbookmarks(ui, repo, other)
491 return diffbookmarks(ui, repo, other)
492 else:
492 else:
493 return oldincoming(ui, repo, source, **opts)
493 return oldincoming(ui, repo, source, **opts)
494
494
495 def outgoing(oldoutgoing, ui, repo, dest=None, **opts):
495 def outgoing(oldoutgoing, ui, repo, dest=None, **opts):
496 if opts.get('bookmarks'):
496 if opts.get('bookmarks'):
497 dest = ui.expandpath(dest or 'default-push', dest or 'default')
497 dest = ui.expandpath(dest or 'default-push', dest or 'default')
498 dest, branches = hg.parseurl(dest, opts.get('branch'))
498 dest, branches = hg.parseurl(dest, opts.get('branch'))
499 other = hg.repository(hg.remoteui(repo, opts), dest)
499 other = hg.repository(hg.remoteui(repo, opts), dest)
500 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
500 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
501 return diffbookmarks(ui, other, repo)
501 return diffbookmarks(ui, other, repo)
502 else:
502 else:
503 return oldoutgoing(ui, repo, dest, **opts)
503 return oldoutgoing(ui, repo, dest, **opts)
504
504
505 def uisetup(ui):
505 def uisetup(ui):
506 extensions.wrapfunction(repair, "strip", strip)
506 extensions.wrapfunction(repair, "strip", strip)
507 if ui.configbool('bookmarks', 'track.current'):
507 if ui.configbool('bookmarks', 'track.current'):
508 extensions.wrapcommand(commands.table, 'update', updatecurbookmark)
508 extensions.wrapcommand(commands.table, 'update', updatecurbookmark)
509
509
510 entry = extensions.wrapcommand(commands.table, 'pull', pull)
510 entry = extensions.wrapcommand(commands.table, 'pull', pull)
511 entry[1].append(('B', 'bookmark', [],
511 entry[1].append(('B', 'bookmark', [],
512 _("bookmark to import"),
512 _("bookmark to import"),
513 _('BOOKMARK')))
513 _('BOOKMARK')))
514 entry = extensions.wrapcommand(commands.table, 'push', push)
514 entry = extensions.wrapcommand(commands.table, 'push', push)
515 entry[1].append(('B', 'bookmark', [],
515 entry[1].append(('B', 'bookmark', [],
516 _("bookmark to export"),
516 _("bookmark to export"),
517 _('BOOKMARK')))
517 _('BOOKMARK')))
518 entry = extensions.wrapcommand(commands.table, 'incoming', incoming)
518 entry = extensions.wrapcommand(commands.table, 'incoming', incoming)
519 entry[1].append(('B', 'bookmarks', False,
519 entry[1].append(('B', 'bookmarks', False,
520 _("compare bookmark")))
520 _("compare bookmark")))
521 entry = extensions.wrapcommand(commands.table, 'outgoing', outgoing)
521 entry = extensions.wrapcommand(commands.table, 'outgoing', outgoing)
522 entry[1].append(('B', 'bookmarks', False,
522 entry[1].append(('B', 'bookmarks', False,
523 _("compare bookmark")))
523 _("compare bookmark")))
524
524
525 pushkey.register('bookmarks', pushbookmark, listbookmarks)
525 pushkey.register('bookmarks', pushbookmark, listbookmarks)
526
526
527 def updatecurbookmark(orig, ui, repo, *args, **opts):
527 def updatecurbookmark(orig, ui, repo, *args, **opts):
528 '''Set the current bookmark
528 '''Set the current bookmark
529
529
530 If the user updates to a bookmark we update the .hg/bookmarks.current
530 If the user updates to a bookmark we update the .hg/bookmarks.current
531 file.
531 file.
532 '''
532 '''
533 res = orig(ui, repo, *args, **opts)
533 res = orig(ui, repo, *args, **opts)
534 rev = opts['rev']
534 rev = opts['rev']
535 if not rev and len(args) > 0:
535 if not rev and len(args) > 0:
536 rev = args[0]
536 rev = args[0]
537 setcurrent(repo, rev)
537 setcurrent(repo, rev)
538 return res
538 return res
539
539
540 def bmrevset(repo, subset, x):
540 def bmrevset(repo, subset, x):
541 """``bookmark([name])``
541 """``bookmark([name])``
542 The named bookmark or all bookmarks.
542 The named bookmark or all bookmarks.
543 """
543 """
544 # i18n: "bookmark" is a keyword
544 args = revset.getargs(x, 0, 1, _('bookmark takes one or no arguments'))
545 args = revset.getargs(x, 0, 1, _('bookmark takes one or no arguments'))
545 if args:
546 if args:
546 bm = revset.getstring(args[0],
547 bm = revset.getstring(args[0],
548 # i18n: "bookmark" is a keyword
547 _('the argument to bookmark must be a string'))
549 _('the argument to bookmark must be a string'))
548 bmrev = listbookmarks(repo).get(bm, None)
550 bmrev = listbookmarks(repo).get(bm, None)
549 if bmrev:
551 if bmrev:
550 bmrev = repo.changelog.rev(bin(bmrev))
552 bmrev = repo.changelog.rev(bin(bmrev))
551 return [r for r in subset if r == bmrev]
553 return [r for r in subset if r == bmrev]
552 bms = set([repo.changelog.rev(bin(r)) for r in listbookmarks(repo).values()])
554 bms = set([repo.changelog.rev(bin(r)) for r in listbookmarks(repo).values()])
553 return [r for r in subset if r in bms]
555 return [r for r in subset if r in bms]
554
556
555 def extsetup(ui):
557 def extsetup(ui):
556 revset.symbols['bookmark'] = bmrevset
558 revset.symbols['bookmark'] = bmrevset
557
559
558 cmdtable = {
560 cmdtable = {
559 "bookmarks":
561 "bookmarks":
560 (bookmark,
562 (bookmark,
561 [('f', 'force', False, _('force')),
563 [('f', 'force', False, _('force')),
562 ('r', 'rev', '', _('revision'), _('REV')),
564 ('r', 'rev', '', _('revision'), _('REV')),
563 ('d', 'delete', False, _('delete a given bookmark')),
565 ('d', 'delete', False, _('delete a given bookmark')),
564 ('m', 'rename', '', _('rename a given bookmark'), _('NAME'))],
566 ('m', 'rename', '', _('rename a given bookmark'), _('NAME'))],
565 _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
567 _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
566 }
568 }
567
569
568 colortable = {'bookmarks.current': 'green'}
570 colortable = {'bookmarks.current': 'green'}
569
571
570 # tell hggettext to extract docstrings from these functions:
572 # tell hggettext to extract docstrings from these functions:
571 i18nfunctions = [bmrevset]
573 i18nfunctions = [bmrevset]
General Comments 0
You need to be logged in to leave comments. Login now