##// END OF EJS Templates
Spell Mercurial as a proper noun
timeless -
r8760:bf17aeaf default
parent child Browse files
Show More
@@ -1,322 +1,322 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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 '''mercurial bookmarks
8 '''Mercurial bookmarks
9
9
10 Mercurial bookmarks are local moveable pointers to changesets. Every
10 Mercurial bookmarks are local moveable pointers to changesets. Every
11 bookmark points to a changeset identified by its hash. If you commit a
11 bookmark 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 is forwarded to the new changeset.
13 bookmark is forwarded to the new changeset.
14
14
15 It is possible to use bookmark names in every revision lookup (e.g. hg
15 It is possible to use bookmark names in every revision lookup (e.g. hg
16 merge, hg update).
16 merge, hg update).
17
17
18 The bookmark extension offers the possiblity to have a more git-like
18 The bookmark extension offers the possiblity to have a more git-like
19 experience by adding the following configuration option to your .hgrc:
19 experience by adding the following configuration option to your .hgrc:
20
20
21 [bookmarks]
21 [bookmarks]
22 track.current = True
22 track.current = True
23
23
24 This will cause bookmarks to track the bookmark that you are currently
24 This will cause bookmarks to track the bookmark that you are currently
25 on, and just updates it. This is similar to git's approach of
25 on, and just updates it. This is similar to git's approach of
26 branching.
26 branching.
27 '''
27 '''
28
28
29 from mercurial.i18n import _
29 from mercurial.i18n import _
30 from mercurial.node import nullid, nullrev, hex, short
30 from mercurial.node import nullid, nullrev, hex, short
31 from mercurial import util, commands, localrepo, repair, extensions
31 from mercurial import util, commands, localrepo, repair, extensions
32 import os
32 import os
33
33
34 def parse(repo):
34 def parse(repo):
35 '''Parse .hg/bookmarks file and return a dictionary
35 '''Parse .hg/bookmarks file and return a dictionary
36
36
37 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
37 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
38 in the .hg/bookmarks file. They are read by the parse() method and
38 in the .hg/bookmarks file. They are read by the parse() method and
39 returned as a dictionary with name => hash values.
39 returned as a dictionary with name => hash values.
40
40
41 The parsed dictionary is cached until a write() operation is done.
41 The parsed dictionary is cached until a write() operation is done.
42 '''
42 '''
43 try:
43 try:
44 if repo._bookmarks:
44 if repo._bookmarks:
45 return repo._bookmarks
45 return repo._bookmarks
46 repo._bookmarks = {}
46 repo._bookmarks = {}
47 for line in repo.opener('bookmarks'):
47 for line in repo.opener('bookmarks'):
48 sha, refspec = line.strip().split(' ', 1)
48 sha, refspec = line.strip().split(' ', 1)
49 repo._bookmarks[refspec] = repo.lookup(sha)
49 repo._bookmarks[refspec] = repo.lookup(sha)
50 except:
50 except:
51 pass
51 pass
52 return repo._bookmarks
52 return repo._bookmarks
53
53
54 def write(repo, refs):
54 def write(repo, refs):
55 '''Write bookmarks
55 '''Write bookmarks
56
56
57 Write the given bookmark => hash dictionary to the .hg/bookmarks file
57 Write the given bookmark => hash dictionary to the .hg/bookmarks file
58 in a format equal to those of localtags.
58 in a format equal to those of localtags.
59
59
60 We also store a backup of the previous state in undo.bookmarks that
60 We also store a backup of the previous state in undo.bookmarks that
61 can be copied back on rollback.
61 can be copied back on rollback.
62 '''
62 '''
63 if os.path.exists(repo.join('bookmarks')):
63 if os.path.exists(repo.join('bookmarks')):
64 util.copyfile(repo.join('bookmarks'), repo.join('undo.bookmarks'))
64 util.copyfile(repo.join('bookmarks'), repo.join('undo.bookmarks'))
65 if current(repo) not in refs:
65 if current(repo) not in refs:
66 setcurrent(repo, None)
66 setcurrent(repo, None)
67 file = repo.opener('bookmarks', 'w+')
67 file = repo.opener('bookmarks', 'w+')
68 for refspec, node in refs.iteritems():
68 for refspec, node in refs.iteritems():
69 file.write("%s %s\n" % (hex(node), refspec))
69 file.write("%s %s\n" % (hex(node), refspec))
70 file.close()
70 file.close()
71
71
72 def current(repo):
72 def current(repo):
73 '''Get the current bookmark
73 '''Get the current bookmark
74
74
75 If we use gittishsh branches we have a current bookmark that
75 If we use gittishsh branches we have a current bookmark that
76 we are on. This function returns the name of the bookmark. It
76 we are on. This function returns the name of the bookmark. It
77 is stored in .hg/bookmarks.current
77 is stored in .hg/bookmarks.current
78 '''
78 '''
79 if repo._bookmarkcurrent:
79 if repo._bookmarkcurrent:
80 return repo._bookmarkcurrent
80 return repo._bookmarkcurrent
81 mark = None
81 mark = None
82 if os.path.exists(repo.join('bookmarks.current')):
82 if os.path.exists(repo.join('bookmarks.current')):
83 file = repo.opener('bookmarks.current')
83 file = repo.opener('bookmarks.current')
84 # No readline() in posixfile_nt, reading everything is cheap
84 # No readline() in posixfile_nt, reading everything is cheap
85 mark = (file.readlines() or [''])[0]
85 mark = (file.readlines() or [''])[0]
86 if mark == '':
86 if mark == '':
87 mark = None
87 mark = None
88 file.close()
88 file.close()
89 repo._bookmarkcurrent = mark
89 repo._bookmarkcurrent = mark
90 return mark
90 return mark
91
91
92 def setcurrent(repo, mark):
92 def setcurrent(repo, mark):
93 '''Set the name of the bookmark that we are currently on
93 '''Set the name of the bookmark that we are currently on
94
94
95 Set the name of the bookmark that we are on (hg update <bookmark>).
95 Set the name of the bookmark that we are on (hg update <bookmark>).
96 The name is recorded in .hg/bookmarks.current
96 The name is recorded in .hg/bookmarks.current
97 '''
97 '''
98 if current(repo) == mark:
98 if current(repo) == mark:
99 return
99 return
100
100
101 refs = parse(repo)
101 refs = parse(repo)
102
102
103 # do not update if we do update to a rev equal to the current bookmark
103 # do not update if we do update to a rev equal to the current bookmark
104 if (mark and mark not in refs and
104 if (mark and mark not in refs and
105 current(repo) and refs[current(repo)] == repo.changectx('.').node()):
105 current(repo) and refs[current(repo)] == repo.changectx('.').node()):
106 return
106 return
107 if mark not in refs:
107 if mark not in refs:
108 mark = ''
108 mark = ''
109 file = repo.opener('bookmarks.current', 'w+')
109 file = repo.opener('bookmarks.current', 'w+')
110 file.write(mark)
110 file.write(mark)
111 file.close()
111 file.close()
112 repo._bookmarkcurrent = mark
112 repo._bookmarkcurrent = mark
113
113
114 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
114 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None):
115 '''mercurial bookmarks
115 '''Mercurial bookmarks
116
116
117 Bookmarks are pointers to certain commits that move when
117 Bookmarks are pointers to certain commits that move when
118 commiting. Bookmarks are local. They can be renamed, copied and
118 commiting. Bookmarks are local. They can be renamed, copied and
119 deleted. It is possible to use bookmark names in 'hg merge' and
119 deleted. It is possible to use bookmark names in 'hg merge' and
120 'hg update' to update to a given bookmark.
120 'hg update' to update to a given bookmark.
121
121
122 You can use 'hg bookmark NAME' to set a bookmark on the working
122 You can use 'hg bookmark NAME' to set a bookmark on the working
123 directory's parent revision with the given name. If you specify
123 directory's parent revision with the given name. If you specify
124 a revision using -r REV (where REV may be an existing bookmark),
124 a revision using -r REV (where REV may be an existing bookmark),
125 the bookmark is assigned to that revision.
125 the bookmark is assigned to that revision.
126 '''
126 '''
127 hexfn = ui.debugflag and hex or short
127 hexfn = ui.debugflag and hex or short
128 marks = parse(repo)
128 marks = parse(repo)
129 cur = repo.changectx('.').node()
129 cur = repo.changectx('.').node()
130
130
131 if rename:
131 if rename:
132 if rename not in marks:
132 if rename 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 in marks and not force:
134 if mark in marks and not force:
135 raise util.Abort(_("a bookmark of the same name already exists"))
135 raise util.Abort(_("a bookmark of the same name already exists"))
136 if mark is None:
136 if mark is None:
137 raise util.Abort(_("new bookmark name required"))
137 raise util.Abort(_("new bookmark name required"))
138 marks[mark] = marks[rename]
138 marks[mark] = marks[rename]
139 del marks[rename]
139 del marks[rename]
140 if current(repo) == rename:
140 if current(repo) == rename:
141 setcurrent(repo, mark)
141 setcurrent(repo, mark)
142 write(repo, marks)
142 write(repo, marks)
143 return
143 return
144
144
145 if delete:
145 if delete:
146 if mark is None:
146 if mark is None:
147 raise util.Abort(_("bookmark name required"))
147 raise util.Abort(_("bookmark name required"))
148 if mark not in marks:
148 if mark not in marks:
149 raise util.Abort(_("a bookmark of this name does not exist"))
149 raise util.Abort(_("a bookmark of this name does not exist"))
150 if mark == current(repo):
150 if mark == current(repo):
151 setcurrent(repo, None)
151 setcurrent(repo, None)
152 del marks[mark]
152 del marks[mark]
153 write(repo, marks)
153 write(repo, marks)
154 return
154 return
155
155
156 if mark != None:
156 if mark != None:
157 if "\n" in mark:
157 if "\n" in mark:
158 raise util.Abort(_("bookmark name cannot contain newlines"))
158 raise util.Abort(_("bookmark name cannot contain newlines"))
159 mark = mark.strip()
159 mark = mark.strip()
160 if mark in marks and not force:
160 if mark in marks and not force:
161 raise util.Abort(_("a bookmark of the same name already exists"))
161 raise util.Abort(_("a bookmark of the same name already exists"))
162 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
162 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
163 and not force):
163 and not force):
164 raise util.Abort(
164 raise util.Abort(
165 _("a bookmark cannot have the name of an existing branch"))
165 _("a bookmark cannot have the name of an existing branch"))
166 if rev:
166 if rev:
167 marks[mark] = repo.lookup(rev)
167 marks[mark] = repo.lookup(rev)
168 else:
168 else:
169 marks[mark] = repo.changectx('.').node()
169 marks[mark] = repo.changectx('.').node()
170 setcurrent(repo, mark)
170 setcurrent(repo, mark)
171 write(repo, marks)
171 write(repo, marks)
172 return
172 return
173
173
174 if mark is None:
174 if mark is None:
175 if rev:
175 if rev:
176 raise util.Abort(_("bookmark name required"))
176 raise util.Abort(_("bookmark name required"))
177 if len(marks) == 0:
177 if len(marks) == 0:
178 ui.status("no bookmarks set\n")
178 ui.status("no bookmarks set\n")
179 else:
179 else:
180 for bmark, n in marks.iteritems():
180 for bmark, n in marks.iteritems():
181 if ui.configbool('bookmarks', 'track.current'):
181 if ui.configbool('bookmarks', 'track.current'):
182 prefix = (bmark == current(repo) and n == cur) and '*' or ' '
182 prefix = (bmark == current(repo) and n == cur) and '*' or ' '
183 else:
183 else:
184 prefix = (n == cur) and '*' or ' '
184 prefix = (n == cur) and '*' or ' '
185
185
186 ui.write(" %s %-25s %d:%s\n" % (
186 ui.write(" %s %-25s %d:%s\n" % (
187 prefix, bmark, repo.changelog.rev(n), hexfn(n)))
187 prefix, bmark, repo.changelog.rev(n), hexfn(n)))
188 return
188 return
189
189
190 def _revstostrip(changelog, node):
190 def _revstostrip(changelog, node):
191 srev = changelog.rev(node)
191 srev = changelog.rev(node)
192 tostrip = [srev]
192 tostrip = [srev]
193 saveheads = []
193 saveheads = []
194 for r in xrange(srev, len(changelog)):
194 for r in xrange(srev, len(changelog)):
195 parents = changelog.parentrevs(r)
195 parents = changelog.parentrevs(r)
196 if parents[0] in tostrip or parents[1] in tostrip:
196 if parents[0] in tostrip or parents[1] in tostrip:
197 tostrip.append(r)
197 tostrip.append(r)
198 if parents[1] != nullrev:
198 if parents[1] != nullrev:
199 for p in parents:
199 for p in parents:
200 if p not in tostrip and p > srev:
200 if p not in tostrip and p > srev:
201 saveheads.append(p)
201 saveheads.append(p)
202 return [r for r in tostrip if r not in saveheads]
202 return [r for r in tostrip if r not in saveheads]
203
203
204 def strip(oldstrip, ui, repo, node, backup="all"):
204 def strip(oldstrip, ui, repo, node, backup="all"):
205 """Strip bookmarks if revisions are stripped using
205 """Strip bookmarks if revisions are stripped using
206 the mercurial.strip method. This usually happens during
206 the mercurial.strip method. This usually happens during
207 qpush and qpop"""
207 qpush and qpop"""
208 revisions = _revstostrip(repo.changelog, node)
208 revisions = _revstostrip(repo.changelog, node)
209 marks = parse(repo)
209 marks = parse(repo)
210 update = []
210 update = []
211 for mark, n in marks.iteritems():
211 for mark, n in marks.iteritems():
212 if repo.changelog.rev(n) in revisions:
212 if repo.changelog.rev(n) in revisions:
213 update.append(mark)
213 update.append(mark)
214 oldstrip(ui, repo, node, backup)
214 oldstrip(ui, repo, node, backup)
215 if len(update) > 0:
215 if len(update) > 0:
216 for m in update:
216 for m in update:
217 marks[m] = repo.changectx('.').node()
217 marks[m] = repo.changectx('.').node()
218 write(repo, marks)
218 write(repo, marks)
219
219
220 def reposetup(ui, repo):
220 def reposetup(ui, repo):
221 if not isinstance(repo, localrepo.localrepository):
221 if not isinstance(repo, localrepo.localrepository):
222 return
222 return
223
223
224 # init a bookmark cache as otherwise we would get a infinite reading
224 # init a bookmark cache as otherwise we would get a infinite reading
225 # in lookup()
225 # in lookup()
226 repo._bookmarks = None
226 repo._bookmarks = None
227 repo._bookmarkcurrent = None
227 repo._bookmarkcurrent = None
228
228
229 class bookmark_repo(repo.__class__):
229 class bookmark_repo(repo.__class__):
230 def rollback(self):
230 def rollback(self):
231 if os.path.exists(self.join('undo.bookmarks')):
231 if os.path.exists(self.join('undo.bookmarks')):
232 util.rename(self.join('undo.bookmarks'), self.join('bookmarks'))
232 util.rename(self.join('undo.bookmarks'), self.join('bookmarks'))
233 return super(bookmark_repo, self).rollback()
233 return super(bookmark_repo, self).rollback()
234
234
235 def lookup(self, key):
235 def lookup(self, key):
236 if self._bookmarks is None:
236 if self._bookmarks is None:
237 self._bookmarks = parse(self)
237 self._bookmarks = parse(self)
238 if key in self._bookmarks:
238 if key in self._bookmarks:
239 key = self._bookmarks[key]
239 key = self._bookmarks[key]
240 return super(bookmark_repo, self).lookup(key)
240 return super(bookmark_repo, self).lookup(key)
241
241
242 def commit(self, *k, **kw):
242 def commit(self, *k, **kw):
243 """Add a revision to the repository and
243 """Add a revision to the repository and
244 move the bookmark"""
244 move the bookmark"""
245 node = super(bookmark_repo, self).commit(*k, **kw)
245 node = super(bookmark_repo, self).commit(*k, **kw)
246 if node is None:
246 if node is None:
247 return None
247 return None
248 parents = repo.changelog.parents(node)
248 parents = repo.changelog.parents(node)
249 if parents[1] == nullid:
249 if parents[1] == nullid:
250 parents = (parents[0],)
250 parents = (parents[0],)
251 marks = parse(repo)
251 marks = parse(repo)
252 update = False
252 update = False
253 for mark, n in marks.items():
253 for mark, n in marks.items():
254 if ui.configbool('bookmarks', 'track.current'):
254 if ui.configbool('bookmarks', 'track.current'):
255 if mark == current(repo) and n in parents:
255 if mark == current(repo) and n in parents:
256 marks[mark] = node
256 marks[mark] = node
257 update = True
257 update = True
258 else:
258 else:
259 if n in parents:
259 if n in parents:
260 marks[mark] = node
260 marks[mark] = node
261 update = True
261 update = True
262 if update:
262 if update:
263 write(repo, marks)
263 write(repo, marks)
264 return node
264 return node
265
265
266 def addchangegroup(self, source, srctype, url, emptyok=False):
266 def addchangegroup(self, source, srctype, url, emptyok=False):
267 parents = repo.dirstate.parents()
267 parents = repo.dirstate.parents()
268
268
269 result = super(bookmark_repo, self).addchangegroup(
269 result = super(bookmark_repo, self).addchangegroup(
270 source, srctype, url, emptyok)
270 source, srctype, url, emptyok)
271 if result > 1:
271 if result > 1:
272 # We have more heads than before
272 # We have more heads than before
273 return result
273 return result
274 node = repo.changelog.tip()
274 node = repo.changelog.tip()
275 marks = parse(repo)
275 marks = parse(repo)
276 update = False
276 update = False
277 for mark, n in marks.items():
277 for mark, n in marks.items():
278 if n in parents:
278 if n in parents:
279 marks[mark] = node
279 marks[mark] = node
280 update = True
280 update = True
281 if update:
281 if update:
282 write(repo, marks)
282 write(repo, marks)
283 return result
283 return result
284
284
285 def tags(self):
285 def tags(self):
286 """Merge bookmarks with normal tags"""
286 """Merge bookmarks with normal tags"""
287 if self.tagscache:
287 if self.tagscache:
288 return self.tagscache
288 return self.tagscache
289
289
290 tagscache = super(bookmark_repo, self).tags()
290 tagscache = super(bookmark_repo, self).tags()
291 tagscache.update(parse(repo))
291 tagscache.update(parse(repo))
292 return tagscache
292 return tagscache
293
293
294 repo.__class__ = bookmark_repo
294 repo.__class__ = bookmark_repo
295
295
296 def uisetup(ui):
296 def uisetup(ui):
297 extensions.wrapfunction(repair, "strip", strip)
297 extensions.wrapfunction(repair, "strip", strip)
298 if ui.configbool('bookmarks', 'track.current'):
298 if ui.configbool('bookmarks', 'track.current'):
299 extensions.wrapcommand(commands.table, 'update', updatecurbookmark)
299 extensions.wrapcommand(commands.table, 'update', updatecurbookmark)
300
300
301 def updatecurbookmark(orig, ui, repo, *args, **opts):
301 def updatecurbookmark(orig, ui, repo, *args, **opts):
302 '''Set the current bookmark
302 '''Set the current bookmark
303
303
304 If the user updates to a bookmark we update the .hg/bookmarks.current
304 If the user updates to a bookmark we update the .hg/bookmarks.current
305 file.
305 file.
306 '''
306 '''
307 res = orig(ui, repo, *args, **opts)
307 res = orig(ui, repo, *args, **opts)
308 rev = opts['rev']
308 rev = opts['rev']
309 if not rev and len(args) > 0:
309 if not rev and len(args) > 0:
310 rev = args[0]
310 rev = args[0]
311 setcurrent(repo, rev)
311 setcurrent(repo, rev)
312 return res
312 return res
313
313
314 cmdtable = {
314 cmdtable = {
315 "bookmarks":
315 "bookmarks":
316 (bookmark,
316 (bookmark,
317 [('f', 'force', False, _('force')),
317 [('f', 'force', False, _('force')),
318 ('r', 'rev', '', _('revision')),
318 ('r', 'rev', '', _('revision')),
319 ('d', 'delete', False, _('delete a given bookmark')),
319 ('d', 'delete', False, _('delete a given bookmark')),
320 ('m', 'rename', '', _('rename a given bookmark'))],
320 ('m', 'rename', '', _('rename a given bookmark'))],
321 _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
321 _('hg bookmarks [-f] [-d] [-m NAME] [-r REV] [NAME]')),
322 }
322 }
@@ -1,358 +1,358 b''
1 # Minimal support for git commands on an hg repository
1 # Minimal support for git commands on an hg repository
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 '''browsing the repository in a graphical way
8 '''browsing the repository in a graphical way
9
9
10 The hgk extension allows browsing the history of a repository in a
10 The hgk extension allows browsing the history of a repository in a
11 graphical way. It requires Tcl/Tk version 8.4 or later. (Tcl/Tk is not
11 graphical way. It requires Tcl/Tk version 8.4 or later. (Tcl/Tk is not
12 distributed with Mercurial.)
12 distributed with Mercurial.)
13
13
14 hgk consists of two parts: a Tcl script that does the displaying and
14 hgk consists of two parts: a Tcl script that does the displaying and
15 querying of information, and an extension to mercurial named hgk.py,
15 querying of information, and an extension to Mercurial named hgk.py,
16 which provides hooks for hgk to get information. hgk can be found in
16 which provides hooks for hgk to get information. hgk can be found in
17 the contrib directory, and hgk.py can be found in the hgext directory.
17 the contrib directory, and hgk.py can be found in the hgext directory.
18
18
19 To load the hgext.py extension, add it to your .hgrc file (you have to
19 To load the hgext.py extension, add it to your .hgrc file (you have to
20 use your global $HOME/.hgrc file, not one in a repository). You can
20 use your global $HOME/.hgrc file, not one in a repository). You can
21 specify an absolute path:
21 specify an absolute path:
22
22
23 [extensions]
23 [extensions]
24 hgk=/usr/local/lib/hgk.py
24 hgk=/usr/local/lib/hgk.py
25
25
26 Mercurial can also scan the default python library path for a file
26 Mercurial can also scan the default python library path for a file
27 named 'hgk.py' if you set hgk empty:
27 named 'hgk.py' if you set hgk empty:
28
28
29 [extensions]
29 [extensions]
30 hgk=
30 hgk=
31
31
32 The hg view command will launch the hgk Tcl script. For this command
32 The hg view command will launch the hgk Tcl script. For this command
33 to work, hgk must be in your search path. Alternately, you can specify
33 to work, hgk must be in your search path. Alternately, you can specify
34 the path to hgk in your .hgrc file:
34 the path to hgk in your .hgrc file:
35
35
36 [hgk]
36 [hgk]
37 path=/location/of/hgk
37 path=/location/of/hgk
38
38
39 hgk can make use of the extdiff extension to visualize revisions.
39 hgk can make use of the extdiff extension to visualize revisions.
40 Assuming you had already configured extdiff vdiff command, just add:
40 Assuming you had already configured extdiff vdiff command, just add:
41
41
42 [hgk]
42 [hgk]
43 vdiff=vdiff
43 vdiff=vdiff
44
44
45 Revisions context menu will now display additional entries to fire
45 Revisions context menu will now display additional entries to fire
46 vdiff on hovered and selected revisions.'''
46 vdiff on hovered and selected revisions.'''
47
47
48 import os
48 import os
49 from mercurial import commands, util, patch, revlog, cmdutil
49 from mercurial import commands, util, patch, revlog, cmdutil
50 from mercurial.node import nullid, nullrev, short
50 from mercurial.node import nullid, nullrev, short
51 from mercurial.i18n import _
51 from mercurial.i18n import _
52
52
53 def difftree(ui, repo, node1=None, node2=None, *files, **opts):
53 def difftree(ui, repo, node1=None, node2=None, *files, **opts):
54 """diff trees from two commits"""
54 """diff trees from two commits"""
55 def __difftree(repo, node1, node2, files=[]):
55 def __difftree(repo, node1, node2, files=[]):
56 assert node2 is not None
56 assert node2 is not None
57 mmap = repo[node1].manifest()
57 mmap = repo[node1].manifest()
58 mmap2 = repo[node2].manifest()
58 mmap2 = repo[node2].manifest()
59 m = cmdutil.match(repo, files)
59 m = cmdutil.match(repo, files)
60 modified, added, removed = repo.status(node1, node2, m)[:3]
60 modified, added, removed = repo.status(node1, node2, m)[:3]
61 empty = short(nullid)
61 empty = short(nullid)
62
62
63 for f in modified:
63 for f in modified:
64 # TODO get file permissions
64 # TODO get file permissions
65 ui.write(":100664 100664 %s %s M\t%s\t%s\n" %
65 ui.write(":100664 100664 %s %s M\t%s\t%s\n" %
66 (short(mmap[f]), short(mmap2[f]), f, f))
66 (short(mmap[f]), short(mmap2[f]), f, f))
67 for f in added:
67 for f in added:
68 ui.write(":000000 100664 %s %s N\t%s\t%s\n" %
68 ui.write(":000000 100664 %s %s N\t%s\t%s\n" %
69 (empty, short(mmap2[f]), f, f))
69 (empty, short(mmap2[f]), f, f))
70 for f in removed:
70 for f in removed:
71 ui.write(":100664 000000 %s %s D\t%s\t%s\n" %
71 ui.write(":100664 000000 %s %s D\t%s\t%s\n" %
72 (short(mmap[f]), empty, f, f))
72 (short(mmap[f]), empty, f, f))
73 ##
73 ##
74
74
75 while True:
75 while True:
76 if opts['stdin']:
76 if opts['stdin']:
77 try:
77 try:
78 line = raw_input().split(' ')
78 line = raw_input().split(' ')
79 node1 = line[0]
79 node1 = line[0]
80 if len(line) > 1:
80 if len(line) > 1:
81 node2 = line[1]
81 node2 = line[1]
82 else:
82 else:
83 node2 = None
83 node2 = None
84 except EOFError:
84 except EOFError:
85 break
85 break
86 node1 = repo.lookup(node1)
86 node1 = repo.lookup(node1)
87 if node2:
87 if node2:
88 node2 = repo.lookup(node2)
88 node2 = repo.lookup(node2)
89 else:
89 else:
90 node2 = node1
90 node2 = node1
91 node1 = repo.changelog.parents(node1)[0]
91 node1 = repo.changelog.parents(node1)[0]
92 if opts['patch']:
92 if opts['patch']:
93 if opts['pretty']:
93 if opts['pretty']:
94 catcommit(ui, repo, node2, "")
94 catcommit(ui, repo, node2, "")
95 m = cmdutil.match(repo, files)
95 m = cmdutil.match(repo, files)
96 chunks = patch.diff(repo, node1, node2, match=m,
96 chunks = patch.diff(repo, node1, node2, match=m,
97 opts=patch.diffopts(ui, {'git': True}))
97 opts=patch.diffopts(ui, {'git': True}))
98 for chunk in chunks:
98 for chunk in chunks:
99 ui.write(chunk)
99 ui.write(chunk)
100 else:
100 else:
101 __difftree(repo, node1, node2, files=files)
101 __difftree(repo, node1, node2, files=files)
102 if not opts['stdin']:
102 if not opts['stdin']:
103 break
103 break
104
104
105 def catcommit(ui, repo, n, prefix, ctx=None):
105 def catcommit(ui, repo, n, prefix, ctx=None):
106 nlprefix = '\n' + prefix;
106 nlprefix = '\n' + prefix;
107 if ctx is None:
107 if ctx is None:
108 ctx = repo[n]
108 ctx = repo[n]
109 ui.write("tree %s\n" % short(ctx.changeset()[0])) # use ctx.node() instead ??
109 ui.write("tree %s\n" % short(ctx.changeset()[0])) # use ctx.node() instead ??
110 for p in ctx.parents():
110 for p in ctx.parents():
111 ui.write("parent %s\n" % p)
111 ui.write("parent %s\n" % p)
112
112
113 date = ctx.date()
113 date = ctx.date()
114 description = ctx.description().replace("\0", "")
114 description = ctx.description().replace("\0", "")
115 lines = description.splitlines()
115 lines = description.splitlines()
116 if lines and lines[-1].startswith('committer:'):
116 if lines and lines[-1].startswith('committer:'):
117 committer = lines[-1].split(': ')[1].rstrip()
117 committer = lines[-1].split(': ')[1].rstrip()
118 else:
118 else:
119 committer = ctx.user()
119 committer = ctx.user()
120
120
121 ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))
121 ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))
122 ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1]))
122 ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1]))
123 ui.write("revision %d\n" % ctx.rev())
123 ui.write("revision %d\n" % ctx.rev())
124 ui.write("branch %s\n\n" % ctx.branch())
124 ui.write("branch %s\n\n" % ctx.branch())
125
125
126 if prefix != "":
126 if prefix != "":
127 ui.write("%s%s\n" % (prefix, description.replace('\n', nlprefix).strip()))
127 ui.write("%s%s\n" % (prefix, description.replace('\n', nlprefix).strip()))
128 else:
128 else:
129 ui.write(description + "\n")
129 ui.write(description + "\n")
130 if prefix:
130 if prefix:
131 ui.write('\0')
131 ui.write('\0')
132
132
133 def base(ui, repo, node1, node2):
133 def base(ui, repo, node1, node2):
134 """output common ancestor information"""
134 """output common ancestor information"""
135 node1 = repo.lookup(node1)
135 node1 = repo.lookup(node1)
136 node2 = repo.lookup(node2)
136 node2 = repo.lookup(node2)
137 n = repo.changelog.ancestor(node1, node2)
137 n = repo.changelog.ancestor(node1, node2)
138 ui.write(short(n) + "\n")
138 ui.write(short(n) + "\n")
139
139
140 def catfile(ui, repo, type=None, r=None, **opts):
140 def catfile(ui, repo, type=None, r=None, **opts):
141 """cat a specific revision"""
141 """cat a specific revision"""
142 # in stdin mode, every line except the commit is prefixed with two
142 # in stdin mode, every line except the commit is prefixed with two
143 # spaces. This way the our caller can find the commit without magic
143 # spaces. This way the our caller can find the commit without magic
144 # strings
144 # strings
145 #
145 #
146 prefix = ""
146 prefix = ""
147 if opts['stdin']:
147 if opts['stdin']:
148 try:
148 try:
149 (type, r) = raw_input().split(' ');
149 (type, r) = raw_input().split(' ');
150 prefix = " "
150 prefix = " "
151 except EOFError:
151 except EOFError:
152 return
152 return
153
153
154 else:
154 else:
155 if not type or not r:
155 if not type or not r:
156 ui.warn(_("cat-file: type or revision not supplied\n"))
156 ui.warn(_("cat-file: type or revision not supplied\n"))
157 commands.help_(ui, 'cat-file')
157 commands.help_(ui, 'cat-file')
158
158
159 while r:
159 while r:
160 if type != "commit":
160 if type != "commit":
161 ui.warn(_("aborting hg cat-file only understands commits\n"))
161 ui.warn(_("aborting hg cat-file only understands commits\n"))
162 return 1;
162 return 1;
163 n = repo.lookup(r)
163 n = repo.lookup(r)
164 catcommit(ui, repo, n, prefix)
164 catcommit(ui, repo, n, prefix)
165 if opts['stdin']:
165 if opts['stdin']:
166 try:
166 try:
167 (type, r) = raw_input().split(' ');
167 (type, r) = raw_input().split(' ');
168 except EOFError:
168 except EOFError:
169 break
169 break
170 else:
170 else:
171 break
171 break
172
172
173 # git rev-tree is a confusing thing. You can supply a number of
173 # git rev-tree is a confusing thing. You can supply a number of
174 # commit sha1s on the command line, and it walks the commit history
174 # commit sha1s on the command line, and it walks the commit history
175 # telling you which commits are reachable from the supplied ones via
175 # telling you which commits are reachable from the supplied ones via
176 # a bitmask based on arg position.
176 # a bitmask based on arg position.
177 # you can specify a commit to stop at by starting the sha1 with ^
177 # you can specify a commit to stop at by starting the sha1 with ^
178 def revtree(ui, args, repo, full="tree", maxnr=0, parents=False):
178 def revtree(ui, args, repo, full="tree", maxnr=0, parents=False):
179 def chlogwalk():
179 def chlogwalk():
180 count = len(repo)
180 count = len(repo)
181 i = count
181 i = count
182 l = [0] * 100
182 l = [0] * 100
183 chunk = 100
183 chunk = 100
184 while True:
184 while True:
185 if chunk > i:
185 if chunk > i:
186 chunk = i
186 chunk = i
187 i = 0
187 i = 0
188 else:
188 else:
189 i -= chunk
189 i -= chunk
190
190
191 for x in xrange(chunk):
191 for x in xrange(chunk):
192 if i + x >= count:
192 if i + x >= count:
193 l[chunk - x:] = [0] * (chunk - x)
193 l[chunk - x:] = [0] * (chunk - x)
194 break
194 break
195 if full != None:
195 if full != None:
196 l[x] = repo[i + x]
196 l[x] = repo[i + x]
197 l[x].changeset() # force reading
197 l[x].changeset() # force reading
198 else:
198 else:
199 l[x] = 1
199 l[x] = 1
200 for x in xrange(chunk-1, -1, -1):
200 for x in xrange(chunk-1, -1, -1):
201 if l[x] != 0:
201 if l[x] != 0:
202 yield (i + x, full != None and l[x] or None)
202 yield (i + x, full != None and l[x] or None)
203 if i == 0:
203 if i == 0:
204 break
204 break
205
205
206 # calculate and return the reachability bitmask for sha
206 # calculate and return the reachability bitmask for sha
207 def is_reachable(ar, reachable, sha):
207 def is_reachable(ar, reachable, sha):
208 if len(ar) == 0:
208 if len(ar) == 0:
209 return 1
209 return 1
210 mask = 0
210 mask = 0
211 for i in xrange(len(ar)):
211 for i in xrange(len(ar)):
212 if sha in reachable[i]:
212 if sha in reachable[i]:
213 mask |= 1 << i
213 mask |= 1 << i
214
214
215 return mask
215 return mask
216
216
217 reachable = []
217 reachable = []
218 stop_sha1 = []
218 stop_sha1 = []
219 want_sha1 = []
219 want_sha1 = []
220 count = 0
220 count = 0
221
221
222 # figure out which commits they are asking for and which ones they
222 # figure out which commits they are asking for and which ones they
223 # want us to stop on
223 # want us to stop on
224 for i, arg in enumerate(args):
224 for i, arg in enumerate(args):
225 if arg.startswith('^'):
225 if arg.startswith('^'):
226 s = repo.lookup(arg[1:])
226 s = repo.lookup(arg[1:])
227 stop_sha1.append(s)
227 stop_sha1.append(s)
228 want_sha1.append(s)
228 want_sha1.append(s)
229 elif arg != 'HEAD':
229 elif arg != 'HEAD':
230 want_sha1.append(repo.lookup(arg))
230 want_sha1.append(repo.lookup(arg))
231
231
232 # calculate the graph for the supplied commits
232 # calculate the graph for the supplied commits
233 for i, n in enumerate(want_sha1):
233 for i, n in enumerate(want_sha1):
234 reachable.append(set());
234 reachable.append(set());
235 visit = [n];
235 visit = [n];
236 reachable[i].add(n)
236 reachable[i].add(n)
237 while visit:
237 while visit:
238 n = visit.pop(0)
238 n = visit.pop(0)
239 if n in stop_sha1:
239 if n in stop_sha1:
240 continue
240 continue
241 for p in repo.changelog.parents(n):
241 for p in repo.changelog.parents(n):
242 if p not in reachable[i]:
242 if p not in reachable[i]:
243 reachable[i].add(p)
243 reachable[i].add(p)
244 visit.append(p)
244 visit.append(p)
245 if p in stop_sha1:
245 if p in stop_sha1:
246 continue
246 continue
247
247
248 # walk the repository looking for commits that are in our
248 # walk the repository looking for commits that are in our
249 # reachability graph
249 # reachability graph
250 for i, ctx in chlogwalk():
250 for i, ctx in chlogwalk():
251 n = repo.changelog.node(i)
251 n = repo.changelog.node(i)
252 mask = is_reachable(want_sha1, reachable, n)
252 mask = is_reachable(want_sha1, reachable, n)
253 if mask:
253 if mask:
254 parentstr = ""
254 parentstr = ""
255 if parents:
255 if parents:
256 pp = repo.changelog.parents(n)
256 pp = repo.changelog.parents(n)
257 if pp[0] != nullid:
257 if pp[0] != nullid:
258 parentstr += " " + short(pp[0])
258 parentstr += " " + short(pp[0])
259 if pp[1] != nullid:
259 if pp[1] != nullid:
260 parentstr += " " + short(pp[1])
260 parentstr += " " + short(pp[1])
261 if not full:
261 if not full:
262 ui.write("%s%s\n" % (short(n), parentstr))
262 ui.write("%s%s\n" % (short(n), parentstr))
263 elif full == "commit":
263 elif full == "commit":
264 ui.write("%s%s\n" % (short(n), parentstr))
264 ui.write("%s%s\n" % (short(n), parentstr))
265 catcommit(ui, repo, n, ' ', ctx)
265 catcommit(ui, repo, n, ' ', ctx)
266 else:
266 else:
267 (p1, p2) = repo.changelog.parents(n)
267 (p1, p2) = repo.changelog.parents(n)
268 (h, h1, h2) = map(short, (n, p1, p2))
268 (h, h1, h2) = map(short, (n, p1, p2))
269 (i1, i2) = map(repo.changelog.rev, (p1, p2))
269 (i1, i2) = map(repo.changelog.rev, (p1, p2))
270
270
271 date = ctx.date()[0]
271 date = ctx.date()[0]
272 ui.write("%s %s:%s" % (date, h, mask))
272 ui.write("%s %s:%s" % (date, h, mask))
273 mask = is_reachable(want_sha1, reachable, p1)
273 mask = is_reachable(want_sha1, reachable, p1)
274 if i1 != nullrev and mask > 0:
274 if i1 != nullrev and mask > 0:
275 ui.write("%s:%s " % (h1, mask)),
275 ui.write("%s:%s " % (h1, mask)),
276 mask = is_reachable(want_sha1, reachable, p2)
276 mask = is_reachable(want_sha1, reachable, p2)
277 if i2 != nullrev and mask > 0:
277 if i2 != nullrev and mask > 0:
278 ui.write("%s:%s " % (h2, mask))
278 ui.write("%s:%s " % (h2, mask))
279 ui.write("\n")
279 ui.write("\n")
280 if maxnr and count >= maxnr:
280 if maxnr and count >= maxnr:
281 break
281 break
282 count += 1
282 count += 1
283
283
284 def revparse(ui, repo, *revs, **opts):
284 def revparse(ui, repo, *revs, **opts):
285 """parse given revisions"""
285 """parse given revisions"""
286 def revstr(rev):
286 def revstr(rev):
287 if rev == 'HEAD':
287 if rev == 'HEAD':
288 rev = 'tip'
288 rev = 'tip'
289 return revlog.hex(repo.lookup(rev))
289 return revlog.hex(repo.lookup(rev))
290
290
291 for r in revs:
291 for r in revs:
292 revrange = r.split(':', 1)
292 revrange = r.split(':', 1)
293 ui.write('%s\n' % revstr(revrange[0]))
293 ui.write('%s\n' % revstr(revrange[0]))
294 if len(revrange) == 2:
294 if len(revrange) == 2:
295 ui.write('^%s\n' % revstr(revrange[1]))
295 ui.write('^%s\n' % revstr(revrange[1]))
296
296
297 # git rev-list tries to order things by date, and has the ability to stop
297 # git rev-list tries to order things by date, and has the ability to stop
298 # at a given commit without walking the whole repo. TODO add the stop
298 # at a given commit without walking the whole repo. TODO add the stop
299 # parameter
299 # parameter
300 def revlist(ui, repo, *revs, **opts):
300 def revlist(ui, repo, *revs, **opts):
301 """print revisions"""
301 """print revisions"""
302 if opts['header']:
302 if opts['header']:
303 full = "commit"
303 full = "commit"
304 else:
304 else:
305 full = None
305 full = None
306 copy = [x for x in revs]
306 copy = [x for x in revs]
307 revtree(ui, copy, repo, full, opts['max_count'], opts['parents'])
307 revtree(ui, copy, repo, full, opts['max_count'], opts['parents'])
308
308
309 def config(ui, repo, **opts):
309 def config(ui, repo, **opts):
310 """print extension options"""
310 """print extension options"""
311 def writeopt(name, value):
311 def writeopt(name, value):
312 ui.write('k=%s\nv=%s\n' % (name, value))
312 ui.write('k=%s\nv=%s\n' % (name, value))
313
313
314 writeopt('vdiff', ui.config('hgk', 'vdiff', ''))
314 writeopt('vdiff', ui.config('hgk', 'vdiff', ''))
315
315
316
316
317 def view(ui, repo, *etc, **opts):
317 def view(ui, repo, *etc, **opts):
318 "start interactive history viewer"
318 "start interactive history viewer"
319 os.chdir(repo.root)
319 os.chdir(repo.root)
320 optstr = ' '.join(['--%s %s' % (k, v) for k, v in opts.iteritems() if v])
320 optstr = ' '.join(['--%s %s' % (k, v) for k, v in opts.iteritems() if v])
321 cmd = ui.config("hgk", "path", "hgk") + " %s %s" % (optstr, " ".join(etc))
321 cmd = ui.config("hgk", "path", "hgk") + " %s %s" % (optstr, " ".join(etc))
322 ui.debug(_("running %s\n") % cmd)
322 ui.debug(_("running %s\n") % cmd)
323 util.system(cmd)
323 util.system(cmd)
324
324
325 cmdtable = {
325 cmdtable = {
326 "^view":
326 "^view":
327 (view,
327 (view,
328 [('l', 'limit', '', _('limit number of changes displayed'))],
328 [('l', 'limit', '', _('limit number of changes displayed'))],
329 _('hg view [-l LIMIT] [REVRANGE]')),
329 _('hg view [-l LIMIT] [REVRANGE]')),
330 "debug-diff-tree":
330 "debug-diff-tree":
331 (difftree,
331 (difftree,
332 [('p', 'patch', None, _('generate patch')),
332 [('p', 'patch', None, _('generate patch')),
333 ('r', 'recursive', None, _('recursive')),
333 ('r', 'recursive', None, _('recursive')),
334 ('P', 'pretty', None, _('pretty')),
334 ('P', 'pretty', None, _('pretty')),
335 ('s', 'stdin', None, _('stdin')),
335 ('s', 'stdin', None, _('stdin')),
336 ('C', 'copy', None, _('detect copies')),
336 ('C', 'copy', None, _('detect copies')),
337 ('S', 'search', "", _('search'))],
337 ('S', 'search', "", _('search'))],
338 _('hg git-diff-tree [OPTION]... NODE1 NODE2 [FILE]...')),
338 _('hg git-diff-tree [OPTION]... NODE1 NODE2 [FILE]...')),
339 "debug-cat-file":
339 "debug-cat-file":
340 (catfile,
340 (catfile,
341 [('s', 'stdin', None, _('stdin'))],
341 [('s', 'stdin', None, _('stdin'))],
342 _('hg debug-cat-file [OPTION]... TYPE FILE')),
342 _('hg debug-cat-file [OPTION]... TYPE FILE')),
343 "debug-config":
343 "debug-config":
344 (config, [], _('hg debug-config')),
344 (config, [], _('hg debug-config')),
345 "debug-merge-base":
345 "debug-merge-base":
346 (base, [], _('hg debug-merge-base node node')),
346 (base, [], _('hg debug-merge-base node node')),
347 "debug-rev-parse":
347 "debug-rev-parse":
348 (revparse,
348 (revparse,
349 [('', 'default', '', _('ignored'))],
349 [('', 'default', '', _('ignored'))],
350 _('hg debug-rev-parse REV')),
350 _('hg debug-rev-parse REV')),
351 "debug-rev-list":
351 "debug-rev-list":
352 (revlist,
352 (revlist,
353 [('H', 'header', None, _('header')),
353 [('H', 'header', None, _('header')),
354 ('t', 'topo-order', None, _('topo-order')),
354 ('t', 'topo-order', None, _('topo-order')),
355 ('p', 'parents', None, _('parents')),
355 ('p', 'parents', None, _('parents')),
356 ('n', 'max-count', 0, _('max-count'))],
356 ('n', 'max-count', 0, _('max-count'))],
357 _('hg debug-rev-list [options] revs')),
357 _('hg debug-rev-list [options] revs')),
358 }
358 }
@@ -1,2630 +1,2630 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 '''patch management and development
8 '''patch management and development
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use "hg help command" for more details):
17 Common tasks (use "hg help command" for more details):
18
18
19 prepare repository to work with patches qinit
19 prepare repository to work with patches qinit
20 create new patch qnew
20 create new patch qnew
21 import existing patch qimport
21 import existing patch qimport
22
22
23 print patch series qseries
23 print patch series qseries
24 print applied patches qapplied
24 print applied patches qapplied
25 print name of top applied patch qtop
25 print name of top applied patch qtop
26
26
27 add known patch to applied stack qpush
27 add known patch to applied stack qpush
28 remove patch from applied stack qpop
28 remove patch from applied stack qpop
29 refresh contents of top applied patch qrefresh
29 refresh contents of top applied patch qrefresh
30 '''
30 '''
31
31
32 from mercurial.i18n import _
32 from mercurial.i18n import _
33 from mercurial.node import bin, hex, short, nullid, nullrev
33 from mercurial.node import bin, hex, short, nullid, nullrev
34 from mercurial.lock import release
34 from mercurial.lock import release
35 from mercurial import commands, cmdutil, hg, patch, util
35 from mercurial import commands, cmdutil, hg, patch, util
36 from mercurial import repair, extensions, url, error
36 from mercurial import repair, extensions, url, error
37 import os, sys, re, errno
37 import os, sys, re, errno
38
38
39 commands.norepo += " qclone"
39 commands.norepo += " qclone"
40
40
41 # Patch names looks like unix-file names.
41 # Patch names looks like unix-file names.
42 # They must be joinable with queue directory and result in the patch path.
42 # They must be joinable with queue directory and result in the patch path.
43 normname = util.normpath
43 normname = util.normpath
44
44
45 class statusentry:
45 class statusentry:
46 def __init__(self, rev, name=None):
46 def __init__(self, rev, name=None):
47 if not name:
47 if not name:
48 fields = rev.split(':', 1)
48 fields = rev.split(':', 1)
49 if len(fields) == 2:
49 if len(fields) == 2:
50 self.rev, self.name = fields
50 self.rev, self.name = fields
51 else:
51 else:
52 self.rev, self.name = None, None
52 self.rev, self.name = None, None
53 else:
53 else:
54 self.rev, self.name = rev, name
54 self.rev, self.name = rev, name
55
55
56 def __str__(self):
56 def __str__(self):
57 return self.rev + ':' + self.name
57 return self.rev + ':' + self.name
58
58
59 class patchheader(object):
59 class patchheader(object):
60 def __init__(self, pf):
60 def __init__(self, pf):
61 def eatdiff(lines):
61 def eatdiff(lines):
62 while lines:
62 while lines:
63 l = lines[-1]
63 l = lines[-1]
64 if (l.startswith("diff -") or
64 if (l.startswith("diff -") or
65 l.startswith("Index:") or
65 l.startswith("Index:") or
66 l.startswith("===========")):
66 l.startswith("===========")):
67 del lines[-1]
67 del lines[-1]
68 else:
68 else:
69 break
69 break
70 def eatempty(lines):
70 def eatempty(lines):
71 while lines:
71 while lines:
72 l = lines[-1]
72 l = lines[-1]
73 if re.match('\s*$', l):
73 if re.match('\s*$', l):
74 del lines[-1]
74 del lines[-1]
75 else:
75 else:
76 break
76 break
77
77
78 message = []
78 message = []
79 comments = []
79 comments = []
80 user = None
80 user = None
81 date = None
81 date = None
82 format = None
82 format = None
83 subject = None
83 subject = None
84 diffstart = 0
84 diffstart = 0
85
85
86 for line in file(pf):
86 for line in file(pf):
87 line = line.rstrip()
87 line = line.rstrip()
88 if line.startswith('diff --git'):
88 if line.startswith('diff --git'):
89 diffstart = 2
89 diffstart = 2
90 break
90 break
91 if diffstart:
91 if diffstart:
92 if line.startswith('+++ '):
92 if line.startswith('+++ '):
93 diffstart = 2
93 diffstart = 2
94 break
94 break
95 if line.startswith("--- "):
95 if line.startswith("--- "):
96 diffstart = 1
96 diffstart = 1
97 continue
97 continue
98 elif format == "hgpatch":
98 elif format == "hgpatch":
99 # parse values when importing the result of an hg export
99 # parse values when importing the result of an hg export
100 if line.startswith("# User "):
100 if line.startswith("# User "):
101 user = line[7:]
101 user = line[7:]
102 elif line.startswith("# Date "):
102 elif line.startswith("# Date "):
103 date = line[7:]
103 date = line[7:]
104 elif not line.startswith("# ") and line:
104 elif not line.startswith("# ") and line:
105 message.append(line)
105 message.append(line)
106 format = None
106 format = None
107 elif line == '# HG changeset patch':
107 elif line == '# HG changeset patch':
108 format = "hgpatch"
108 format = "hgpatch"
109 elif (format != "tagdone" and (line.startswith("Subject: ") or
109 elif (format != "tagdone" and (line.startswith("Subject: ") or
110 line.startswith("subject: "))):
110 line.startswith("subject: "))):
111 subject = line[9:]
111 subject = line[9:]
112 format = "tag"
112 format = "tag"
113 elif (format != "tagdone" and (line.startswith("From: ") or
113 elif (format != "tagdone" and (line.startswith("From: ") or
114 line.startswith("from: "))):
114 line.startswith("from: "))):
115 user = line[6:]
115 user = line[6:]
116 format = "tag"
116 format = "tag"
117 elif format == "tag" and line == "":
117 elif format == "tag" and line == "":
118 # when looking for tags (subject: from: etc) they
118 # when looking for tags (subject: from: etc) they
119 # end once you find a blank line in the source
119 # end once you find a blank line in the source
120 format = "tagdone"
120 format = "tagdone"
121 elif message or line:
121 elif message or line:
122 message.append(line)
122 message.append(line)
123 comments.append(line)
123 comments.append(line)
124
124
125 eatdiff(message)
125 eatdiff(message)
126 eatdiff(comments)
126 eatdiff(comments)
127 eatempty(message)
127 eatempty(message)
128 eatempty(comments)
128 eatempty(comments)
129
129
130 # make sure message isn't empty
130 # make sure message isn't empty
131 if format and format.startswith("tag") and subject:
131 if format and format.startswith("tag") and subject:
132 message.insert(0, "")
132 message.insert(0, "")
133 message.insert(0, subject)
133 message.insert(0, subject)
134
134
135 self.message = message
135 self.message = message
136 self.comments = comments
136 self.comments = comments
137 self.user = user
137 self.user = user
138 self.date = date
138 self.date = date
139 self.haspatch = diffstart > 1
139 self.haspatch = diffstart > 1
140
140
141 def setuser(self, user):
141 def setuser(self, user):
142 if not self.updateheader(['From: ', '# User '], user):
142 if not self.updateheader(['From: ', '# User '], user):
143 try:
143 try:
144 patchheaderat = self.comments.index('# HG changeset patch')
144 patchheaderat = self.comments.index('# HG changeset patch')
145 self.comments.insert(patchheaderat + 1,'# User ' + user)
145 self.comments.insert(patchheaderat + 1,'# User ' + user)
146 except ValueError:
146 except ValueError:
147 self.comments = ['From: ' + user, ''] + self.comments
147 self.comments = ['From: ' + user, ''] + self.comments
148 self.user = user
148 self.user = user
149
149
150 def setdate(self, date):
150 def setdate(self, date):
151 if self.updateheader(['# Date '], date):
151 if self.updateheader(['# Date '], date):
152 self.date = date
152 self.date = date
153
153
154 def setmessage(self, message):
154 def setmessage(self, message):
155 if self.comments:
155 if self.comments:
156 self._delmsg()
156 self._delmsg()
157 self.message = [message]
157 self.message = [message]
158 self.comments += self.message
158 self.comments += self.message
159
159
160 def updateheader(self, prefixes, new):
160 def updateheader(self, prefixes, new):
161 '''Update all references to a field in the patch header.
161 '''Update all references to a field in the patch header.
162 Return whether the field is present.'''
162 Return whether the field is present.'''
163 res = False
163 res = False
164 for prefix in prefixes:
164 for prefix in prefixes:
165 for i in xrange(len(self.comments)):
165 for i in xrange(len(self.comments)):
166 if self.comments[i].startswith(prefix):
166 if self.comments[i].startswith(prefix):
167 self.comments[i] = prefix + new
167 self.comments[i] = prefix + new
168 res = True
168 res = True
169 break
169 break
170 return res
170 return res
171
171
172 def __str__(self):
172 def __str__(self):
173 if not self.comments:
173 if not self.comments:
174 return ''
174 return ''
175 return '\n'.join(self.comments) + '\n\n'
175 return '\n'.join(self.comments) + '\n\n'
176
176
177 def _delmsg(self):
177 def _delmsg(self):
178 '''Remove existing message, keeping the rest of the comments fields.
178 '''Remove existing message, keeping the rest of the comments fields.
179 If comments contains 'subject: ', message will prepend
179 If comments contains 'subject: ', message will prepend
180 the field and a blank line.'''
180 the field and a blank line.'''
181 if self.message:
181 if self.message:
182 subj = 'subject: ' + self.message[0].lower()
182 subj = 'subject: ' + self.message[0].lower()
183 for i in xrange(len(self.comments)):
183 for i in xrange(len(self.comments)):
184 if subj == self.comments[i].lower():
184 if subj == self.comments[i].lower():
185 del self.comments[i]
185 del self.comments[i]
186 self.message = self.message[2:]
186 self.message = self.message[2:]
187 break
187 break
188 ci = 0
188 ci = 0
189 for mi in self.message:
189 for mi in self.message:
190 while mi != self.comments[ci]:
190 while mi != self.comments[ci]:
191 ci += 1
191 ci += 1
192 del self.comments[ci]
192 del self.comments[ci]
193
193
194 class queue:
194 class queue:
195 def __init__(self, ui, path, patchdir=None):
195 def __init__(self, ui, path, patchdir=None):
196 self.basepath = path
196 self.basepath = path
197 self.path = patchdir or os.path.join(path, "patches")
197 self.path = patchdir or os.path.join(path, "patches")
198 self.opener = util.opener(self.path)
198 self.opener = util.opener(self.path)
199 self.ui = ui
199 self.ui = ui
200 self.applied_dirty = 0
200 self.applied_dirty = 0
201 self.series_dirty = 0
201 self.series_dirty = 0
202 self.series_path = "series"
202 self.series_path = "series"
203 self.status_path = "status"
203 self.status_path = "status"
204 self.guards_path = "guards"
204 self.guards_path = "guards"
205 self.active_guards = None
205 self.active_guards = None
206 self.guards_dirty = False
206 self.guards_dirty = False
207 self._diffopts = None
207 self._diffopts = None
208
208
209 @util.propertycache
209 @util.propertycache
210 def applied(self):
210 def applied(self):
211 if os.path.exists(self.join(self.status_path)):
211 if os.path.exists(self.join(self.status_path)):
212 lines = self.opener(self.status_path).read().splitlines()
212 lines = self.opener(self.status_path).read().splitlines()
213 return [statusentry(l) for l in lines]
213 return [statusentry(l) for l in lines]
214 return []
214 return []
215
215
216 @util.propertycache
216 @util.propertycache
217 def full_series(self):
217 def full_series(self):
218 if os.path.exists(self.join(self.series_path)):
218 if os.path.exists(self.join(self.series_path)):
219 return self.opener(self.series_path).read().splitlines()
219 return self.opener(self.series_path).read().splitlines()
220 return []
220 return []
221
221
222 @util.propertycache
222 @util.propertycache
223 def series(self):
223 def series(self):
224 self.parse_series()
224 self.parse_series()
225 return self.series
225 return self.series
226
226
227 @util.propertycache
227 @util.propertycache
228 def series_guards(self):
228 def series_guards(self):
229 self.parse_series()
229 self.parse_series()
230 return self.series_guards
230 return self.series_guards
231
231
232 def invalidate(self):
232 def invalidate(self):
233 for a in 'applied full_series series series_guards'.split():
233 for a in 'applied full_series series series_guards'.split():
234 if a in self.__dict__:
234 if a in self.__dict__:
235 delattr(self, a)
235 delattr(self, a)
236 self.applied_dirty = 0
236 self.applied_dirty = 0
237 self.series_dirty = 0
237 self.series_dirty = 0
238 self.guards_dirty = False
238 self.guards_dirty = False
239 self.active_guards = None
239 self.active_guards = None
240
240
241 def diffopts(self):
241 def diffopts(self):
242 if self._diffopts is None:
242 if self._diffopts is None:
243 self._diffopts = patch.diffopts(self.ui)
243 self._diffopts = patch.diffopts(self.ui)
244 return self._diffopts
244 return self._diffopts
245
245
246 def join(self, *p):
246 def join(self, *p):
247 return os.path.join(self.path, *p)
247 return os.path.join(self.path, *p)
248
248
249 def find_series(self, patch):
249 def find_series(self, patch):
250 pre = re.compile("(\s*)([^#]+)")
250 pre = re.compile("(\s*)([^#]+)")
251 index = 0
251 index = 0
252 for l in self.full_series:
252 for l in self.full_series:
253 m = pre.match(l)
253 m = pre.match(l)
254 if m:
254 if m:
255 s = m.group(2)
255 s = m.group(2)
256 s = s.rstrip()
256 s = s.rstrip()
257 if s == patch:
257 if s == patch:
258 return index
258 return index
259 index += 1
259 index += 1
260 return None
260 return None
261
261
262 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
262 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
263
263
264 def parse_series(self):
264 def parse_series(self):
265 self.series = []
265 self.series = []
266 self.series_guards = []
266 self.series_guards = []
267 for l in self.full_series:
267 for l in self.full_series:
268 h = l.find('#')
268 h = l.find('#')
269 if h == -1:
269 if h == -1:
270 patch = l
270 patch = l
271 comment = ''
271 comment = ''
272 elif h == 0:
272 elif h == 0:
273 continue
273 continue
274 else:
274 else:
275 patch = l[:h]
275 patch = l[:h]
276 comment = l[h:]
276 comment = l[h:]
277 patch = patch.strip()
277 patch = patch.strip()
278 if patch:
278 if patch:
279 if patch in self.series:
279 if patch in self.series:
280 raise util.Abort(_('%s appears more than once in %s') %
280 raise util.Abort(_('%s appears more than once in %s') %
281 (patch, self.join(self.series_path)))
281 (patch, self.join(self.series_path)))
282 self.series.append(patch)
282 self.series.append(patch)
283 self.series_guards.append(self.guard_re.findall(comment))
283 self.series_guards.append(self.guard_re.findall(comment))
284
284
285 def check_guard(self, guard):
285 def check_guard(self, guard):
286 if not guard:
286 if not guard:
287 return _('guard cannot be an empty string')
287 return _('guard cannot be an empty string')
288 bad_chars = '# \t\r\n\f'
288 bad_chars = '# \t\r\n\f'
289 first = guard[0]
289 first = guard[0]
290 if first in '-+':
290 if first in '-+':
291 return (_('guard %r starts with invalid character: %r') %
291 return (_('guard %r starts with invalid character: %r') %
292 (guard, first))
292 (guard, first))
293 for c in bad_chars:
293 for c in bad_chars:
294 if c in guard:
294 if c in guard:
295 return _('invalid character in guard %r: %r') % (guard, c)
295 return _('invalid character in guard %r: %r') % (guard, c)
296
296
297 def set_active(self, guards):
297 def set_active(self, guards):
298 for guard in guards:
298 for guard in guards:
299 bad = self.check_guard(guard)
299 bad = self.check_guard(guard)
300 if bad:
300 if bad:
301 raise util.Abort(bad)
301 raise util.Abort(bad)
302 guards = sorted(set(guards))
302 guards = sorted(set(guards))
303 self.ui.debug(_('active guards: %s\n') % ' '.join(guards))
303 self.ui.debug(_('active guards: %s\n') % ' '.join(guards))
304 self.active_guards = guards
304 self.active_guards = guards
305 self.guards_dirty = True
305 self.guards_dirty = True
306
306
307 def active(self):
307 def active(self):
308 if self.active_guards is None:
308 if self.active_guards is None:
309 self.active_guards = []
309 self.active_guards = []
310 try:
310 try:
311 guards = self.opener(self.guards_path).read().split()
311 guards = self.opener(self.guards_path).read().split()
312 except IOError, err:
312 except IOError, err:
313 if err.errno != errno.ENOENT: raise
313 if err.errno != errno.ENOENT: raise
314 guards = []
314 guards = []
315 for i, guard in enumerate(guards):
315 for i, guard in enumerate(guards):
316 bad = self.check_guard(guard)
316 bad = self.check_guard(guard)
317 if bad:
317 if bad:
318 self.ui.warn('%s:%d: %s\n' %
318 self.ui.warn('%s:%d: %s\n' %
319 (self.join(self.guards_path), i + 1, bad))
319 (self.join(self.guards_path), i + 1, bad))
320 else:
320 else:
321 self.active_guards.append(guard)
321 self.active_guards.append(guard)
322 return self.active_guards
322 return self.active_guards
323
323
324 def set_guards(self, idx, guards):
324 def set_guards(self, idx, guards):
325 for g in guards:
325 for g in guards:
326 if len(g) < 2:
326 if len(g) < 2:
327 raise util.Abort(_('guard %r too short') % g)
327 raise util.Abort(_('guard %r too short') % g)
328 if g[0] not in '-+':
328 if g[0] not in '-+':
329 raise util.Abort(_('guard %r starts with invalid char') % g)
329 raise util.Abort(_('guard %r starts with invalid char') % g)
330 bad = self.check_guard(g[1:])
330 bad = self.check_guard(g[1:])
331 if bad:
331 if bad:
332 raise util.Abort(bad)
332 raise util.Abort(bad)
333 drop = self.guard_re.sub('', self.full_series[idx])
333 drop = self.guard_re.sub('', self.full_series[idx])
334 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
334 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
335 self.parse_series()
335 self.parse_series()
336 self.series_dirty = True
336 self.series_dirty = True
337
337
338 def pushable(self, idx):
338 def pushable(self, idx):
339 if isinstance(idx, str):
339 if isinstance(idx, str):
340 idx = self.series.index(idx)
340 idx = self.series.index(idx)
341 patchguards = self.series_guards[idx]
341 patchguards = self.series_guards[idx]
342 if not patchguards:
342 if not patchguards:
343 return True, None
343 return True, None
344 guards = self.active()
344 guards = self.active()
345 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
345 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
346 if exactneg:
346 if exactneg:
347 return False, exactneg[0]
347 return False, exactneg[0]
348 pos = [g for g in patchguards if g[0] == '+']
348 pos = [g for g in patchguards if g[0] == '+']
349 exactpos = [g for g in pos if g[1:] in guards]
349 exactpos = [g for g in pos if g[1:] in guards]
350 if pos:
350 if pos:
351 if exactpos:
351 if exactpos:
352 return True, exactpos[0]
352 return True, exactpos[0]
353 return False, pos
353 return False, pos
354 return True, ''
354 return True, ''
355
355
356 def explain_pushable(self, idx, all_patches=False):
356 def explain_pushable(self, idx, all_patches=False):
357 write = all_patches and self.ui.write or self.ui.warn
357 write = all_patches and self.ui.write or self.ui.warn
358 if all_patches or self.ui.verbose:
358 if all_patches or self.ui.verbose:
359 if isinstance(idx, str):
359 if isinstance(idx, str):
360 idx = self.series.index(idx)
360 idx = self.series.index(idx)
361 pushable, why = self.pushable(idx)
361 pushable, why = self.pushable(idx)
362 if all_patches and pushable:
362 if all_patches and pushable:
363 if why is None:
363 if why is None:
364 write(_('allowing %s - no guards in effect\n') %
364 write(_('allowing %s - no guards in effect\n') %
365 self.series[idx])
365 self.series[idx])
366 else:
366 else:
367 if not why:
367 if not why:
368 write(_('allowing %s - no matching negative guards\n') %
368 write(_('allowing %s - no matching negative guards\n') %
369 self.series[idx])
369 self.series[idx])
370 else:
370 else:
371 write(_('allowing %s - guarded by %r\n') %
371 write(_('allowing %s - guarded by %r\n') %
372 (self.series[idx], why))
372 (self.series[idx], why))
373 if not pushable:
373 if not pushable:
374 if why:
374 if why:
375 write(_('skipping %s - guarded by %r\n') %
375 write(_('skipping %s - guarded by %r\n') %
376 (self.series[idx], why))
376 (self.series[idx], why))
377 else:
377 else:
378 write(_('skipping %s - no matching guards\n') %
378 write(_('skipping %s - no matching guards\n') %
379 self.series[idx])
379 self.series[idx])
380
380
381 def save_dirty(self):
381 def save_dirty(self):
382 def write_list(items, path):
382 def write_list(items, path):
383 fp = self.opener(path, 'w')
383 fp = self.opener(path, 'w')
384 for i in items:
384 for i in items:
385 fp.write("%s\n" % i)
385 fp.write("%s\n" % i)
386 fp.close()
386 fp.close()
387 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
387 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
388 if self.series_dirty: write_list(self.full_series, self.series_path)
388 if self.series_dirty: write_list(self.full_series, self.series_path)
389 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
389 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
390
390
391 def removeundo(self, repo):
391 def removeundo(self, repo):
392 undo = repo.sjoin('undo')
392 undo = repo.sjoin('undo')
393 if not os.path.exists(undo):
393 if not os.path.exists(undo):
394 return
394 return
395 try:
395 try:
396 os.unlink(undo)
396 os.unlink(undo)
397 except OSError, inst:
397 except OSError, inst:
398 self.ui.warn(_('error removing undo: %s\n') % str(inst))
398 self.ui.warn(_('error removing undo: %s\n') % str(inst))
399
399
400 def printdiff(self, repo, node1, node2=None, files=None,
400 def printdiff(self, repo, node1, node2=None, files=None,
401 fp=None, changes=None, opts={}):
401 fp=None, changes=None, opts={}):
402 m = cmdutil.match(repo, files, opts)
402 m = cmdutil.match(repo, files, opts)
403 chunks = patch.diff(repo, node1, node2, m, changes, self.diffopts())
403 chunks = patch.diff(repo, node1, node2, m, changes, self.diffopts())
404 write = fp is None and repo.ui.write or fp.write
404 write = fp is None and repo.ui.write or fp.write
405 for chunk in chunks:
405 for chunk in chunks:
406 write(chunk)
406 write(chunk)
407
407
408 def mergeone(self, repo, mergeq, head, patch, rev):
408 def mergeone(self, repo, mergeq, head, patch, rev):
409 # first try just applying the patch
409 # first try just applying the patch
410 (err, n) = self.apply(repo, [ patch ], update_status=False,
410 (err, n) = self.apply(repo, [ patch ], update_status=False,
411 strict=True, merge=rev)
411 strict=True, merge=rev)
412
412
413 if err == 0:
413 if err == 0:
414 return (err, n)
414 return (err, n)
415
415
416 if n is None:
416 if n is None:
417 raise util.Abort(_("apply failed for patch %s") % patch)
417 raise util.Abort(_("apply failed for patch %s") % patch)
418
418
419 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
419 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
420
420
421 # apply failed, strip away that rev and merge.
421 # apply failed, strip away that rev and merge.
422 hg.clean(repo, head)
422 hg.clean(repo, head)
423 self.strip(repo, n, update=False, backup='strip')
423 self.strip(repo, n, update=False, backup='strip')
424
424
425 ctx = repo[rev]
425 ctx = repo[rev]
426 ret = hg.merge(repo, rev)
426 ret = hg.merge(repo, rev)
427 if ret:
427 if ret:
428 raise util.Abort(_("update returned %d") % ret)
428 raise util.Abort(_("update returned %d") % ret)
429 n = repo.commit(ctx.description(), ctx.user(), force=True)
429 n = repo.commit(ctx.description(), ctx.user(), force=True)
430 if n is None:
430 if n is None:
431 raise util.Abort(_("repo commit failed"))
431 raise util.Abort(_("repo commit failed"))
432 try:
432 try:
433 ph = patchheader(mergeq.join(patch))
433 ph = patchheader(mergeq.join(patch))
434 except:
434 except:
435 raise util.Abort(_("unable to read %s") % patch)
435 raise util.Abort(_("unable to read %s") % patch)
436
436
437 patchf = self.opener(patch, "w")
437 patchf = self.opener(patch, "w")
438 comments = str(ph)
438 comments = str(ph)
439 if comments:
439 if comments:
440 patchf.write(comments)
440 patchf.write(comments)
441 self.printdiff(repo, head, n, fp=patchf)
441 self.printdiff(repo, head, n, fp=patchf)
442 patchf.close()
442 patchf.close()
443 self.removeundo(repo)
443 self.removeundo(repo)
444 return (0, n)
444 return (0, n)
445
445
446 def qparents(self, repo, rev=None):
446 def qparents(self, repo, rev=None):
447 if rev is None:
447 if rev is None:
448 (p1, p2) = repo.dirstate.parents()
448 (p1, p2) = repo.dirstate.parents()
449 if p2 == nullid:
449 if p2 == nullid:
450 return p1
450 return p1
451 if len(self.applied) == 0:
451 if len(self.applied) == 0:
452 return None
452 return None
453 return bin(self.applied[-1].rev)
453 return bin(self.applied[-1].rev)
454 pp = repo.changelog.parents(rev)
454 pp = repo.changelog.parents(rev)
455 if pp[1] != nullid:
455 if pp[1] != nullid:
456 arevs = [ x.rev for x in self.applied ]
456 arevs = [ x.rev for x in self.applied ]
457 p0 = hex(pp[0])
457 p0 = hex(pp[0])
458 p1 = hex(pp[1])
458 p1 = hex(pp[1])
459 if p0 in arevs:
459 if p0 in arevs:
460 return pp[0]
460 return pp[0]
461 if p1 in arevs:
461 if p1 in arevs:
462 return pp[1]
462 return pp[1]
463 return pp[0]
463 return pp[0]
464
464
465 def mergepatch(self, repo, mergeq, series):
465 def mergepatch(self, repo, mergeq, series):
466 if len(self.applied) == 0:
466 if len(self.applied) == 0:
467 # each of the patches merged in will have two parents. This
467 # each of the patches merged in will have two parents. This
468 # can confuse the qrefresh, qdiff, and strip code because it
468 # can confuse the qrefresh, qdiff, and strip code because it
469 # needs to know which parent is actually in the patch queue.
469 # needs to know which parent is actually in the patch queue.
470 # so, we insert a merge marker with only one parent. This way
470 # so, we insert a merge marker with only one parent. This way
471 # the first patch in the queue is never a merge patch
471 # the first patch in the queue is never a merge patch
472 #
472 #
473 pname = ".hg.patches.merge.marker"
473 pname = ".hg.patches.merge.marker"
474 n = repo.commit('[mq]: merge marker', force=True)
474 n = repo.commit('[mq]: merge marker', force=True)
475 self.removeundo(repo)
475 self.removeundo(repo)
476 self.applied.append(statusentry(hex(n), pname))
476 self.applied.append(statusentry(hex(n), pname))
477 self.applied_dirty = 1
477 self.applied_dirty = 1
478
478
479 head = self.qparents(repo)
479 head = self.qparents(repo)
480
480
481 for patch in series:
481 for patch in series:
482 patch = mergeq.lookup(patch, strict=True)
482 patch = mergeq.lookup(patch, strict=True)
483 if not patch:
483 if not patch:
484 self.ui.warn(_("patch %s does not exist\n") % patch)
484 self.ui.warn(_("patch %s does not exist\n") % patch)
485 return (1, None)
485 return (1, None)
486 pushable, reason = self.pushable(patch)
486 pushable, reason = self.pushable(patch)
487 if not pushable:
487 if not pushable:
488 self.explain_pushable(patch, all_patches=True)
488 self.explain_pushable(patch, all_patches=True)
489 continue
489 continue
490 info = mergeq.isapplied(patch)
490 info = mergeq.isapplied(patch)
491 if not info:
491 if not info:
492 self.ui.warn(_("patch %s is not applied\n") % patch)
492 self.ui.warn(_("patch %s is not applied\n") % patch)
493 return (1, None)
493 return (1, None)
494 rev = bin(info[1])
494 rev = bin(info[1])
495 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
495 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
496 if head:
496 if head:
497 self.applied.append(statusentry(hex(head), patch))
497 self.applied.append(statusentry(hex(head), patch))
498 self.applied_dirty = 1
498 self.applied_dirty = 1
499 if err:
499 if err:
500 return (err, head)
500 return (err, head)
501 self.save_dirty()
501 self.save_dirty()
502 return (0, head)
502 return (0, head)
503
503
504 def patch(self, repo, patchfile):
504 def patch(self, repo, patchfile):
505 '''Apply patchfile to the working directory.
505 '''Apply patchfile to the working directory.
506 patchfile: file name of patch'''
506 patchfile: file name of patch'''
507 files = {}
507 files = {}
508 try:
508 try:
509 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
509 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
510 files=files)
510 files=files)
511 except Exception, inst:
511 except Exception, inst:
512 self.ui.note(str(inst) + '\n')
512 self.ui.note(str(inst) + '\n')
513 if not self.ui.verbose:
513 if not self.ui.verbose:
514 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
514 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
515 return (False, files, False)
515 return (False, files, False)
516
516
517 return (True, files, fuzz)
517 return (True, files, fuzz)
518
518
519 def apply(self, repo, series, list=False, update_status=True,
519 def apply(self, repo, series, list=False, update_status=True,
520 strict=False, patchdir=None, merge=None, all_files={}):
520 strict=False, patchdir=None, merge=None, all_files={}):
521 wlock = lock = tr = None
521 wlock = lock = tr = None
522 try:
522 try:
523 wlock = repo.wlock()
523 wlock = repo.wlock()
524 lock = repo.lock()
524 lock = repo.lock()
525 tr = repo.transaction()
525 tr = repo.transaction()
526 try:
526 try:
527 ret = self._apply(repo, series, list, update_status,
527 ret = self._apply(repo, series, list, update_status,
528 strict, patchdir, merge, all_files=all_files)
528 strict, patchdir, merge, all_files=all_files)
529 tr.close()
529 tr.close()
530 self.save_dirty()
530 self.save_dirty()
531 return ret
531 return ret
532 except:
532 except:
533 try:
533 try:
534 tr.abort()
534 tr.abort()
535 finally:
535 finally:
536 repo.invalidate()
536 repo.invalidate()
537 repo.dirstate.invalidate()
537 repo.dirstate.invalidate()
538 raise
538 raise
539 finally:
539 finally:
540 del tr
540 del tr
541 release(lock, wlock)
541 release(lock, wlock)
542 self.removeundo(repo)
542 self.removeundo(repo)
543
543
544 def _apply(self, repo, series, list=False, update_status=True,
544 def _apply(self, repo, series, list=False, update_status=True,
545 strict=False, patchdir=None, merge=None, all_files={}):
545 strict=False, patchdir=None, merge=None, all_files={}):
546 # TODO unify with commands.py
546 # TODO unify with commands.py
547 if not patchdir:
547 if not patchdir:
548 patchdir = self.path
548 patchdir = self.path
549 err = 0
549 err = 0
550 n = None
550 n = None
551 for patchname in series:
551 for patchname in series:
552 pushable, reason = self.pushable(patchname)
552 pushable, reason = self.pushable(patchname)
553 if not pushable:
553 if not pushable:
554 self.explain_pushable(patchname, all_patches=True)
554 self.explain_pushable(patchname, all_patches=True)
555 continue
555 continue
556 self.ui.warn(_("applying %s\n") % patchname)
556 self.ui.warn(_("applying %s\n") % patchname)
557 pf = os.path.join(patchdir, patchname)
557 pf = os.path.join(patchdir, patchname)
558
558
559 try:
559 try:
560 ph = patchheader(self.join(patchname))
560 ph = patchheader(self.join(patchname))
561 except:
561 except:
562 self.ui.warn(_("Unable to read %s\n") % patchname)
562 self.ui.warn(_("Unable to read %s\n") % patchname)
563 err = 1
563 err = 1
564 break
564 break
565
565
566 message = ph.message
566 message = ph.message
567 if not message:
567 if not message:
568 message = _("imported patch %s\n") % patchname
568 message = _("imported patch %s\n") % patchname
569 else:
569 else:
570 if list:
570 if list:
571 message.append(_("\nimported patch %s") % patchname)
571 message.append(_("\nimported patch %s") % patchname)
572 message = '\n'.join(message)
572 message = '\n'.join(message)
573
573
574 if ph.haspatch:
574 if ph.haspatch:
575 (patcherr, files, fuzz) = self.patch(repo, pf)
575 (patcherr, files, fuzz) = self.patch(repo, pf)
576 all_files.update(files)
576 all_files.update(files)
577 patcherr = not patcherr
577 patcherr = not patcherr
578 else:
578 else:
579 self.ui.warn(_("patch %s is empty\n") % patchname)
579 self.ui.warn(_("patch %s is empty\n") % patchname)
580 patcherr, files, fuzz = 0, [], 0
580 patcherr, files, fuzz = 0, [], 0
581
581
582 if merge and files:
582 if merge and files:
583 # Mark as removed/merged and update dirstate parent info
583 # Mark as removed/merged and update dirstate parent info
584 removed = []
584 removed = []
585 merged = []
585 merged = []
586 for f in files:
586 for f in files:
587 if os.path.exists(repo.wjoin(f)):
587 if os.path.exists(repo.wjoin(f)):
588 merged.append(f)
588 merged.append(f)
589 else:
589 else:
590 removed.append(f)
590 removed.append(f)
591 for f in removed:
591 for f in removed:
592 repo.dirstate.remove(f)
592 repo.dirstate.remove(f)
593 for f in merged:
593 for f in merged:
594 repo.dirstate.merge(f)
594 repo.dirstate.merge(f)
595 p1, p2 = repo.dirstate.parents()
595 p1, p2 = repo.dirstate.parents()
596 repo.dirstate.setparents(p1, merge)
596 repo.dirstate.setparents(p1, merge)
597
597
598 files = patch.updatedir(self.ui, repo, files)
598 files = patch.updatedir(self.ui, repo, files)
599 match = cmdutil.matchfiles(repo, files or [])
599 match = cmdutil.matchfiles(repo, files or [])
600 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
600 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
601
601
602 if n is None:
602 if n is None:
603 raise util.Abort(_("repo commit failed"))
603 raise util.Abort(_("repo commit failed"))
604
604
605 if update_status:
605 if update_status:
606 self.applied.append(statusentry(hex(n), patchname))
606 self.applied.append(statusentry(hex(n), patchname))
607
607
608 if patcherr:
608 if patcherr:
609 self.ui.warn(_("patch failed, rejects left in working dir\n"))
609 self.ui.warn(_("patch failed, rejects left in working dir\n"))
610 err = 1
610 err = 1
611 break
611 break
612
612
613 if fuzz and strict:
613 if fuzz and strict:
614 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
614 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
615 err = 1
615 err = 1
616 break
616 break
617 return (err, n)
617 return (err, n)
618
618
619 def _clean_series(self, patches):
619 def _clean_series(self, patches):
620 for i in sorted([self.find_series(p) for p in patches], reverse=True):
620 for i in sorted([self.find_series(p) for p in patches], reverse=True):
621 del self.full_series[i]
621 del self.full_series[i]
622 self.parse_series()
622 self.parse_series()
623 self.series_dirty = 1
623 self.series_dirty = 1
624
624
625 def finish(self, repo, revs):
625 def finish(self, repo, revs):
626 firstrev = repo[self.applied[0].rev].rev()
626 firstrev = repo[self.applied[0].rev].rev()
627 appliedbase = 0
627 appliedbase = 0
628 patches = []
628 patches = []
629 for rev in sorted(revs):
629 for rev in sorted(revs):
630 if rev < firstrev:
630 if rev < firstrev:
631 raise util.Abort(_('revision %d is not managed') % rev)
631 raise util.Abort(_('revision %d is not managed') % rev)
632 base = bin(self.applied[appliedbase].rev)
632 base = bin(self.applied[appliedbase].rev)
633 node = repo.changelog.node(rev)
633 node = repo.changelog.node(rev)
634 if node != base:
634 if node != base:
635 raise util.Abort(_('cannot delete revision %d above '
635 raise util.Abort(_('cannot delete revision %d above '
636 'applied patches') % rev)
636 'applied patches') % rev)
637 patches.append(self.applied[appliedbase].name)
637 patches.append(self.applied[appliedbase].name)
638 appliedbase += 1
638 appliedbase += 1
639
639
640 r = self.qrepo()
640 r = self.qrepo()
641 if r:
641 if r:
642 r.remove(patches, True)
642 r.remove(patches, True)
643 else:
643 else:
644 for p in patches:
644 for p in patches:
645 os.unlink(self.join(p))
645 os.unlink(self.join(p))
646
646
647 del self.applied[:appliedbase]
647 del self.applied[:appliedbase]
648 self.applied_dirty = 1
648 self.applied_dirty = 1
649 self._clean_series(patches)
649 self._clean_series(patches)
650
650
651 def delete(self, repo, patches, opts):
651 def delete(self, repo, patches, opts):
652 if not patches and not opts.get('rev'):
652 if not patches and not opts.get('rev'):
653 raise util.Abort(_('qdelete requires at least one revision or '
653 raise util.Abort(_('qdelete requires at least one revision or '
654 'patch name'))
654 'patch name'))
655
655
656 realpatches = []
656 realpatches = []
657 for patch in patches:
657 for patch in patches:
658 patch = self.lookup(patch, strict=True)
658 patch = self.lookup(patch, strict=True)
659 info = self.isapplied(patch)
659 info = self.isapplied(patch)
660 if info:
660 if info:
661 raise util.Abort(_("cannot delete applied patch %s") % patch)
661 raise util.Abort(_("cannot delete applied patch %s") % patch)
662 if patch not in self.series:
662 if patch not in self.series:
663 raise util.Abort(_("patch %s not in series file") % patch)
663 raise util.Abort(_("patch %s not in series file") % patch)
664 realpatches.append(patch)
664 realpatches.append(patch)
665
665
666 appliedbase = 0
666 appliedbase = 0
667 if opts.get('rev'):
667 if opts.get('rev'):
668 if not self.applied:
668 if not self.applied:
669 raise util.Abort(_('no patches applied'))
669 raise util.Abort(_('no patches applied'))
670 revs = cmdutil.revrange(repo, opts['rev'])
670 revs = cmdutil.revrange(repo, opts['rev'])
671 if len(revs) > 1 and revs[0] > revs[1]:
671 if len(revs) > 1 and revs[0] > revs[1]:
672 revs.reverse()
672 revs.reverse()
673 for rev in revs:
673 for rev in revs:
674 if appliedbase >= len(self.applied):
674 if appliedbase >= len(self.applied):
675 raise util.Abort(_("revision %d is not managed") % rev)
675 raise util.Abort(_("revision %d is not managed") % rev)
676
676
677 base = bin(self.applied[appliedbase].rev)
677 base = bin(self.applied[appliedbase].rev)
678 node = repo.changelog.node(rev)
678 node = repo.changelog.node(rev)
679 if node != base:
679 if node != base:
680 raise util.Abort(_("cannot delete revision %d above "
680 raise util.Abort(_("cannot delete revision %d above "
681 "applied patches") % rev)
681 "applied patches") % rev)
682 realpatches.append(self.applied[appliedbase].name)
682 realpatches.append(self.applied[appliedbase].name)
683 appliedbase += 1
683 appliedbase += 1
684
684
685 if not opts.get('keep'):
685 if not opts.get('keep'):
686 r = self.qrepo()
686 r = self.qrepo()
687 if r:
687 if r:
688 r.remove(realpatches, True)
688 r.remove(realpatches, True)
689 else:
689 else:
690 for p in realpatches:
690 for p in realpatches:
691 os.unlink(self.join(p))
691 os.unlink(self.join(p))
692
692
693 if appliedbase:
693 if appliedbase:
694 del self.applied[:appliedbase]
694 del self.applied[:appliedbase]
695 self.applied_dirty = 1
695 self.applied_dirty = 1
696 self._clean_series(realpatches)
696 self._clean_series(realpatches)
697
697
698 def check_toppatch(self, repo):
698 def check_toppatch(self, repo):
699 if len(self.applied) > 0:
699 if len(self.applied) > 0:
700 top = bin(self.applied[-1].rev)
700 top = bin(self.applied[-1].rev)
701 pp = repo.dirstate.parents()
701 pp = repo.dirstate.parents()
702 if top not in pp:
702 if top not in pp:
703 raise util.Abort(_("working directory revision is not qtip"))
703 raise util.Abort(_("working directory revision is not qtip"))
704 return top
704 return top
705 return None
705 return None
706 def check_localchanges(self, repo, force=False, refresh=True):
706 def check_localchanges(self, repo, force=False, refresh=True):
707 m, a, r, d = repo.status()[:4]
707 m, a, r, d = repo.status()[:4]
708 if m or a or r or d:
708 if m or a or r or d:
709 if not force:
709 if not force:
710 if refresh:
710 if refresh:
711 raise util.Abort(_("local changes found, refresh first"))
711 raise util.Abort(_("local changes found, refresh first"))
712 else:
712 else:
713 raise util.Abort(_("local changes found"))
713 raise util.Abort(_("local changes found"))
714 return m, a, r, d
714 return m, a, r, d
715
715
716 _reserved = ('series', 'status', 'guards')
716 _reserved = ('series', 'status', 'guards')
717 def check_reserved_name(self, name):
717 def check_reserved_name(self, name):
718 if (name in self._reserved or name.startswith('.hg')
718 if (name in self._reserved or name.startswith('.hg')
719 or name.startswith('.mq')):
719 or name.startswith('.mq')):
720 raise util.Abort(_('"%s" cannot be used as the name of a patch')
720 raise util.Abort(_('"%s" cannot be used as the name of a patch')
721 % name)
721 % name)
722
722
723 def new(self, repo, patchfn, *pats, **opts):
723 def new(self, repo, patchfn, *pats, **opts):
724 """options:
724 """options:
725 msg: a string or a no-argument function returning a string
725 msg: a string or a no-argument function returning a string
726 """
726 """
727 msg = opts.get('msg')
727 msg = opts.get('msg')
728 force = opts.get('force')
728 force = opts.get('force')
729 user = opts.get('user')
729 user = opts.get('user')
730 date = opts.get('date')
730 date = opts.get('date')
731 if date:
731 if date:
732 date = util.parsedate(date)
732 date = util.parsedate(date)
733 self.check_reserved_name(patchfn)
733 self.check_reserved_name(patchfn)
734 if os.path.exists(self.join(patchfn)):
734 if os.path.exists(self.join(patchfn)):
735 raise util.Abort(_('patch "%s" already exists') % patchfn)
735 raise util.Abort(_('patch "%s" already exists') % patchfn)
736 if opts.get('include') or opts.get('exclude') or pats:
736 if opts.get('include') or opts.get('exclude') or pats:
737 match = cmdutil.match(repo, pats, opts)
737 match = cmdutil.match(repo, pats, opts)
738 # detect missing files in pats
738 # detect missing files in pats
739 def badfn(f, msg):
739 def badfn(f, msg):
740 raise util.Abort('%s: %s' % (f, msg))
740 raise util.Abort('%s: %s' % (f, msg))
741 match.bad = badfn
741 match.bad = badfn
742 m, a, r, d = repo.status(match=match)[:4]
742 m, a, r, d = repo.status(match=match)[:4]
743 else:
743 else:
744 m, a, r, d = self.check_localchanges(repo, force)
744 m, a, r, d = self.check_localchanges(repo, force)
745 match = cmdutil.matchfiles(repo, m + a + r)
745 match = cmdutil.matchfiles(repo, m + a + r)
746 commitfiles = m + a + r
746 commitfiles = m + a + r
747 self.check_toppatch(repo)
747 self.check_toppatch(repo)
748 insert = self.full_series_end()
748 insert = self.full_series_end()
749 wlock = repo.wlock()
749 wlock = repo.wlock()
750 try:
750 try:
751 # if patch file write fails, abort early
751 # if patch file write fails, abort early
752 p = self.opener(patchfn, "w")
752 p = self.opener(patchfn, "w")
753 try:
753 try:
754 if date:
754 if date:
755 p.write("# HG changeset patch\n")
755 p.write("# HG changeset patch\n")
756 if user:
756 if user:
757 p.write("# User " + user + "\n")
757 p.write("# User " + user + "\n")
758 p.write("# Date %d %d\n\n" % date)
758 p.write("# Date %d %d\n\n" % date)
759 elif user:
759 elif user:
760 p.write("From: " + user + "\n\n")
760 p.write("From: " + user + "\n\n")
761
761
762 if hasattr(msg, '__call__'):
762 if hasattr(msg, '__call__'):
763 msg = msg()
763 msg = msg()
764 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
764 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
765 n = repo.commit(commitmsg, user, date, match=match, force=True)
765 n = repo.commit(commitmsg, user, date, match=match, force=True)
766 if n is None:
766 if n is None:
767 raise util.Abort(_("repo commit failed"))
767 raise util.Abort(_("repo commit failed"))
768 try:
768 try:
769 self.full_series[insert:insert] = [patchfn]
769 self.full_series[insert:insert] = [patchfn]
770 self.applied.append(statusentry(hex(n), patchfn))
770 self.applied.append(statusentry(hex(n), patchfn))
771 self.parse_series()
771 self.parse_series()
772 self.series_dirty = 1
772 self.series_dirty = 1
773 self.applied_dirty = 1
773 self.applied_dirty = 1
774 if msg:
774 if msg:
775 msg = msg + "\n\n"
775 msg = msg + "\n\n"
776 p.write(msg)
776 p.write(msg)
777 if commitfiles:
777 if commitfiles:
778 diffopts = self.diffopts()
778 diffopts = self.diffopts()
779 if opts.get('git'): diffopts.git = True
779 if opts.get('git'): diffopts.git = True
780 parent = self.qparents(repo, n)
780 parent = self.qparents(repo, n)
781 chunks = patch.diff(repo, node1=parent, node2=n,
781 chunks = patch.diff(repo, node1=parent, node2=n,
782 match=match, opts=diffopts)
782 match=match, opts=diffopts)
783 for chunk in chunks:
783 for chunk in chunks:
784 p.write(chunk)
784 p.write(chunk)
785 p.close()
785 p.close()
786 wlock.release()
786 wlock.release()
787 wlock = None
787 wlock = None
788 r = self.qrepo()
788 r = self.qrepo()
789 if r: r.add([patchfn])
789 if r: r.add([patchfn])
790 except:
790 except:
791 repo.rollback()
791 repo.rollback()
792 raise
792 raise
793 except Exception:
793 except Exception:
794 patchpath = self.join(patchfn)
794 patchpath = self.join(patchfn)
795 try:
795 try:
796 os.unlink(patchpath)
796 os.unlink(patchpath)
797 except:
797 except:
798 self.ui.warn(_('error unlinking %s\n') % patchpath)
798 self.ui.warn(_('error unlinking %s\n') % patchpath)
799 raise
799 raise
800 self.removeundo(repo)
800 self.removeundo(repo)
801 finally:
801 finally:
802 release(wlock)
802 release(wlock)
803
803
804 def strip(self, repo, rev, update=True, backup="all", force=None):
804 def strip(self, repo, rev, update=True, backup="all", force=None):
805 wlock = lock = None
805 wlock = lock = None
806 try:
806 try:
807 wlock = repo.wlock()
807 wlock = repo.wlock()
808 lock = repo.lock()
808 lock = repo.lock()
809
809
810 if update:
810 if update:
811 self.check_localchanges(repo, force=force, refresh=False)
811 self.check_localchanges(repo, force=force, refresh=False)
812 urev = self.qparents(repo, rev)
812 urev = self.qparents(repo, rev)
813 hg.clean(repo, urev)
813 hg.clean(repo, urev)
814 repo.dirstate.write()
814 repo.dirstate.write()
815
815
816 self.removeundo(repo)
816 self.removeundo(repo)
817 repair.strip(self.ui, repo, rev, backup)
817 repair.strip(self.ui, repo, rev, backup)
818 # strip may have unbundled a set of backed up revisions after
818 # strip may have unbundled a set of backed up revisions after
819 # the actual strip
819 # the actual strip
820 self.removeundo(repo)
820 self.removeundo(repo)
821 finally:
821 finally:
822 release(lock, wlock)
822 release(lock, wlock)
823
823
824 def isapplied(self, patch):
824 def isapplied(self, patch):
825 """returns (index, rev, patch)"""
825 """returns (index, rev, patch)"""
826 for i, a in enumerate(self.applied):
826 for i, a in enumerate(self.applied):
827 if a.name == patch:
827 if a.name == patch:
828 return (i, a.rev, a.name)
828 return (i, a.rev, a.name)
829 return None
829 return None
830
830
831 # if the exact patch name does not exist, we try a few
831 # if the exact patch name does not exist, we try a few
832 # variations. If strict is passed, we try only #1
832 # variations. If strict is passed, we try only #1
833 #
833 #
834 # 1) a number to indicate an offset in the series file
834 # 1) a number to indicate an offset in the series file
835 # 2) a unique substring of the patch name was given
835 # 2) a unique substring of the patch name was given
836 # 3) patchname[-+]num to indicate an offset in the series file
836 # 3) patchname[-+]num to indicate an offset in the series file
837 def lookup(self, patch, strict=False):
837 def lookup(self, patch, strict=False):
838 patch = patch and str(patch)
838 patch = patch and str(patch)
839
839
840 def partial_name(s):
840 def partial_name(s):
841 if s in self.series:
841 if s in self.series:
842 return s
842 return s
843 matches = [x for x in self.series if s in x]
843 matches = [x for x in self.series if s in x]
844 if len(matches) > 1:
844 if len(matches) > 1:
845 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
845 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
846 for m in matches:
846 for m in matches:
847 self.ui.warn(' %s\n' % m)
847 self.ui.warn(' %s\n' % m)
848 return None
848 return None
849 if matches:
849 if matches:
850 return matches[0]
850 return matches[0]
851 if len(self.series) > 0 and len(self.applied) > 0:
851 if len(self.series) > 0 and len(self.applied) > 0:
852 if s == 'qtip':
852 if s == 'qtip':
853 return self.series[self.series_end(True)-1]
853 return self.series[self.series_end(True)-1]
854 if s == 'qbase':
854 if s == 'qbase':
855 return self.series[0]
855 return self.series[0]
856 return None
856 return None
857
857
858 if patch is None:
858 if patch is None:
859 return None
859 return None
860 if patch in self.series:
860 if patch in self.series:
861 return patch
861 return patch
862
862
863 if not os.path.isfile(self.join(patch)):
863 if not os.path.isfile(self.join(patch)):
864 try:
864 try:
865 sno = int(patch)
865 sno = int(patch)
866 except(ValueError, OverflowError):
866 except(ValueError, OverflowError):
867 pass
867 pass
868 else:
868 else:
869 if -len(self.series) <= sno < len(self.series):
869 if -len(self.series) <= sno < len(self.series):
870 return self.series[sno]
870 return self.series[sno]
871
871
872 if not strict:
872 if not strict:
873 res = partial_name(patch)
873 res = partial_name(patch)
874 if res:
874 if res:
875 return res
875 return res
876 minus = patch.rfind('-')
876 minus = patch.rfind('-')
877 if minus >= 0:
877 if minus >= 0:
878 res = partial_name(patch[:minus])
878 res = partial_name(patch[:minus])
879 if res:
879 if res:
880 i = self.series.index(res)
880 i = self.series.index(res)
881 try:
881 try:
882 off = int(patch[minus+1:] or 1)
882 off = int(patch[minus+1:] or 1)
883 except(ValueError, OverflowError):
883 except(ValueError, OverflowError):
884 pass
884 pass
885 else:
885 else:
886 if i - off >= 0:
886 if i - off >= 0:
887 return self.series[i - off]
887 return self.series[i - off]
888 plus = patch.rfind('+')
888 plus = patch.rfind('+')
889 if plus >= 0:
889 if plus >= 0:
890 res = partial_name(patch[:plus])
890 res = partial_name(patch[:plus])
891 if res:
891 if res:
892 i = self.series.index(res)
892 i = self.series.index(res)
893 try:
893 try:
894 off = int(patch[plus+1:] or 1)
894 off = int(patch[plus+1:] or 1)
895 except(ValueError, OverflowError):
895 except(ValueError, OverflowError):
896 pass
896 pass
897 else:
897 else:
898 if i + off < len(self.series):
898 if i + off < len(self.series):
899 return self.series[i + off]
899 return self.series[i + off]
900 raise util.Abort(_("patch %s not in series") % patch)
900 raise util.Abort(_("patch %s not in series") % patch)
901
901
902 def push(self, repo, patch=None, force=False, list=False,
902 def push(self, repo, patch=None, force=False, list=False,
903 mergeq=None, all=False):
903 mergeq=None, all=False):
904 wlock = repo.wlock()
904 wlock = repo.wlock()
905 if repo.dirstate.parents()[0] not in repo.heads():
905 if repo.dirstate.parents()[0] not in repo.heads():
906 self.ui.status(_("(working directory not at a head)\n"))
906 self.ui.status(_("(working directory not at a head)\n"))
907
907
908 if not self.series:
908 if not self.series:
909 self.ui.warn(_('no patches in series\n'))
909 self.ui.warn(_('no patches in series\n'))
910 return 0
910 return 0
911
911
912 try:
912 try:
913 patch = self.lookup(patch)
913 patch = self.lookup(patch)
914 # Suppose our series file is: A B C and the current 'top'
914 # Suppose our series file is: A B C and the current 'top'
915 # patch is B. qpush C should be performed (moving forward)
915 # patch is B. qpush C should be performed (moving forward)
916 # qpush B is a NOP (no change) qpush A is an error (can't
916 # qpush B is a NOP (no change) qpush A is an error (can't
917 # go backwards with qpush)
917 # go backwards with qpush)
918 if patch:
918 if patch:
919 info = self.isapplied(patch)
919 info = self.isapplied(patch)
920 if info:
920 if info:
921 if info[0] < len(self.applied) - 1:
921 if info[0] < len(self.applied) - 1:
922 raise util.Abort(
922 raise util.Abort(
923 _("cannot push to a previous patch: %s") % patch)
923 _("cannot push to a previous patch: %s") % patch)
924 self.ui.warn(
924 self.ui.warn(
925 _('qpush: %s is already at the top\n') % patch)
925 _('qpush: %s is already at the top\n') % patch)
926 return
926 return
927 pushable, reason = self.pushable(patch)
927 pushable, reason = self.pushable(patch)
928 if not pushable:
928 if not pushable:
929 if reason:
929 if reason:
930 reason = _('guarded by %r') % reason
930 reason = _('guarded by %r') % reason
931 else:
931 else:
932 reason = _('no matching guards')
932 reason = _('no matching guards')
933 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
933 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
934 return 1
934 return 1
935 elif all:
935 elif all:
936 patch = self.series[-1]
936 patch = self.series[-1]
937 if self.isapplied(patch):
937 if self.isapplied(patch):
938 self.ui.warn(_('all patches are currently applied\n'))
938 self.ui.warn(_('all patches are currently applied\n'))
939 return 0
939 return 0
940
940
941 # Following the above example, starting at 'top' of B:
941 # Following the above example, starting at 'top' of B:
942 # qpush should be performed (pushes C), but a subsequent
942 # qpush should be performed (pushes C), but a subsequent
943 # qpush without an argument is an error (nothing to
943 # qpush without an argument is an error (nothing to
944 # apply). This allows a loop of "...while hg qpush..." to
944 # apply). This allows a loop of "...while hg qpush..." to
945 # work as it detects an error when done
945 # work as it detects an error when done
946 start = self.series_end()
946 start = self.series_end()
947 if start == len(self.series):
947 if start == len(self.series):
948 self.ui.warn(_('patch series already fully applied\n'))
948 self.ui.warn(_('patch series already fully applied\n'))
949 return 1
949 return 1
950 if not force:
950 if not force:
951 self.check_localchanges(repo)
951 self.check_localchanges(repo)
952
952
953 self.applied_dirty = 1
953 self.applied_dirty = 1
954 if start > 0:
954 if start > 0:
955 self.check_toppatch(repo)
955 self.check_toppatch(repo)
956 if not patch:
956 if not patch:
957 patch = self.series[start]
957 patch = self.series[start]
958 end = start + 1
958 end = start + 1
959 else:
959 else:
960 end = self.series.index(patch, start) + 1
960 end = self.series.index(patch, start) + 1
961 s = self.series[start:end]
961 s = self.series[start:end]
962 all_files = {}
962 all_files = {}
963 try:
963 try:
964 if mergeq:
964 if mergeq:
965 ret = self.mergepatch(repo, mergeq, s)
965 ret = self.mergepatch(repo, mergeq, s)
966 else:
966 else:
967 ret = self.apply(repo, s, list, all_files=all_files)
967 ret = self.apply(repo, s, list, all_files=all_files)
968 except:
968 except:
969 self.ui.warn(_('cleaning up working directory...'))
969 self.ui.warn(_('cleaning up working directory...'))
970 node = repo.dirstate.parents()[0]
970 node = repo.dirstate.parents()[0]
971 hg.revert(repo, node, None)
971 hg.revert(repo, node, None)
972 unknown = repo.status(unknown=True)[4]
972 unknown = repo.status(unknown=True)[4]
973 # only remove unknown files that we know we touched or
973 # only remove unknown files that we know we touched or
974 # created while patching
974 # created while patching
975 for f in unknown:
975 for f in unknown:
976 if f in all_files:
976 if f in all_files:
977 util.unlink(repo.wjoin(f))
977 util.unlink(repo.wjoin(f))
978 self.ui.warn(_('done\n'))
978 self.ui.warn(_('done\n'))
979 raise
979 raise
980 top = self.applied[-1].name
980 top = self.applied[-1].name
981 if ret[0]:
981 if ret[0]:
982 self.ui.write(_("errors during apply, please fix and "
982 self.ui.write(_("errors during apply, please fix and "
983 "refresh %s\n") % top)
983 "refresh %s\n") % top)
984 else:
984 else:
985 self.ui.write(_("now at: %s\n") % top)
985 self.ui.write(_("now at: %s\n") % top)
986 return ret[0]
986 return ret[0]
987 finally:
987 finally:
988 wlock.release()
988 wlock.release()
989
989
990 def pop(self, repo, patch=None, force=False, update=True, all=False):
990 def pop(self, repo, patch=None, force=False, update=True, all=False):
991 def getfile(f, rev, flags):
991 def getfile(f, rev, flags):
992 t = repo.file(f).read(rev)
992 t = repo.file(f).read(rev)
993 repo.wwrite(f, t, flags)
993 repo.wwrite(f, t, flags)
994
994
995 wlock = repo.wlock()
995 wlock = repo.wlock()
996 try:
996 try:
997 if patch:
997 if patch:
998 # index, rev, patch
998 # index, rev, patch
999 info = self.isapplied(patch)
999 info = self.isapplied(patch)
1000 if not info:
1000 if not info:
1001 patch = self.lookup(patch)
1001 patch = self.lookup(patch)
1002 info = self.isapplied(patch)
1002 info = self.isapplied(patch)
1003 if not info:
1003 if not info:
1004 raise util.Abort(_("patch %s is not applied") % patch)
1004 raise util.Abort(_("patch %s is not applied") % patch)
1005
1005
1006 if len(self.applied) == 0:
1006 if len(self.applied) == 0:
1007 # Allow qpop -a to work repeatedly,
1007 # Allow qpop -a to work repeatedly,
1008 # but not qpop without an argument
1008 # but not qpop without an argument
1009 self.ui.warn(_("no patches applied\n"))
1009 self.ui.warn(_("no patches applied\n"))
1010 return not all
1010 return not all
1011
1011
1012 if all:
1012 if all:
1013 start = 0
1013 start = 0
1014 elif patch:
1014 elif patch:
1015 start = info[0] + 1
1015 start = info[0] + 1
1016 else:
1016 else:
1017 start = len(self.applied) - 1
1017 start = len(self.applied) - 1
1018
1018
1019 if start >= len(self.applied):
1019 if start >= len(self.applied):
1020 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1020 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1021 return
1021 return
1022
1022
1023 if not update:
1023 if not update:
1024 parents = repo.dirstate.parents()
1024 parents = repo.dirstate.parents()
1025 rr = [ bin(x.rev) for x in self.applied ]
1025 rr = [ bin(x.rev) for x in self.applied ]
1026 for p in parents:
1026 for p in parents:
1027 if p in rr:
1027 if p in rr:
1028 self.ui.warn(_("qpop: forcing dirstate update\n"))
1028 self.ui.warn(_("qpop: forcing dirstate update\n"))
1029 update = True
1029 update = True
1030 else:
1030 else:
1031 parents = [p.hex() for p in repo[None].parents()]
1031 parents = [p.hex() for p in repo[None].parents()]
1032 needupdate = False
1032 needupdate = False
1033 for entry in self.applied[start:]:
1033 for entry in self.applied[start:]:
1034 if entry.rev in parents:
1034 if entry.rev in parents:
1035 needupdate = True
1035 needupdate = True
1036 break
1036 break
1037 update = needupdate
1037 update = needupdate
1038
1038
1039 if not force and update:
1039 if not force and update:
1040 self.check_localchanges(repo)
1040 self.check_localchanges(repo)
1041
1041
1042 self.applied_dirty = 1
1042 self.applied_dirty = 1
1043 end = len(self.applied)
1043 end = len(self.applied)
1044 rev = bin(self.applied[start].rev)
1044 rev = bin(self.applied[start].rev)
1045 if update:
1045 if update:
1046 top = self.check_toppatch(repo)
1046 top = self.check_toppatch(repo)
1047
1047
1048 try:
1048 try:
1049 heads = repo.changelog.heads(rev)
1049 heads = repo.changelog.heads(rev)
1050 except error.LookupError:
1050 except error.LookupError:
1051 node = short(rev)
1051 node = short(rev)
1052 raise util.Abort(_('trying to pop unknown node %s') % node)
1052 raise util.Abort(_('trying to pop unknown node %s') % node)
1053
1053
1054 if heads != [bin(self.applied[-1].rev)]:
1054 if heads != [bin(self.applied[-1].rev)]:
1055 raise util.Abort(_("popping would remove a revision not "
1055 raise util.Abort(_("popping would remove a revision not "
1056 "managed by this patch queue"))
1056 "managed by this patch queue"))
1057
1057
1058 # we know there are no local changes, so we can make a simplified
1058 # we know there are no local changes, so we can make a simplified
1059 # form of hg.update.
1059 # form of hg.update.
1060 if update:
1060 if update:
1061 qp = self.qparents(repo, rev)
1061 qp = self.qparents(repo, rev)
1062 changes = repo.changelog.read(qp)
1062 changes = repo.changelog.read(qp)
1063 mmap = repo.manifest.read(changes[0])
1063 mmap = repo.manifest.read(changes[0])
1064 m, a, r, d = repo.status(qp, top)[:4]
1064 m, a, r, d = repo.status(qp, top)[:4]
1065 if d:
1065 if d:
1066 raise util.Abort(_("deletions found between repo revs"))
1066 raise util.Abort(_("deletions found between repo revs"))
1067 for f in m:
1067 for f in m:
1068 getfile(f, mmap[f], mmap.flags(f))
1068 getfile(f, mmap[f], mmap.flags(f))
1069 for f in r:
1069 for f in r:
1070 getfile(f, mmap[f], mmap.flags(f))
1070 getfile(f, mmap[f], mmap.flags(f))
1071 for f in m + r:
1071 for f in m + r:
1072 repo.dirstate.normal(f)
1072 repo.dirstate.normal(f)
1073 for f in a:
1073 for f in a:
1074 try:
1074 try:
1075 os.unlink(repo.wjoin(f))
1075 os.unlink(repo.wjoin(f))
1076 except OSError, e:
1076 except OSError, e:
1077 if e.errno != errno.ENOENT:
1077 if e.errno != errno.ENOENT:
1078 raise
1078 raise
1079 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1079 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1080 except: pass
1080 except: pass
1081 repo.dirstate.forget(f)
1081 repo.dirstate.forget(f)
1082 repo.dirstate.setparents(qp, nullid)
1082 repo.dirstate.setparents(qp, nullid)
1083 del self.applied[start:end]
1083 del self.applied[start:end]
1084 self.strip(repo, rev, update=False, backup='strip')
1084 self.strip(repo, rev, update=False, backup='strip')
1085 if len(self.applied):
1085 if len(self.applied):
1086 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1086 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1087 else:
1087 else:
1088 self.ui.write(_("patch queue now empty\n"))
1088 self.ui.write(_("patch queue now empty\n"))
1089 finally:
1089 finally:
1090 wlock.release()
1090 wlock.release()
1091
1091
1092 def diff(self, repo, pats, opts):
1092 def diff(self, repo, pats, opts):
1093 top = self.check_toppatch(repo)
1093 top = self.check_toppatch(repo)
1094 if not top:
1094 if not top:
1095 self.ui.write(_("no patches applied\n"))
1095 self.ui.write(_("no patches applied\n"))
1096 return
1096 return
1097 qp = self.qparents(repo, top)
1097 qp = self.qparents(repo, top)
1098 self._diffopts = patch.diffopts(self.ui, opts)
1098 self._diffopts = patch.diffopts(self.ui, opts)
1099 self.printdiff(repo, qp, files=pats, opts=opts)
1099 self.printdiff(repo, qp, files=pats, opts=opts)
1100
1100
1101 def refresh(self, repo, pats=None, **opts):
1101 def refresh(self, repo, pats=None, **opts):
1102 if len(self.applied) == 0:
1102 if len(self.applied) == 0:
1103 self.ui.write(_("no patches applied\n"))
1103 self.ui.write(_("no patches applied\n"))
1104 return 1
1104 return 1
1105 msg = opts.get('msg', '').rstrip()
1105 msg = opts.get('msg', '').rstrip()
1106 newuser = opts.get('user')
1106 newuser = opts.get('user')
1107 newdate = opts.get('date')
1107 newdate = opts.get('date')
1108 if newdate:
1108 if newdate:
1109 newdate = '%d %d' % util.parsedate(newdate)
1109 newdate = '%d %d' % util.parsedate(newdate)
1110 wlock = repo.wlock()
1110 wlock = repo.wlock()
1111 try:
1111 try:
1112 self.check_toppatch(repo)
1112 self.check_toppatch(repo)
1113 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1113 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1114 top = bin(top)
1114 top = bin(top)
1115 if repo.changelog.heads(top) != [top]:
1115 if repo.changelog.heads(top) != [top]:
1116 raise util.Abort(_("cannot refresh a revision with children"))
1116 raise util.Abort(_("cannot refresh a revision with children"))
1117 cparents = repo.changelog.parents(top)
1117 cparents = repo.changelog.parents(top)
1118 patchparent = self.qparents(repo, top)
1118 patchparent = self.qparents(repo, top)
1119 ph = patchheader(self.join(patchfn))
1119 ph = patchheader(self.join(patchfn))
1120
1120
1121 patchf = self.opener(patchfn, 'r')
1121 patchf = self.opener(patchfn, 'r')
1122
1122
1123 # if the patch was a git patch, refresh it as a git patch
1123 # if the patch was a git patch, refresh it as a git patch
1124 for line in patchf:
1124 for line in patchf:
1125 if line.startswith('diff --git'):
1125 if line.startswith('diff --git'):
1126 self.diffopts().git = True
1126 self.diffopts().git = True
1127 break
1127 break
1128
1128
1129 if msg:
1129 if msg:
1130 ph.setmessage(msg)
1130 ph.setmessage(msg)
1131 if newuser:
1131 if newuser:
1132 ph.setuser(newuser)
1132 ph.setuser(newuser)
1133 if newdate:
1133 if newdate:
1134 ph.setdate(newdate)
1134 ph.setdate(newdate)
1135
1135
1136 # only commit new patch when write is complete
1136 # only commit new patch when write is complete
1137 patchf = self.opener(patchfn, 'w', atomictemp=True)
1137 patchf = self.opener(patchfn, 'w', atomictemp=True)
1138
1138
1139 patchf.seek(0)
1139 patchf.seek(0)
1140 patchf.truncate()
1140 patchf.truncate()
1141
1141
1142 comments = str(ph)
1142 comments = str(ph)
1143 if comments:
1143 if comments:
1144 patchf.write(comments)
1144 patchf.write(comments)
1145
1145
1146 if opts.get('git'):
1146 if opts.get('git'):
1147 self.diffopts().git = True
1147 self.diffopts().git = True
1148 tip = repo.changelog.tip()
1148 tip = repo.changelog.tip()
1149 if top == tip:
1149 if top == tip:
1150 # if the top of our patch queue is also the tip, there is an
1150 # if the top of our patch queue is also the tip, there is an
1151 # optimization here. We update the dirstate in place and strip
1151 # optimization here. We update the dirstate in place and strip
1152 # off the tip commit. Then just commit the current directory
1152 # off the tip commit. Then just commit the current directory
1153 # tree. We can also send repo.commit the list of files
1153 # tree. We can also send repo.commit the list of files
1154 # changed to speed up the diff
1154 # changed to speed up the diff
1155 #
1155 #
1156 # in short mode, we only diff the files included in the
1156 # in short mode, we only diff the files included in the
1157 # patch already plus specified files
1157 # patch already plus specified files
1158 #
1158 #
1159 # this should really read:
1159 # this should really read:
1160 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1160 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1161 # but we do it backwards to take advantage of manifest/chlog
1161 # but we do it backwards to take advantage of manifest/chlog
1162 # caching against the next repo.status call
1162 # caching against the next repo.status call
1163 #
1163 #
1164 mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
1164 mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
1165 changes = repo.changelog.read(tip)
1165 changes = repo.changelog.read(tip)
1166 man = repo.manifest.read(changes[0])
1166 man = repo.manifest.read(changes[0])
1167 aaa = aa[:]
1167 aaa = aa[:]
1168 matchfn = cmdutil.match(repo, pats, opts)
1168 matchfn = cmdutil.match(repo, pats, opts)
1169 if opts.get('short'):
1169 if opts.get('short'):
1170 # if amending a patch, we start with existing
1170 # if amending a patch, we start with existing
1171 # files plus specified files - unfiltered
1171 # files plus specified files - unfiltered
1172 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1172 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1173 # filter with inc/exl options
1173 # filter with inc/exl options
1174 matchfn = cmdutil.match(repo, opts=opts)
1174 matchfn = cmdutil.match(repo, opts=opts)
1175 else:
1175 else:
1176 match = cmdutil.matchall(repo)
1176 match = cmdutil.matchall(repo)
1177 m, a, r, d = repo.status(match=match)[:4]
1177 m, a, r, d = repo.status(match=match)[:4]
1178
1178
1179 # we might end up with files that were added between
1179 # we might end up with files that were added between
1180 # tip and the dirstate parent, but then changed in the
1180 # tip and the dirstate parent, but then changed in the
1181 # local dirstate. in this case, we want them to only
1181 # local dirstate. in this case, we want them to only
1182 # show up in the added section
1182 # show up in the added section
1183 for x in m:
1183 for x in m:
1184 if x not in aa:
1184 if x not in aa:
1185 mm.append(x)
1185 mm.append(x)
1186 # we might end up with files added by the local dirstate that
1186 # we might end up with files added by the local dirstate that
1187 # were deleted by the patch. In this case, they should only
1187 # were deleted by the patch. In this case, they should only
1188 # show up in the changed section.
1188 # show up in the changed section.
1189 for x in a:
1189 for x in a:
1190 if x in dd:
1190 if x in dd:
1191 del dd[dd.index(x)]
1191 del dd[dd.index(x)]
1192 mm.append(x)
1192 mm.append(x)
1193 else:
1193 else:
1194 aa.append(x)
1194 aa.append(x)
1195 # make sure any files deleted in the local dirstate
1195 # make sure any files deleted in the local dirstate
1196 # are not in the add or change column of the patch
1196 # are not in the add or change column of the patch
1197 forget = []
1197 forget = []
1198 for x in d + r:
1198 for x in d + r:
1199 if x in aa:
1199 if x in aa:
1200 del aa[aa.index(x)]
1200 del aa[aa.index(x)]
1201 forget.append(x)
1201 forget.append(x)
1202 continue
1202 continue
1203 elif x in mm:
1203 elif x in mm:
1204 del mm[mm.index(x)]
1204 del mm[mm.index(x)]
1205 dd.append(x)
1205 dd.append(x)
1206
1206
1207 m = list(set(mm))
1207 m = list(set(mm))
1208 r = list(set(dd))
1208 r = list(set(dd))
1209 a = list(set(aa))
1209 a = list(set(aa))
1210 c = [filter(matchfn, l) for l in (m, a, r)]
1210 c = [filter(matchfn, l) for l in (m, a, r)]
1211 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1211 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1212 chunks = patch.diff(repo, patchparent, match=match,
1212 chunks = patch.diff(repo, patchparent, match=match,
1213 changes=c, opts=self.diffopts())
1213 changes=c, opts=self.diffopts())
1214 for chunk in chunks:
1214 for chunk in chunks:
1215 patchf.write(chunk)
1215 patchf.write(chunk)
1216
1216
1217 try:
1217 try:
1218 if self.diffopts().git:
1218 if self.diffopts().git:
1219 copies = {}
1219 copies = {}
1220 for dst in a:
1220 for dst in a:
1221 src = repo.dirstate.copied(dst)
1221 src = repo.dirstate.copied(dst)
1222 # during qfold, the source file for copies may
1222 # during qfold, the source file for copies may
1223 # be removed. Treat this as a simple add.
1223 # be removed. Treat this as a simple add.
1224 if src is not None and src in repo.dirstate:
1224 if src is not None and src in repo.dirstate:
1225 copies.setdefault(src, []).append(dst)
1225 copies.setdefault(src, []).append(dst)
1226 repo.dirstate.add(dst)
1226 repo.dirstate.add(dst)
1227 # remember the copies between patchparent and tip
1227 # remember the copies between patchparent and tip
1228 for dst in aaa:
1228 for dst in aaa:
1229 f = repo.file(dst)
1229 f = repo.file(dst)
1230 src = f.renamed(man[dst])
1230 src = f.renamed(man[dst])
1231 if src:
1231 if src:
1232 copies.setdefault(src[0], []).extend(copies.get(dst, []))
1232 copies.setdefault(src[0], []).extend(copies.get(dst, []))
1233 if dst in a:
1233 if dst in a:
1234 copies[src[0]].append(dst)
1234 copies[src[0]].append(dst)
1235 # we can't copy a file created by the patch itself
1235 # we can't copy a file created by the patch itself
1236 if dst in copies:
1236 if dst in copies:
1237 del copies[dst]
1237 del copies[dst]
1238 for src, dsts in copies.iteritems():
1238 for src, dsts in copies.iteritems():
1239 for dst in dsts:
1239 for dst in dsts:
1240 repo.dirstate.copy(src, dst)
1240 repo.dirstate.copy(src, dst)
1241 else:
1241 else:
1242 for dst in a:
1242 for dst in a:
1243 repo.dirstate.add(dst)
1243 repo.dirstate.add(dst)
1244 # Drop useless copy information
1244 # Drop useless copy information
1245 for f in list(repo.dirstate.copies()):
1245 for f in list(repo.dirstate.copies()):
1246 repo.dirstate.copy(None, f)
1246 repo.dirstate.copy(None, f)
1247 for f in r:
1247 for f in r:
1248 repo.dirstate.remove(f)
1248 repo.dirstate.remove(f)
1249 # if the patch excludes a modified file, mark that
1249 # if the patch excludes a modified file, mark that
1250 # file with mtime=0 so status can see it.
1250 # file with mtime=0 so status can see it.
1251 mm = []
1251 mm = []
1252 for i in xrange(len(m)-1, -1, -1):
1252 for i in xrange(len(m)-1, -1, -1):
1253 if not matchfn(m[i]):
1253 if not matchfn(m[i]):
1254 mm.append(m[i])
1254 mm.append(m[i])
1255 del m[i]
1255 del m[i]
1256 for f in m:
1256 for f in m:
1257 repo.dirstate.normal(f)
1257 repo.dirstate.normal(f)
1258 for f in mm:
1258 for f in mm:
1259 repo.dirstate.normallookup(f)
1259 repo.dirstate.normallookup(f)
1260 for f in forget:
1260 for f in forget:
1261 repo.dirstate.forget(f)
1261 repo.dirstate.forget(f)
1262
1262
1263 if not msg:
1263 if not msg:
1264 if not ph.message:
1264 if not ph.message:
1265 message = "[mq]: %s\n" % patchfn
1265 message = "[mq]: %s\n" % patchfn
1266 else:
1266 else:
1267 message = "\n".join(ph.message)
1267 message = "\n".join(ph.message)
1268 else:
1268 else:
1269 message = msg
1269 message = msg
1270
1270
1271 user = ph.user or changes[1]
1271 user = ph.user or changes[1]
1272
1272
1273 # assumes strip can roll itself back if interrupted
1273 # assumes strip can roll itself back if interrupted
1274 repo.dirstate.setparents(*cparents)
1274 repo.dirstate.setparents(*cparents)
1275 self.applied.pop()
1275 self.applied.pop()
1276 self.applied_dirty = 1
1276 self.applied_dirty = 1
1277 self.strip(repo, top, update=False,
1277 self.strip(repo, top, update=False,
1278 backup='strip')
1278 backup='strip')
1279 except:
1279 except:
1280 repo.dirstate.invalidate()
1280 repo.dirstate.invalidate()
1281 raise
1281 raise
1282
1282
1283 try:
1283 try:
1284 # might be nice to attempt to roll back strip after this
1284 # might be nice to attempt to roll back strip after this
1285 patchf.rename()
1285 patchf.rename()
1286 n = repo.commit(message, user, ph.date, match=match,
1286 n = repo.commit(message, user, ph.date, match=match,
1287 force=True)
1287 force=True)
1288 self.applied.append(statusentry(hex(n), patchfn))
1288 self.applied.append(statusentry(hex(n), patchfn))
1289 except:
1289 except:
1290 ctx = repo[cparents[0]]
1290 ctx = repo[cparents[0]]
1291 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1291 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1292 self.save_dirty()
1292 self.save_dirty()
1293 self.ui.warn(_('refresh interrupted while patch was popped! '
1293 self.ui.warn(_('refresh interrupted while patch was popped! '
1294 '(revert --all, qpush to recover)\n'))
1294 '(revert --all, qpush to recover)\n'))
1295 raise
1295 raise
1296 else:
1296 else:
1297 self.printdiff(repo, patchparent, fp=patchf)
1297 self.printdiff(repo, patchparent, fp=patchf)
1298 patchf.rename()
1298 patchf.rename()
1299 added = repo.status()[1]
1299 added = repo.status()[1]
1300 for a in added:
1300 for a in added:
1301 f = repo.wjoin(a)
1301 f = repo.wjoin(a)
1302 try:
1302 try:
1303 os.unlink(f)
1303 os.unlink(f)
1304 except OSError, e:
1304 except OSError, e:
1305 if e.errno != errno.ENOENT:
1305 if e.errno != errno.ENOENT:
1306 raise
1306 raise
1307 try: os.removedirs(os.path.dirname(f))
1307 try: os.removedirs(os.path.dirname(f))
1308 except: pass
1308 except: pass
1309 # forget the file copies in the dirstate
1309 # forget the file copies in the dirstate
1310 # push should readd the files later on
1310 # push should readd the files later on
1311 repo.dirstate.forget(a)
1311 repo.dirstate.forget(a)
1312 self.pop(repo, force=True)
1312 self.pop(repo, force=True)
1313 self.push(repo, force=True)
1313 self.push(repo, force=True)
1314 finally:
1314 finally:
1315 wlock.release()
1315 wlock.release()
1316 self.removeundo(repo)
1316 self.removeundo(repo)
1317
1317
1318 def init(self, repo, create=False):
1318 def init(self, repo, create=False):
1319 if not create and os.path.isdir(self.path):
1319 if not create and os.path.isdir(self.path):
1320 raise util.Abort(_("patch queue directory already exists"))
1320 raise util.Abort(_("patch queue directory already exists"))
1321 try:
1321 try:
1322 os.mkdir(self.path)
1322 os.mkdir(self.path)
1323 except OSError, inst:
1323 except OSError, inst:
1324 if inst.errno != errno.EEXIST or not create:
1324 if inst.errno != errno.EEXIST or not create:
1325 raise
1325 raise
1326 if create:
1326 if create:
1327 return self.qrepo(create=True)
1327 return self.qrepo(create=True)
1328
1328
1329 def unapplied(self, repo, patch=None):
1329 def unapplied(self, repo, patch=None):
1330 if patch and patch not in self.series:
1330 if patch and patch not in self.series:
1331 raise util.Abort(_("patch %s is not in series file") % patch)
1331 raise util.Abort(_("patch %s is not in series file") % patch)
1332 if not patch:
1332 if not patch:
1333 start = self.series_end()
1333 start = self.series_end()
1334 else:
1334 else:
1335 start = self.series.index(patch) + 1
1335 start = self.series.index(patch) + 1
1336 unapplied = []
1336 unapplied = []
1337 for i in xrange(start, len(self.series)):
1337 for i in xrange(start, len(self.series)):
1338 pushable, reason = self.pushable(i)
1338 pushable, reason = self.pushable(i)
1339 if pushable:
1339 if pushable:
1340 unapplied.append((i, self.series[i]))
1340 unapplied.append((i, self.series[i]))
1341 self.explain_pushable(i)
1341 self.explain_pushable(i)
1342 return unapplied
1342 return unapplied
1343
1343
1344 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1344 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1345 summary=False):
1345 summary=False):
1346 def displayname(patchname):
1346 def displayname(patchname):
1347 if summary:
1347 if summary:
1348 ph = patchheader(self.join(patchname))
1348 ph = patchheader(self.join(patchname))
1349 msg = ph.message
1349 msg = ph.message
1350 msg = msg and ': ' + msg[0] or ': '
1350 msg = msg and ': ' + msg[0] or ': '
1351 else:
1351 else:
1352 msg = ''
1352 msg = ''
1353 return '%s%s' % (patchname, msg)
1353 return '%s%s' % (patchname, msg)
1354
1354
1355 applied = set([p.name for p in self.applied])
1355 applied = set([p.name for p in self.applied])
1356 if length is None:
1356 if length is None:
1357 length = len(self.series) - start
1357 length = len(self.series) - start
1358 if not missing:
1358 if not missing:
1359 for i in xrange(start, start+length):
1359 for i in xrange(start, start+length):
1360 patch = self.series[i]
1360 patch = self.series[i]
1361 if patch in applied:
1361 if patch in applied:
1362 stat = 'A'
1362 stat = 'A'
1363 elif self.pushable(i)[0]:
1363 elif self.pushable(i)[0]:
1364 stat = 'U'
1364 stat = 'U'
1365 else:
1365 else:
1366 stat = 'G'
1366 stat = 'G'
1367 pfx = ''
1367 pfx = ''
1368 if self.ui.verbose:
1368 if self.ui.verbose:
1369 pfx = '%d %s ' % (i, stat)
1369 pfx = '%d %s ' % (i, stat)
1370 elif status and status != stat:
1370 elif status and status != stat:
1371 continue
1371 continue
1372 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1372 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1373 else:
1373 else:
1374 msng_list = []
1374 msng_list = []
1375 for root, dirs, files in os.walk(self.path):
1375 for root, dirs, files in os.walk(self.path):
1376 d = root[len(self.path) + 1:]
1376 d = root[len(self.path) + 1:]
1377 for f in files:
1377 for f in files:
1378 fl = os.path.join(d, f)
1378 fl = os.path.join(d, f)
1379 if (fl not in self.series and
1379 if (fl not in self.series and
1380 fl not in (self.status_path, self.series_path,
1380 fl not in (self.status_path, self.series_path,
1381 self.guards_path)
1381 self.guards_path)
1382 and not fl.startswith('.')):
1382 and not fl.startswith('.')):
1383 msng_list.append(fl)
1383 msng_list.append(fl)
1384 for x in sorted(msng_list):
1384 for x in sorted(msng_list):
1385 pfx = self.ui.verbose and ('D ') or ''
1385 pfx = self.ui.verbose and ('D ') or ''
1386 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1386 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1387
1387
1388 def issaveline(self, l):
1388 def issaveline(self, l):
1389 if l.name == '.hg.patches.save.line':
1389 if l.name == '.hg.patches.save.line':
1390 return True
1390 return True
1391
1391
1392 def qrepo(self, create=False):
1392 def qrepo(self, create=False):
1393 if create or os.path.isdir(self.join(".hg")):
1393 if create or os.path.isdir(self.join(".hg")):
1394 return hg.repository(self.ui, path=self.path, create=create)
1394 return hg.repository(self.ui, path=self.path, create=create)
1395
1395
1396 def restore(self, repo, rev, delete=None, qupdate=None):
1396 def restore(self, repo, rev, delete=None, qupdate=None):
1397 c = repo.changelog.read(rev)
1397 c = repo.changelog.read(rev)
1398 desc = c[4].strip()
1398 desc = c[4].strip()
1399 lines = desc.splitlines()
1399 lines = desc.splitlines()
1400 i = 0
1400 i = 0
1401 datastart = None
1401 datastart = None
1402 series = []
1402 series = []
1403 applied = []
1403 applied = []
1404 qpp = None
1404 qpp = None
1405 for i, line in enumerate(lines):
1405 for i, line in enumerate(lines):
1406 if line == 'Patch Data:':
1406 if line == 'Patch Data:':
1407 datastart = i + 1
1407 datastart = i + 1
1408 elif line.startswith('Dirstate:'):
1408 elif line.startswith('Dirstate:'):
1409 l = line.rstrip()
1409 l = line.rstrip()
1410 l = l[10:].split(' ')
1410 l = l[10:].split(' ')
1411 qpp = [ bin(x) for x in l ]
1411 qpp = [ bin(x) for x in l ]
1412 elif datastart != None:
1412 elif datastart != None:
1413 l = line.rstrip()
1413 l = line.rstrip()
1414 se = statusentry(l)
1414 se = statusentry(l)
1415 file_ = se.name
1415 file_ = se.name
1416 if se.rev:
1416 if se.rev:
1417 applied.append(se)
1417 applied.append(se)
1418 else:
1418 else:
1419 series.append(file_)
1419 series.append(file_)
1420 if datastart is None:
1420 if datastart is None:
1421 self.ui.warn(_("No saved patch data found\n"))
1421 self.ui.warn(_("No saved patch data found\n"))
1422 return 1
1422 return 1
1423 self.ui.warn(_("restoring status: %s\n") % lines[0])
1423 self.ui.warn(_("restoring status: %s\n") % lines[0])
1424 self.full_series = series
1424 self.full_series = series
1425 self.applied = applied
1425 self.applied = applied
1426 self.parse_series()
1426 self.parse_series()
1427 self.series_dirty = 1
1427 self.series_dirty = 1
1428 self.applied_dirty = 1
1428 self.applied_dirty = 1
1429 heads = repo.changelog.heads()
1429 heads = repo.changelog.heads()
1430 if delete:
1430 if delete:
1431 if rev not in heads:
1431 if rev not in heads:
1432 self.ui.warn(_("save entry has children, leaving it alone\n"))
1432 self.ui.warn(_("save entry has children, leaving it alone\n"))
1433 else:
1433 else:
1434 self.ui.warn(_("removing save entry %s\n") % short(rev))
1434 self.ui.warn(_("removing save entry %s\n") % short(rev))
1435 pp = repo.dirstate.parents()
1435 pp = repo.dirstate.parents()
1436 if rev in pp:
1436 if rev in pp:
1437 update = True
1437 update = True
1438 else:
1438 else:
1439 update = False
1439 update = False
1440 self.strip(repo, rev, update=update, backup='strip')
1440 self.strip(repo, rev, update=update, backup='strip')
1441 if qpp:
1441 if qpp:
1442 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1442 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1443 (short(qpp[0]), short(qpp[1])))
1443 (short(qpp[0]), short(qpp[1])))
1444 if qupdate:
1444 if qupdate:
1445 self.ui.status(_("queue directory updating\n"))
1445 self.ui.status(_("queue directory updating\n"))
1446 r = self.qrepo()
1446 r = self.qrepo()
1447 if not r:
1447 if not r:
1448 self.ui.warn(_("Unable to load queue repository\n"))
1448 self.ui.warn(_("Unable to load queue repository\n"))
1449 return 1
1449 return 1
1450 hg.clean(r, qpp[0])
1450 hg.clean(r, qpp[0])
1451
1451
1452 def save(self, repo, msg=None):
1452 def save(self, repo, msg=None):
1453 if len(self.applied) == 0:
1453 if len(self.applied) == 0:
1454 self.ui.warn(_("save: no patches applied, exiting\n"))
1454 self.ui.warn(_("save: no patches applied, exiting\n"))
1455 return 1
1455 return 1
1456 if self.issaveline(self.applied[-1]):
1456 if self.issaveline(self.applied[-1]):
1457 self.ui.warn(_("status is already saved\n"))
1457 self.ui.warn(_("status is already saved\n"))
1458 return 1
1458 return 1
1459
1459
1460 ar = [ ':' + x for x in self.full_series ]
1460 ar = [ ':' + x for x in self.full_series ]
1461 if not msg:
1461 if not msg:
1462 msg = _("hg patches saved state")
1462 msg = _("hg patches saved state")
1463 else:
1463 else:
1464 msg = "hg patches: " + msg.rstrip('\r\n')
1464 msg = "hg patches: " + msg.rstrip('\r\n')
1465 r = self.qrepo()
1465 r = self.qrepo()
1466 if r:
1466 if r:
1467 pp = r.dirstate.parents()
1467 pp = r.dirstate.parents()
1468 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1468 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1469 msg += "\n\nPatch Data:\n"
1469 msg += "\n\nPatch Data:\n"
1470 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1470 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1471 "\n".join(ar) + '\n' or "")
1471 "\n".join(ar) + '\n' or "")
1472 n = repo.commit(text, force=True)
1472 n = repo.commit(text, force=True)
1473 if not n:
1473 if not n:
1474 self.ui.warn(_("repo commit failed\n"))
1474 self.ui.warn(_("repo commit failed\n"))
1475 return 1
1475 return 1
1476 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1476 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1477 self.applied_dirty = 1
1477 self.applied_dirty = 1
1478 self.removeundo(repo)
1478 self.removeundo(repo)
1479
1479
1480 def full_series_end(self):
1480 def full_series_end(self):
1481 if len(self.applied) > 0:
1481 if len(self.applied) > 0:
1482 p = self.applied[-1].name
1482 p = self.applied[-1].name
1483 end = self.find_series(p)
1483 end = self.find_series(p)
1484 if end is None:
1484 if end is None:
1485 return len(self.full_series)
1485 return len(self.full_series)
1486 return end + 1
1486 return end + 1
1487 return 0
1487 return 0
1488
1488
1489 def series_end(self, all_patches=False):
1489 def series_end(self, all_patches=False):
1490 """If all_patches is False, return the index of the next pushable patch
1490 """If all_patches is False, return the index of the next pushable patch
1491 in the series, or the series length. If all_patches is True, return the
1491 in the series, or the series length. If all_patches is True, return the
1492 index of the first patch past the last applied one.
1492 index of the first patch past the last applied one.
1493 """
1493 """
1494 end = 0
1494 end = 0
1495 def next(start):
1495 def next(start):
1496 if all_patches:
1496 if all_patches:
1497 return start
1497 return start
1498 i = start
1498 i = start
1499 while i < len(self.series):
1499 while i < len(self.series):
1500 p, reason = self.pushable(i)
1500 p, reason = self.pushable(i)
1501 if p:
1501 if p:
1502 break
1502 break
1503 self.explain_pushable(i)
1503 self.explain_pushable(i)
1504 i += 1
1504 i += 1
1505 return i
1505 return i
1506 if len(self.applied) > 0:
1506 if len(self.applied) > 0:
1507 p = self.applied[-1].name
1507 p = self.applied[-1].name
1508 try:
1508 try:
1509 end = self.series.index(p)
1509 end = self.series.index(p)
1510 except ValueError:
1510 except ValueError:
1511 return 0
1511 return 0
1512 return next(end + 1)
1512 return next(end + 1)
1513 return next(end)
1513 return next(end)
1514
1514
1515 def appliedname(self, index):
1515 def appliedname(self, index):
1516 pname = self.applied[index].name
1516 pname = self.applied[index].name
1517 if not self.ui.verbose:
1517 if not self.ui.verbose:
1518 p = pname
1518 p = pname
1519 else:
1519 else:
1520 p = str(self.series.index(pname)) + " " + pname
1520 p = str(self.series.index(pname)) + " " + pname
1521 return p
1521 return p
1522
1522
1523 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1523 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1524 force=None, git=False):
1524 force=None, git=False):
1525 def checkseries(patchname):
1525 def checkseries(patchname):
1526 if patchname in self.series:
1526 if patchname in self.series:
1527 raise util.Abort(_('patch %s is already in the series file')
1527 raise util.Abort(_('patch %s is already in the series file')
1528 % patchname)
1528 % patchname)
1529 def checkfile(patchname):
1529 def checkfile(patchname):
1530 if not force and os.path.exists(self.join(patchname)):
1530 if not force and os.path.exists(self.join(patchname)):
1531 raise util.Abort(_('patch "%s" already exists')
1531 raise util.Abort(_('patch "%s" already exists')
1532 % patchname)
1532 % patchname)
1533
1533
1534 if rev:
1534 if rev:
1535 if files:
1535 if files:
1536 raise util.Abort(_('option "-r" not valid when importing '
1536 raise util.Abort(_('option "-r" not valid when importing '
1537 'files'))
1537 'files'))
1538 rev = cmdutil.revrange(repo, rev)
1538 rev = cmdutil.revrange(repo, rev)
1539 rev.sort(lambda x, y: cmp(y, x))
1539 rev.sort(lambda x, y: cmp(y, x))
1540 if (len(files) > 1 or len(rev) > 1) and patchname:
1540 if (len(files) > 1 or len(rev) > 1) and patchname:
1541 raise util.Abort(_('option "-n" not valid when importing multiple '
1541 raise util.Abort(_('option "-n" not valid when importing multiple '
1542 'patches'))
1542 'patches'))
1543 i = 0
1543 i = 0
1544 added = []
1544 added = []
1545 if rev:
1545 if rev:
1546 # If mq patches are applied, we can only import revisions
1546 # If mq patches are applied, we can only import revisions
1547 # that form a linear path to qbase.
1547 # that form a linear path to qbase.
1548 # Otherwise, they should form a linear path to a head.
1548 # Otherwise, they should form a linear path to a head.
1549 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1549 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1550 if len(heads) > 1:
1550 if len(heads) > 1:
1551 raise util.Abort(_('revision %d is the root of more than one '
1551 raise util.Abort(_('revision %d is the root of more than one '
1552 'branch') % rev[-1])
1552 'branch') % rev[-1])
1553 if self.applied:
1553 if self.applied:
1554 base = hex(repo.changelog.node(rev[0]))
1554 base = hex(repo.changelog.node(rev[0]))
1555 if base in [n.rev for n in self.applied]:
1555 if base in [n.rev for n in self.applied]:
1556 raise util.Abort(_('revision %d is already managed')
1556 raise util.Abort(_('revision %d is already managed')
1557 % rev[0])
1557 % rev[0])
1558 if heads != [bin(self.applied[-1].rev)]:
1558 if heads != [bin(self.applied[-1].rev)]:
1559 raise util.Abort(_('revision %d is not the parent of '
1559 raise util.Abort(_('revision %d is not the parent of '
1560 'the queue') % rev[0])
1560 'the queue') % rev[0])
1561 base = repo.changelog.rev(bin(self.applied[0].rev))
1561 base = repo.changelog.rev(bin(self.applied[0].rev))
1562 lastparent = repo.changelog.parentrevs(base)[0]
1562 lastparent = repo.changelog.parentrevs(base)[0]
1563 else:
1563 else:
1564 if heads != [repo.changelog.node(rev[0])]:
1564 if heads != [repo.changelog.node(rev[0])]:
1565 raise util.Abort(_('revision %d has unmanaged children')
1565 raise util.Abort(_('revision %d has unmanaged children')
1566 % rev[0])
1566 % rev[0])
1567 lastparent = None
1567 lastparent = None
1568
1568
1569 if git:
1569 if git:
1570 self.diffopts().git = True
1570 self.diffopts().git = True
1571
1571
1572 for r in rev:
1572 for r in rev:
1573 p1, p2 = repo.changelog.parentrevs(r)
1573 p1, p2 = repo.changelog.parentrevs(r)
1574 n = repo.changelog.node(r)
1574 n = repo.changelog.node(r)
1575 if p2 != nullrev:
1575 if p2 != nullrev:
1576 raise util.Abort(_('cannot import merge revision %d') % r)
1576 raise util.Abort(_('cannot import merge revision %d') % r)
1577 if lastparent and lastparent != r:
1577 if lastparent and lastparent != r:
1578 raise util.Abort(_('revision %d is not the parent of %d')
1578 raise util.Abort(_('revision %d is not the parent of %d')
1579 % (r, lastparent))
1579 % (r, lastparent))
1580 lastparent = p1
1580 lastparent = p1
1581
1581
1582 if not patchname:
1582 if not patchname:
1583 patchname = normname('%d.diff' % r)
1583 patchname = normname('%d.diff' % r)
1584 self.check_reserved_name(patchname)
1584 self.check_reserved_name(patchname)
1585 checkseries(patchname)
1585 checkseries(patchname)
1586 checkfile(patchname)
1586 checkfile(patchname)
1587 self.full_series.insert(0, patchname)
1587 self.full_series.insert(0, patchname)
1588
1588
1589 patchf = self.opener(patchname, "w")
1589 patchf = self.opener(patchname, "w")
1590 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1590 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1591 patchf.close()
1591 patchf.close()
1592
1592
1593 se = statusentry(hex(n), patchname)
1593 se = statusentry(hex(n), patchname)
1594 self.applied.insert(0, se)
1594 self.applied.insert(0, se)
1595
1595
1596 added.append(patchname)
1596 added.append(patchname)
1597 patchname = None
1597 patchname = None
1598 self.parse_series()
1598 self.parse_series()
1599 self.applied_dirty = 1
1599 self.applied_dirty = 1
1600
1600
1601 for filename in files:
1601 for filename in files:
1602 if existing:
1602 if existing:
1603 if filename == '-':
1603 if filename == '-':
1604 raise util.Abort(_('-e is incompatible with import from -'))
1604 raise util.Abort(_('-e is incompatible with import from -'))
1605 if not patchname:
1605 if not patchname:
1606 patchname = normname(filename)
1606 patchname = normname(filename)
1607 self.check_reserved_name(patchname)
1607 self.check_reserved_name(patchname)
1608 if not os.path.isfile(self.join(patchname)):
1608 if not os.path.isfile(self.join(patchname)):
1609 raise util.Abort(_("patch %s does not exist") % patchname)
1609 raise util.Abort(_("patch %s does not exist") % patchname)
1610 else:
1610 else:
1611 try:
1611 try:
1612 if filename == '-':
1612 if filename == '-':
1613 if not patchname:
1613 if not patchname:
1614 raise util.Abort(_('need --name to import a patch from -'))
1614 raise util.Abort(_('need --name to import a patch from -'))
1615 text = sys.stdin.read()
1615 text = sys.stdin.read()
1616 else:
1616 else:
1617 text = url.open(self.ui, filename).read()
1617 text = url.open(self.ui, filename).read()
1618 except (OSError, IOError):
1618 except (OSError, IOError):
1619 raise util.Abort(_("unable to read %s") % filename)
1619 raise util.Abort(_("unable to read %s") % filename)
1620 if not patchname:
1620 if not patchname:
1621 patchname = normname(os.path.basename(filename))
1621 patchname = normname(os.path.basename(filename))
1622 self.check_reserved_name(patchname)
1622 self.check_reserved_name(patchname)
1623 checkfile(patchname)
1623 checkfile(patchname)
1624 patchf = self.opener(patchname, "w")
1624 patchf = self.opener(patchname, "w")
1625 patchf.write(text)
1625 patchf.write(text)
1626 if not force:
1626 if not force:
1627 checkseries(patchname)
1627 checkseries(patchname)
1628 if patchname not in self.series:
1628 if patchname not in self.series:
1629 index = self.full_series_end() + i
1629 index = self.full_series_end() + i
1630 self.full_series[index:index] = [patchname]
1630 self.full_series[index:index] = [patchname]
1631 self.parse_series()
1631 self.parse_series()
1632 self.ui.warn(_("adding %s to series file\n") % patchname)
1632 self.ui.warn(_("adding %s to series file\n") % patchname)
1633 i += 1
1633 i += 1
1634 added.append(patchname)
1634 added.append(patchname)
1635 patchname = None
1635 patchname = None
1636 self.series_dirty = 1
1636 self.series_dirty = 1
1637 qrepo = self.qrepo()
1637 qrepo = self.qrepo()
1638 if qrepo:
1638 if qrepo:
1639 qrepo.add(added)
1639 qrepo.add(added)
1640
1640
1641 def delete(ui, repo, *patches, **opts):
1641 def delete(ui, repo, *patches, **opts):
1642 """remove patches from queue
1642 """remove patches from queue
1643
1643
1644 The patches must not be applied, unless they are arguments to the
1644 The patches must not be applied, unless they are arguments to the
1645 -r/--rev parameter. At least one patch or revision is required.
1645 -r/--rev parameter. At least one patch or revision is required.
1646
1646
1647 With --rev, mq will stop managing the named revisions (converting
1647 With --rev, mq will stop managing the named revisions (converting
1648 them to regular mercurial changesets). The qfinish command should
1648 them to regular Mercurial changesets). The qfinish command should
1649 be used as an alternative for qdelete -r, as the latter option is
1649 be used as an alternative for qdelete -r, as the latter option is
1650 deprecated.
1650 deprecated.
1651
1651
1652 With -k/--keep, the patch files are preserved in the patch
1652 With -k/--keep, the patch files are preserved in the patch
1653 directory."""
1653 directory."""
1654 q = repo.mq
1654 q = repo.mq
1655 q.delete(repo, patches, opts)
1655 q.delete(repo, patches, opts)
1656 q.save_dirty()
1656 q.save_dirty()
1657 return 0
1657 return 0
1658
1658
1659 def applied(ui, repo, patch=None, **opts):
1659 def applied(ui, repo, patch=None, **opts):
1660 """print the patches already applied"""
1660 """print the patches already applied"""
1661 q = repo.mq
1661 q = repo.mq
1662 if patch:
1662 if patch:
1663 if patch not in q.series:
1663 if patch not in q.series:
1664 raise util.Abort(_("patch %s is not in series file") % patch)
1664 raise util.Abort(_("patch %s is not in series file") % patch)
1665 end = q.series.index(patch) + 1
1665 end = q.series.index(patch) + 1
1666 else:
1666 else:
1667 end = q.series_end(True)
1667 end = q.series_end(True)
1668 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1668 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1669
1669
1670 def unapplied(ui, repo, patch=None, **opts):
1670 def unapplied(ui, repo, patch=None, **opts):
1671 """print the patches not yet applied"""
1671 """print the patches not yet applied"""
1672 q = repo.mq
1672 q = repo.mq
1673 if patch:
1673 if patch:
1674 if patch not in q.series:
1674 if patch not in q.series:
1675 raise util.Abort(_("patch %s is not in series file") % patch)
1675 raise util.Abort(_("patch %s is not in series file") % patch)
1676 start = q.series.index(patch) + 1
1676 start = q.series.index(patch) + 1
1677 else:
1677 else:
1678 start = q.series_end(True)
1678 start = q.series_end(True)
1679 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1679 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1680
1680
1681 def qimport(ui, repo, *filename, **opts):
1681 def qimport(ui, repo, *filename, **opts):
1682 """import a patch
1682 """import a patch
1683
1683
1684 The patch is inserted into the series after the last applied
1684 The patch is inserted into the series after the last applied
1685 patch. If no patches have been applied, qimport prepends the patch
1685 patch. If no patches have been applied, qimport prepends the patch
1686 to the series.
1686 to the series.
1687
1687
1688 The patch will have the same name as its source file unless you
1688 The patch will have the same name as its source file unless you
1689 give it a new one with -n/--name.
1689 give it a new one with -n/--name.
1690
1690
1691 You can register an existing patch inside the patch directory with
1691 You can register an existing patch inside the patch directory with
1692 the -e/--existing flag.
1692 the -e/--existing flag.
1693
1693
1694 With -f/--force, an existing patch of the same name will be
1694 With -f/--force, an existing patch of the same name will be
1695 overwritten.
1695 overwritten.
1696
1696
1697 An existing changeset may be placed under mq control with -r/--rev
1697 An existing changeset may be placed under mq control with -r/--rev
1698 (e.g. qimport --rev tip -n patch will place tip under mq control).
1698 (e.g. qimport --rev tip -n patch will place tip under mq control).
1699 With -g/--git, patches imported with --rev will use the git diff
1699 With -g/--git, patches imported with --rev will use the git diff
1700 format. See the diffs help topic for information on why this is
1700 format. See the diffs help topic for information on why this is
1701 important for preserving rename/copy information and permission
1701 important for preserving rename/copy information and permission
1702 changes.
1702 changes.
1703
1703
1704 To import a patch from standard input, pass - as the patch file.
1704 To import a patch from standard input, pass - as the patch file.
1705 When importing from standard input, a patch name must be specified
1705 When importing from standard input, a patch name must be specified
1706 using the --name flag.
1706 using the --name flag.
1707 """
1707 """
1708 q = repo.mq
1708 q = repo.mq
1709 q.qimport(repo, filename, patchname=opts['name'],
1709 q.qimport(repo, filename, patchname=opts['name'],
1710 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1710 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1711 git=opts['git'])
1711 git=opts['git'])
1712 q.save_dirty()
1712 q.save_dirty()
1713
1713
1714 if opts.get('push') and not opts.get('rev'):
1714 if opts.get('push') and not opts.get('rev'):
1715 return q.push(repo, None)
1715 return q.push(repo, None)
1716 return 0
1716 return 0
1717
1717
1718 def init(ui, repo, **opts):
1718 def init(ui, repo, **opts):
1719 """init a new queue repository
1719 """init a new queue repository
1720
1720
1721 The queue repository is unversioned by default. If
1721 The queue repository is unversioned by default. If
1722 -c/--create-repo is specified, qinit will create a separate nested
1722 -c/--create-repo is specified, qinit will create a separate nested
1723 repository for patches (qinit -c may also be run later to convert
1723 repository for patches (qinit -c may also be run later to convert
1724 an unversioned patch repository into a versioned one). You can use
1724 an unversioned patch repository into a versioned one). You can use
1725 qcommit to commit changes to this queue repository."""
1725 qcommit to commit changes to this queue repository."""
1726 q = repo.mq
1726 q = repo.mq
1727 r = q.init(repo, create=opts['create_repo'])
1727 r = q.init(repo, create=opts['create_repo'])
1728 q.save_dirty()
1728 q.save_dirty()
1729 if r:
1729 if r:
1730 if not os.path.exists(r.wjoin('.hgignore')):
1730 if not os.path.exists(r.wjoin('.hgignore')):
1731 fp = r.wopener('.hgignore', 'w')
1731 fp = r.wopener('.hgignore', 'w')
1732 fp.write('^\\.hg\n')
1732 fp.write('^\\.hg\n')
1733 fp.write('^\\.mq\n')
1733 fp.write('^\\.mq\n')
1734 fp.write('syntax: glob\n')
1734 fp.write('syntax: glob\n')
1735 fp.write('status\n')
1735 fp.write('status\n')
1736 fp.write('guards\n')
1736 fp.write('guards\n')
1737 fp.close()
1737 fp.close()
1738 if not os.path.exists(r.wjoin('series')):
1738 if not os.path.exists(r.wjoin('series')):
1739 r.wopener('series', 'w').close()
1739 r.wopener('series', 'w').close()
1740 r.add(['.hgignore', 'series'])
1740 r.add(['.hgignore', 'series'])
1741 commands.add(ui, r)
1741 commands.add(ui, r)
1742 return 0
1742 return 0
1743
1743
1744 def clone(ui, source, dest=None, **opts):
1744 def clone(ui, source, dest=None, **opts):
1745 '''clone main and patch repository at same time
1745 '''clone main and patch repository at same time
1746
1746
1747 If source is local, destination will have no patches applied. If
1747 If source is local, destination will have no patches applied. If
1748 source is remote, this command can not check if patches are
1748 source is remote, this command can not check if patches are
1749 applied in source, so cannot guarantee that patches are not
1749 applied in source, so cannot guarantee that patches are not
1750 applied in destination. If you clone remote repository, be sure
1750 applied in destination. If you clone remote repository, be sure
1751 before that it has no patches applied.
1751 before that it has no patches applied.
1752
1752
1753 Source patch repository is looked for in <src>/.hg/patches by
1753 Source patch repository is looked for in <src>/.hg/patches by
1754 default. Use -p <url> to change.
1754 default. Use -p <url> to change.
1755
1755
1756 The patch directory must be a nested mercurial repository, as
1756 The patch directory must be a nested Mercurial repository, as
1757 would be created by qinit -c.
1757 would be created by qinit -c.
1758 '''
1758 '''
1759 def patchdir(repo):
1759 def patchdir(repo):
1760 url = repo.url()
1760 url = repo.url()
1761 if url.endswith('/'):
1761 if url.endswith('/'):
1762 url = url[:-1]
1762 url = url[:-1]
1763 return url + '/.hg/patches'
1763 return url + '/.hg/patches'
1764 if dest is None:
1764 if dest is None:
1765 dest = hg.defaultdest(source)
1765 dest = hg.defaultdest(source)
1766 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1766 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1767 if opts['patches']:
1767 if opts['patches']:
1768 patchespath = ui.expandpath(opts['patches'])
1768 patchespath = ui.expandpath(opts['patches'])
1769 else:
1769 else:
1770 patchespath = patchdir(sr)
1770 patchespath = patchdir(sr)
1771 try:
1771 try:
1772 hg.repository(ui, patchespath)
1772 hg.repository(ui, patchespath)
1773 except error.RepoError:
1773 except error.RepoError:
1774 raise util.Abort(_('versioned patch repository not found'
1774 raise util.Abort(_('versioned patch repository not found'
1775 ' (see qinit -c)'))
1775 ' (see qinit -c)'))
1776 qbase, destrev = None, None
1776 qbase, destrev = None, None
1777 if sr.local():
1777 if sr.local():
1778 if sr.mq.applied:
1778 if sr.mq.applied:
1779 qbase = bin(sr.mq.applied[0].rev)
1779 qbase = bin(sr.mq.applied[0].rev)
1780 if not hg.islocal(dest):
1780 if not hg.islocal(dest):
1781 heads = set(sr.heads())
1781 heads = set(sr.heads())
1782 destrev = list(heads.difference(sr.heads(qbase)))
1782 destrev = list(heads.difference(sr.heads(qbase)))
1783 destrev.append(sr.changelog.parents(qbase)[0])
1783 destrev.append(sr.changelog.parents(qbase)[0])
1784 elif sr.capable('lookup'):
1784 elif sr.capable('lookup'):
1785 try:
1785 try:
1786 qbase = sr.lookup('qbase')
1786 qbase = sr.lookup('qbase')
1787 except error.RepoError:
1787 except error.RepoError:
1788 pass
1788 pass
1789 ui.note(_('cloning main repository\n'))
1789 ui.note(_('cloning main repository\n'))
1790 sr, dr = hg.clone(ui, sr.url(), dest,
1790 sr, dr = hg.clone(ui, sr.url(), dest,
1791 pull=opts['pull'],
1791 pull=opts['pull'],
1792 rev=destrev,
1792 rev=destrev,
1793 update=False,
1793 update=False,
1794 stream=opts['uncompressed'])
1794 stream=opts['uncompressed'])
1795 ui.note(_('cloning patch repository\n'))
1795 ui.note(_('cloning patch repository\n'))
1796 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1796 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1797 pull=opts['pull'], update=not opts['noupdate'],
1797 pull=opts['pull'], update=not opts['noupdate'],
1798 stream=opts['uncompressed'])
1798 stream=opts['uncompressed'])
1799 if dr.local():
1799 if dr.local():
1800 if qbase:
1800 if qbase:
1801 ui.note(_('stripping applied patches from destination '
1801 ui.note(_('stripping applied patches from destination '
1802 'repository\n'))
1802 'repository\n'))
1803 dr.mq.strip(dr, qbase, update=False, backup=None)
1803 dr.mq.strip(dr, qbase, update=False, backup=None)
1804 if not opts['noupdate']:
1804 if not opts['noupdate']:
1805 ui.note(_('updating destination repository\n'))
1805 ui.note(_('updating destination repository\n'))
1806 hg.update(dr, dr.changelog.tip())
1806 hg.update(dr, dr.changelog.tip())
1807
1807
1808 def commit(ui, repo, *pats, **opts):
1808 def commit(ui, repo, *pats, **opts):
1809 """commit changes in the queue repository"""
1809 """commit changes in the queue repository"""
1810 q = repo.mq
1810 q = repo.mq
1811 r = q.qrepo()
1811 r = q.qrepo()
1812 if not r: raise util.Abort('no queue repository')
1812 if not r: raise util.Abort('no queue repository')
1813 commands.commit(r.ui, r, *pats, **opts)
1813 commands.commit(r.ui, r, *pats, **opts)
1814
1814
1815 def series(ui, repo, **opts):
1815 def series(ui, repo, **opts):
1816 """print the entire series file"""
1816 """print the entire series file"""
1817 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1817 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1818 return 0
1818 return 0
1819
1819
1820 def top(ui, repo, **opts):
1820 def top(ui, repo, **opts):
1821 """print the name of the current patch"""
1821 """print the name of the current patch"""
1822 q = repo.mq
1822 q = repo.mq
1823 t = q.applied and q.series_end(True) or 0
1823 t = q.applied and q.series_end(True) or 0
1824 if t:
1824 if t:
1825 return q.qseries(repo, start=t-1, length=1, status='A',
1825 return q.qseries(repo, start=t-1, length=1, status='A',
1826 summary=opts.get('summary'))
1826 summary=opts.get('summary'))
1827 else:
1827 else:
1828 ui.write(_("no patches applied\n"))
1828 ui.write(_("no patches applied\n"))
1829 return 1
1829 return 1
1830
1830
1831 def next(ui, repo, **opts):
1831 def next(ui, repo, **opts):
1832 """print the name of the next patch"""
1832 """print the name of the next patch"""
1833 q = repo.mq
1833 q = repo.mq
1834 end = q.series_end()
1834 end = q.series_end()
1835 if end == len(q.series):
1835 if end == len(q.series):
1836 ui.write(_("all patches applied\n"))
1836 ui.write(_("all patches applied\n"))
1837 return 1
1837 return 1
1838 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1838 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1839
1839
1840 def prev(ui, repo, **opts):
1840 def prev(ui, repo, **opts):
1841 """print the name of the previous patch"""
1841 """print the name of the previous patch"""
1842 q = repo.mq
1842 q = repo.mq
1843 l = len(q.applied)
1843 l = len(q.applied)
1844 if l == 1:
1844 if l == 1:
1845 ui.write(_("only one patch applied\n"))
1845 ui.write(_("only one patch applied\n"))
1846 return 1
1846 return 1
1847 if not l:
1847 if not l:
1848 ui.write(_("no patches applied\n"))
1848 ui.write(_("no patches applied\n"))
1849 return 1
1849 return 1
1850 return q.qseries(repo, start=l-2, length=1, status='A',
1850 return q.qseries(repo, start=l-2, length=1, status='A',
1851 summary=opts.get('summary'))
1851 summary=opts.get('summary'))
1852
1852
1853 def setupheaderopts(ui, opts):
1853 def setupheaderopts(ui, opts):
1854 def do(opt,val):
1854 def do(opt,val):
1855 if not opts[opt] and opts['current' + opt]:
1855 if not opts[opt] and opts['current' + opt]:
1856 opts[opt] = val
1856 opts[opt] = val
1857 do('user', ui.username())
1857 do('user', ui.username())
1858 do('date', "%d %d" % util.makedate())
1858 do('date', "%d %d" % util.makedate())
1859
1859
1860 def new(ui, repo, patch, *args, **opts):
1860 def new(ui, repo, patch, *args, **opts):
1861 """create a new patch
1861 """create a new patch
1862
1862
1863 qnew creates a new patch on top of the currently-applied patch (if
1863 qnew creates a new patch on top of the currently-applied patch (if
1864 any). It will refuse to run if there are any outstanding changes
1864 any). It will refuse to run if there are any outstanding changes
1865 unless -f/--force is specified, in which case the patch will be
1865 unless -f/--force is specified, in which case the patch will be
1866 initialized with them. You may also use -I/--include,
1866 initialized with them. You may also use -I/--include,
1867 -X/--exclude, and/or a list of files after the patch name to add
1867 -X/--exclude, and/or a list of files after the patch name to add
1868 only changes to matching files to the new patch, leaving the rest
1868 only changes to matching files to the new patch, leaving the rest
1869 as uncommitted modifications.
1869 as uncommitted modifications.
1870
1870
1871 -u/--user and -d/--date can be used to set the (given) user and
1871 -u/--user and -d/--date can be used to set the (given) user and
1872 date, respectively. -U/--currentuser and -D/--currentdate set user
1872 date, respectively. -U/--currentuser and -D/--currentdate set user
1873 to current user and date to current date.
1873 to current user and date to current date.
1874
1874
1875 -e/--edit, -m/--message or -l/--logfile set the patch header as
1875 -e/--edit, -m/--message or -l/--logfile set the patch header as
1876 well as the commit message. If none is specified, the header is
1876 well as the commit message. If none is specified, the header is
1877 empty and the commit message is '[mq]: PATCH'.
1877 empty and the commit message is '[mq]: PATCH'.
1878
1878
1879 Use the -g/--git option to keep the patch in the git extended diff
1879 Use the -g/--git option to keep the patch in the git extended diff
1880 format. Read the diffs help topic for more information on why this
1880 format. Read the diffs help topic for more information on why this
1881 is important for preserving permission changes and copy/rename
1881 is important for preserving permission changes and copy/rename
1882 information.
1882 information.
1883 """
1883 """
1884 msg = cmdutil.logmessage(opts)
1884 msg = cmdutil.logmessage(opts)
1885 def getmsg(): return ui.edit(msg, ui.username())
1885 def getmsg(): return ui.edit(msg, ui.username())
1886 q = repo.mq
1886 q = repo.mq
1887 opts['msg'] = msg
1887 opts['msg'] = msg
1888 if opts.get('edit'):
1888 if opts.get('edit'):
1889 opts['msg'] = getmsg
1889 opts['msg'] = getmsg
1890 else:
1890 else:
1891 opts['msg'] = msg
1891 opts['msg'] = msg
1892 setupheaderopts(ui, opts)
1892 setupheaderopts(ui, opts)
1893 q.new(repo, patch, *args, **opts)
1893 q.new(repo, patch, *args, **opts)
1894 q.save_dirty()
1894 q.save_dirty()
1895 return 0
1895 return 0
1896
1896
1897 def refresh(ui, repo, *pats, **opts):
1897 def refresh(ui, repo, *pats, **opts):
1898 """update the current patch
1898 """update the current patch
1899
1899
1900 If any file patterns are provided, the refreshed patch will
1900 If any file patterns are provided, the refreshed patch will
1901 contain only the modifications that match those patterns; the
1901 contain only the modifications that match those patterns; the
1902 remaining modifications will remain in the working directory.
1902 remaining modifications will remain in the working directory.
1903
1903
1904 If -s/--short is specified, files currently included in the patch
1904 If -s/--short is specified, files currently included in the patch
1905 will be refreshed just like matched files and remain in the patch.
1905 will be refreshed just like matched files and remain in the patch.
1906
1906
1907 hg add/remove/copy/rename work as usual, though you might want to
1907 hg add/remove/copy/rename work as usual, though you might want to
1908 use git-style patches (-g/--git or [diff] git=1) to track copies
1908 use git-style patches (-g/--git or [diff] git=1) to track copies
1909 and renames. See the diffs help topic for more information on the
1909 and renames. See the diffs help topic for more information on the
1910 git diff format.
1910 git diff format.
1911 """
1911 """
1912 q = repo.mq
1912 q = repo.mq
1913 message = cmdutil.logmessage(opts)
1913 message = cmdutil.logmessage(opts)
1914 if opts['edit']:
1914 if opts['edit']:
1915 if not q.applied:
1915 if not q.applied:
1916 ui.write(_("no patches applied\n"))
1916 ui.write(_("no patches applied\n"))
1917 return 1
1917 return 1
1918 if message:
1918 if message:
1919 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1919 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1920 patch = q.applied[-1].name
1920 patch = q.applied[-1].name
1921 ph = patchheader(q.join(patch))
1921 ph = patchheader(q.join(patch))
1922 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
1922 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
1923 setupheaderopts(ui, opts)
1923 setupheaderopts(ui, opts)
1924 ret = q.refresh(repo, pats, msg=message, **opts)
1924 ret = q.refresh(repo, pats, msg=message, **opts)
1925 q.save_dirty()
1925 q.save_dirty()
1926 return ret
1926 return ret
1927
1927
1928 def diff(ui, repo, *pats, **opts):
1928 def diff(ui, repo, *pats, **opts):
1929 """diff of the current patch and subsequent modifications
1929 """diff of the current patch and subsequent modifications
1930
1930
1931 Shows a diff which includes the current patch as well as any
1931 Shows a diff which includes the current patch as well as any
1932 changes which have been made in the working directory since the
1932 changes which have been made in the working directory since the
1933 last refresh (thus showing what the current patch would become
1933 last refresh (thus showing what the current patch would become
1934 after a qrefresh).
1934 after a qrefresh).
1935
1935
1936 Use 'hg diff' if you only want to see the changes made since the
1936 Use 'hg diff' if you only want to see the changes made since the
1937 last qrefresh, or 'hg export qtip' if you want to see changes made
1937 last qrefresh, or 'hg export qtip' if you want to see changes made
1938 by the current patch without including changes made since the
1938 by the current patch without including changes made since the
1939 qrefresh.
1939 qrefresh.
1940 """
1940 """
1941 repo.mq.diff(repo, pats, opts)
1941 repo.mq.diff(repo, pats, opts)
1942 return 0
1942 return 0
1943
1943
1944 def fold(ui, repo, *files, **opts):
1944 def fold(ui, repo, *files, **opts):
1945 """fold the named patches into the current patch
1945 """fold the named patches into the current patch
1946
1946
1947 Patches must not yet be applied. Each patch will be successively
1947 Patches must not yet be applied. Each patch will be successively
1948 applied to the current patch in the order given. If all the
1948 applied to the current patch in the order given. If all the
1949 patches apply successfully, the current patch will be refreshed
1949 patches apply successfully, the current patch will be refreshed
1950 with the new cumulative patch, and the folded patches will be
1950 with the new cumulative patch, and the folded patches will be
1951 deleted. With -k/--keep, the folded patch files will not be
1951 deleted. With -k/--keep, the folded patch files will not be
1952 removed afterwards.
1952 removed afterwards.
1953
1953
1954 The header for each folded patch will be concatenated with the
1954 The header for each folded patch will be concatenated with the
1955 current patch header, separated by a line of '* * *'."""
1955 current patch header, separated by a line of '* * *'."""
1956
1956
1957 q = repo.mq
1957 q = repo.mq
1958
1958
1959 if not files:
1959 if not files:
1960 raise util.Abort(_('qfold requires at least one patch name'))
1960 raise util.Abort(_('qfold requires at least one patch name'))
1961 if not q.check_toppatch(repo):
1961 if not q.check_toppatch(repo):
1962 raise util.Abort(_('No patches applied'))
1962 raise util.Abort(_('No patches applied'))
1963 q.check_localchanges(repo)
1963 q.check_localchanges(repo)
1964
1964
1965 message = cmdutil.logmessage(opts)
1965 message = cmdutil.logmessage(opts)
1966 if opts['edit']:
1966 if opts['edit']:
1967 if message:
1967 if message:
1968 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1968 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1969
1969
1970 parent = q.lookup('qtip')
1970 parent = q.lookup('qtip')
1971 patches = []
1971 patches = []
1972 messages = []
1972 messages = []
1973 for f in files:
1973 for f in files:
1974 p = q.lookup(f)
1974 p = q.lookup(f)
1975 if p in patches or p == parent:
1975 if p in patches or p == parent:
1976 ui.warn(_('Skipping already folded patch %s') % p)
1976 ui.warn(_('Skipping already folded patch %s') % p)
1977 if q.isapplied(p):
1977 if q.isapplied(p):
1978 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1978 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1979 patches.append(p)
1979 patches.append(p)
1980
1980
1981 for p in patches:
1981 for p in patches:
1982 if not message:
1982 if not message:
1983 ph = patchheader(q.join(p))
1983 ph = patchheader(q.join(p))
1984 if ph.message:
1984 if ph.message:
1985 messages.append(ph.message)
1985 messages.append(ph.message)
1986 pf = q.join(p)
1986 pf = q.join(p)
1987 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1987 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1988 if not patchsuccess:
1988 if not patchsuccess:
1989 raise util.Abort(_('Error folding patch %s') % p)
1989 raise util.Abort(_('Error folding patch %s') % p)
1990 patch.updatedir(ui, repo, files)
1990 patch.updatedir(ui, repo, files)
1991
1991
1992 if not message:
1992 if not message:
1993 ph = patchheader(q.join(parent))
1993 ph = patchheader(q.join(parent))
1994 message, user = ph.message, ph.user
1994 message, user = ph.message, ph.user
1995 for msg in messages:
1995 for msg in messages:
1996 message.append('* * *')
1996 message.append('* * *')
1997 message.extend(msg)
1997 message.extend(msg)
1998 message = '\n'.join(message)
1998 message = '\n'.join(message)
1999
1999
2000 if opts['edit']:
2000 if opts['edit']:
2001 message = ui.edit(message, user or ui.username())
2001 message = ui.edit(message, user or ui.username())
2002
2002
2003 q.refresh(repo, msg=message)
2003 q.refresh(repo, msg=message)
2004 q.delete(repo, patches, opts)
2004 q.delete(repo, patches, opts)
2005 q.save_dirty()
2005 q.save_dirty()
2006
2006
2007 def goto(ui, repo, patch, **opts):
2007 def goto(ui, repo, patch, **opts):
2008 '''push or pop patches until named patch is at top of stack'''
2008 '''push or pop patches until named patch is at top of stack'''
2009 q = repo.mq
2009 q = repo.mq
2010 patch = q.lookup(patch)
2010 patch = q.lookup(patch)
2011 if q.isapplied(patch):
2011 if q.isapplied(patch):
2012 ret = q.pop(repo, patch, force=opts['force'])
2012 ret = q.pop(repo, patch, force=opts['force'])
2013 else:
2013 else:
2014 ret = q.push(repo, patch, force=opts['force'])
2014 ret = q.push(repo, patch, force=opts['force'])
2015 q.save_dirty()
2015 q.save_dirty()
2016 return ret
2016 return ret
2017
2017
2018 def guard(ui, repo, *args, **opts):
2018 def guard(ui, repo, *args, **opts):
2019 '''set or print guards for a patch
2019 '''set or print guards for a patch
2020
2020
2021 Guards control whether a patch can be pushed. A patch with no
2021 Guards control whether a patch can be pushed. A patch with no
2022 guards is always pushed. A patch with a positive guard ("+foo") is
2022 guards is always pushed. A patch with a positive guard ("+foo") is
2023 pushed only if the qselect command has activated it. A patch with
2023 pushed only if the qselect command has activated it. A patch with
2024 a negative guard ("-foo") is never pushed if the qselect command
2024 a negative guard ("-foo") is never pushed if the qselect command
2025 has activated it.
2025 has activated it.
2026
2026
2027 With no arguments, print the currently active guards.
2027 With no arguments, print the currently active guards.
2028 With arguments, set guards for the named patch.
2028 With arguments, set guards for the named patch.
2029 NOTE: Specifying negative guards now requires '--'.
2029 NOTE: Specifying negative guards now requires '--'.
2030
2030
2031 To set guards on another patch:
2031 To set guards on another patch:
2032 hg qguard -- other.patch +2.6.17 -stable
2032 hg qguard -- other.patch +2.6.17 -stable
2033 '''
2033 '''
2034 def status(idx):
2034 def status(idx):
2035 guards = q.series_guards[idx] or ['unguarded']
2035 guards = q.series_guards[idx] or ['unguarded']
2036 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2036 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2037 q = repo.mq
2037 q = repo.mq
2038 patch = None
2038 patch = None
2039 args = list(args)
2039 args = list(args)
2040 if opts['list']:
2040 if opts['list']:
2041 if args or opts['none']:
2041 if args or opts['none']:
2042 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2042 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2043 for i in xrange(len(q.series)):
2043 for i in xrange(len(q.series)):
2044 status(i)
2044 status(i)
2045 return
2045 return
2046 if not args or args[0][0:1] in '-+':
2046 if not args or args[0][0:1] in '-+':
2047 if not q.applied:
2047 if not q.applied:
2048 raise util.Abort(_('no patches applied'))
2048 raise util.Abort(_('no patches applied'))
2049 patch = q.applied[-1].name
2049 patch = q.applied[-1].name
2050 if patch is None and args[0][0:1] not in '-+':
2050 if patch is None and args[0][0:1] not in '-+':
2051 patch = args.pop(0)
2051 patch = args.pop(0)
2052 if patch is None:
2052 if patch is None:
2053 raise util.Abort(_('no patch to work with'))
2053 raise util.Abort(_('no patch to work with'))
2054 if args or opts['none']:
2054 if args or opts['none']:
2055 idx = q.find_series(patch)
2055 idx = q.find_series(patch)
2056 if idx is None:
2056 if idx is None:
2057 raise util.Abort(_('no patch named %s') % patch)
2057 raise util.Abort(_('no patch named %s') % patch)
2058 q.set_guards(idx, args)
2058 q.set_guards(idx, args)
2059 q.save_dirty()
2059 q.save_dirty()
2060 else:
2060 else:
2061 status(q.series.index(q.lookup(patch)))
2061 status(q.series.index(q.lookup(patch)))
2062
2062
2063 def header(ui, repo, patch=None):
2063 def header(ui, repo, patch=None):
2064 """print the header of the topmost or specified patch"""
2064 """print the header of the topmost or specified patch"""
2065 q = repo.mq
2065 q = repo.mq
2066
2066
2067 if patch:
2067 if patch:
2068 patch = q.lookup(patch)
2068 patch = q.lookup(patch)
2069 else:
2069 else:
2070 if not q.applied:
2070 if not q.applied:
2071 ui.write('no patches applied\n')
2071 ui.write('no patches applied\n')
2072 return 1
2072 return 1
2073 patch = q.lookup('qtip')
2073 patch = q.lookup('qtip')
2074 ph = patchheader(repo.mq.join(patch))
2074 ph = patchheader(repo.mq.join(patch))
2075
2075
2076 ui.write('\n'.join(ph.message) + '\n')
2076 ui.write('\n'.join(ph.message) + '\n')
2077
2077
2078 def lastsavename(path):
2078 def lastsavename(path):
2079 (directory, base) = os.path.split(path)
2079 (directory, base) = os.path.split(path)
2080 names = os.listdir(directory)
2080 names = os.listdir(directory)
2081 namere = re.compile("%s.([0-9]+)" % base)
2081 namere = re.compile("%s.([0-9]+)" % base)
2082 maxindex = None
2082 maxindex = None
2083 maxname = None
2083 maxname = None
2084 for f in names:
2084 for f in names:
2085 m = namere.match(f)
2085 m = namere.match(f)
2086 if m:
2086 if m:
2087 index = int(m.group(1))
2087 index = int(m.group(1))
2088 if maxindex is None or index > maxindex:
2088 if maxindex is None or index > maxindex:
2089 maxindex = index
2089 maxindex = index
2090 maxname = f
2090 maxname = f
2091 if maxname:
2091 if maxname:
2092 return (os.path.join(directory, maxname), maxindex)
2092 return (os.path.join(directory, maxname), maxindex)
2093 return (None, None)
2093 return (None, None)
2094
2094
2095 def savename(path):
2095 def savename(path):
2096 (last, index) = lastsavename(path)
2096 (last, index) = lastsavename(path)
2097 if last is None:
2097 if last is None:
2098 index = 0
2098 index = 0
2099 newpath = path + ".%d" % (index + 1)
2099 newpath = path + ".%d" % (index + 1)
2100 return newpath
2100 return newpath
2101
2101
2102 def push(ui, repo, patch=None, **opts):
2102 def push(ui, repo, patch=None, **opts):
2103 """push the next patch onto the stack
2103 """push the next patch onto the stack
2104
2104
2105 When -f/--force is applied, all local changes in patched files
2105 When -f/--force is applied, all local changes in patched files
2106 will be lost.
2106 will be lost.
2107 """
2107 """
2108 q = repo.mq
2108 q = repo.mq
2109 mergeq = None
2109 mergeq = None
2110
2110
2111 if opts['merge']:
2111 if opts['merge']:
2112 if opts['name']:
2112 if opts['name']:
2113 newpath = repo.join(opts['name'])
2113 newpath = repo.join(opts['name'])
2114 else:
2114 else:
2115 newpath, i = lastsavename(q.path)
2115 newpath, i = lastsavename(q.path)
2116 if not newpath:
2116 if not newpath:
2117 ui.warn(_("no saved queues found, please use -n\n"))
2117 ui.warn(_("no saved queues found, please use -n\n"))
2118 return 1
2118 return 1
2119 mergeq = queue(ui, repo.join(""), newpath)
2119 mergeq = queue(ui, repo.join(""), newpath)
2120 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2120 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2121 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2121 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2122 mergeq=mergeq, all=opts.get('all'))
2122 mergeq=mergeq, all=opts.get('all'))
2123 return ret
2123 return ret
2124
2124
2125 def pop(ui, repo, patch=None, **opts):
2125 def pop(ui, repo, patch=None, **opts):
2126 """pop the current patch off the stack
2126 """pop the current patch off the stack
2127
2127
2128 By default, pops off the top of the patch stack. If given a patch
2128 By default, pops off the top of the patch stack. If given a patch
2129 name, keeps popping off patches until the named patch is at the
2129 name, keeps popping off patches until the named patch is at the
2130 top of the stack.
2130 top of the stack.
2131 """
2131 """
2132 localupdate = True
2132 localupdate = True
2133 if opts['name']:
2133 if opts['name']:
2134 q = queue(ui, repo.join(""), repo.join(opts['name']))
2134 q = queue(ui, repo.join(""), repo.join(opts['name']))
2135 ui.warn(_('using patch queue: %s\n') % q.path)
2135 ui.warn(_('using patch queue: %s\n') % q.path)
2136 localupdate = False
2136 localupdate = False
2137 else:
2137 else:
2138 q = repo.mq
2138 q = repo.mq
2139 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2139 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2140 all=opts['all'])
2140 all=opts['all'])
2141 q.save_dirty()
2141 q.save_dirty()
2142 return ret
2142 return ret
2143
2143
2144 def rename(ui, repo, patch, name=None, **opts):
2144 def rename(ui, repo, patch, name=None, **opts):
2145 """rename a patch
2145 """rename a patch
2146
2146
2147 With one argument, renames the current patch to PATCH1.
2147 With one argument, renames the current patch to PATCH1.
2148 With two arguments, renames PATCH1 to PATCH2."""
2148 With two arguments, renames PATCH1 to PATCH2."""
2149
2149
2150 q = repo.mq
2150 q = repo.mq
2151
2151
2152 if not name:
2152 if not name:
2153 name = patch
2153 name = patch
2154 patch = None
2154 patch = None
2155
2155
2156 if patch:
2156 if patch:
2157 patch = q.lookup(patch)
2157 patch = q.lookup(patch)
2158 else:
2158 else:
2159 if not q.applied:
2159 if not q.applied:
2160 ui.write(_('no patches applied\n'))
2160 ui.write(_('no patches applied\n'))
2161 return
2161 return
2162 patch = q.lookup('qtip')
2162 patch = q.lookup('qtip')
2163 absdest = q.join(name)
2163 absdest = q.join(name)
2164 if os.path.isdir(absdest):
2164 if os.path.isdir(absdest):
2165 name = normname(os.path.join(name, os.path.basename(patch)))
2165 name = normname(os.path.join(name, os.path.basename(patch)))
2166 absdest = q.join(name)
2166 absdest = q.join(name)
2167 if os.path.exists(absdest):
2167 if os.path.exists(absdest):
2168 raise util.Abort(_('%s already exists') % absdest)
2168 raise util.Abort(_('%s already exists') % absdest)
2169
2169
2170 if name in q.series:
2170 if name in q.series:
2171 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2171 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2172
2172
2173 if ui.verbose:
2173 if ui.verbose:
2174 ui.write('renaming %s to %s\n' % (patch, name))
2174 ui.write('renaming %s to %s\n' % (patch, name))
2175 i = q.find_series(patch)
2175 i = q.find_series(patch)
2176 guards = q.guard_re.findall(q.full_series[i])
2176 guards = q.guard_re.findall(q.full_series[i])
2177 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2177 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2178 q.parse_series()
2178 q.parse_series()
2179 q.series_dirty = 1
2179 q.series_dirty = 1
2180
2180
2181 info = q.isapplied(patch)
2181 info = q.isapplied(patch)
2182 if info:
2182 if info:
2183 q.applied[info[0]] = statusentry(info[1], name)
2183 q.applied[info[0]] = statusentry(info[1], name)
2184 q.applied_dirty = 1
2184 q.applied_dirty = 1
2185
2185
2186 util.rename(q.join(patch), absdest)
2186 util.rename(q.join(patch), absdest)
2187 r = q.qrepo()
2187 r = q.qrepo()
2188 if r:
2188 if r:
2189 wlock = r.wlock()
2189 wlock = r.wlock()
2190 try:
2190 try:
2191 if r.dirstate[patch] == 'a':
2191 if r.dirstate[patch] == 'a':
2192 r.dirstate.forget(patch)
2192 r.dirstate.forget(patch)
2193 r.dirstate.add(name)
2193 r.dirstate.add(name)
2194 else:
2194 else:
2195 if r.dirstate[name] == 'r':
2195 if r.dirstate[name] == 'r':
2196 r.undelete([name])
2196 r.undelete([name])
2197 r.copy(patch, name)
2197 r.copy(patch, name)
2198 r.remove([patch], False)
2198 r.remove([patch], False)
2199 finally:
2199 finally:
2200 wlock.release()
2200 wlock.release()
2201
2201
2202 q.save_dirty()
2202 q.save_dirty()
2203
2203
2204 def restore(ui, repo, rev, **opts):
2204 def restore(ui, repo, rev, **opts):
2205 """restore the queue state saved by a revision"""
2205 """restore the queue state saved by a revision"""
2206 rev = repo.lookup(rev)
2206 rev = repo.lookup(rev)
2207 q = repo.mq
2207 q = repo.mq
2208 q.restore(repo, rev, delete=opts['delete'],
2208 q.restore(repo, rev, delete=opts['delete'],
2209 qupdate=opts['update'])
2209 qupdate=opts['update'])
2210 q.save_dirty()
2210 q.save_dirty()
2211 return 0
2211 return 0
2212
2212
2213 def save(ui, repo, **opts):
2213 def save(ui, repo, **opts):
2214 """save current queue state"""
2214 """save current queue state"""
2215 q = repo.mq
2215 q = repo.mq
2216 message = cmdutil.logmessage(opts)
2216 message = cmdutil.logmessage(opts)
2217 ret = q.save(repo, msg=message)
2217 ret = q.save(repo, msg=message)
2218 if ret:
2218 if ret:
2219 return ret
2219 return ret
2220 q.save_dirty()
2220 q.save_dirty()
2221 if opts['copy']:
2221 if opts['copy']:
2222 path = q.path
2222 path = q.path
2223 if opts['name']:
2223 if opts['name']:
2224 newpath = os.path.join(q.basepath, opts['name'])
2224 newpath = os.path.join(q.basepath, opts['name'])
2225 if os.path.exists(newpath):
2225 if os.path.exists(newpath):
2226 if not os.path.isdir(newpath):
2226 if not os.path.isdir(newpath):
2227 raise util.Abort(_('destination %s exists and is not '
2227 raise util.Abort(_('destination %s exists and is not '
2228 'a directory') % newpath)
2228 'a directory') % newpath)
2229 if not opts['force']:
2229 if not opts['force']:
2230 raise util.Abort(_('destination %s exists, '
2230 raise util.Abort(_('destination %s exists, '
2231 'use -f to force') % newpath)
2231 'use -f to force') % newpath)
2232 else:
2232 else:
2233 newpath = savename(path)
2233 newpath = savename(path)
2234 ui.warn(_("copy %s to %s\n") % (path, newpath))
2234 ui.warn(_("copy %s to %s\n") % (path, newpath))
2235 util.copyfiles(path, newpath)
2235 util.copyfiles(path, newpath)
2236 if opts['empty']:
2236 if opts['empty']:
2237 try:
2237 try:
2238 os.unlink(q.join(q.status_path))
2238 os.unlink(q.join(q.status_path))
2239 except:
2239 except:
2240 pass
2240 pass
2241 return 0
2241 return 0
2242
2242
2243 def strip(ui, repo, rev, **opts):
2243 def strip(ui, repo, rev, **opts):
2244 """strip a revision and all its descendants from the repository
2244 """strip a revision and all its descendants from the repository
2245
2245
2246 If one of the working directory's parent revisions is stripped, the
2246 If one of the working directory's parent revisions is stripped, the
2247 working directory will be updated to the parent of the stripped
2247 working directory will be updated to the parent of the stripped
2248 revision.
2248 revision.
2249 """
2249 """
2250 backup = 'all'
2250 backup = 'all'
2251 if opts['backup']:
2251 if opts['backup']:
2252 backup = 'strip'
2252 backup = 'strip'
2253 elif opts['nobackup']:
2253 elif opts['nobackup']:
2254 backup = 'none'
2254 backup = 'none'
2255
2255
2256 rev = repo.lookup(rev)
2256 rev = repo.lookup(rev)
2257 p = repo.dirstate.parents()
2257 p = repo.dirstate.parents()
2258 cl = repo.changelog
2258 cl = repo.changelog
2259 update = True
2259 update = True
2260 if p[0] == nullid:
2260 if p[0] == nullid:
2261 update = False
2261 update = False
2262 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2262 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2263 update = False
2263 update = False
2264 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2264 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2265 update = False
2265 update = False
2266
2266
2267 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2267 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2268 return 0
2268 return 0
2269
2269
2270 def select(ui, repo, *args, **opts):
2270 def select(ui, repo, *args, **opts):
2271 '''set or print guarded patches to push
2271 '''set or print guarded patches to push
2272
2272
2273 Use the qguard command to set or print guards on patch, then use
2273 Use the qguard command to set or print guards on patch, then use
2274 qselect to tell mq which guards to use. A patch will be pushed if
2274 qselect to tell mq which guards to use. A patch will be pushed if
2275 it has no guards or any positive guards match the currently
2275 it has no guards or any positive guards match the currently
2276 selected guard, but will not be pushed if any negative guards
2276 selected guard, but will not be pushed if any negative guards
2277 match the current guard. For example:
2277 match the current guard. For example:
2278
2278
2279 qguard foo.patch -stable (negative guard)
2279 qguard foo.patch -stable (negative guard)
2280 qguard bar.patch +stable (positive guard)
2280 qguard bar.patch +stable (positive guard)
2281 qselect stable
2281 qselect stable
2282
2282
2283 This activates the "stable" guard. mq will skip foo.patch (because
2283 This activates the "stable" guard. mq will skip foo.patch (because
2284 it has a negative match) but push bar.patch (because it has a
2284 it has a negative match) but push bar.patch (because it has a
2285 positive match).
2285 positive match).
2286
2286
2287 With no arguments, prints the currently active guards.
2287 With no arguments, prints the currently active guards.
2288 With one argument, sets the active guard.
2288 With one argument, sets the active guard.
2289
2289
2290 Use -n/--none to deactivate guards (no other arguments needed).
2290 Use -n/--none to deactivate guards (no other arguments needed).
2291 When no guards are active, patches with positive guards are
2291 When no guards are active, patches with positive guards are
2292 skipped and patches with negative guards are pushed.
2292 skipped and patches with negative guards are pushed.
2293
2293
2294 qselect can change the guards on applied patches. It does not pop
2294 qselect can change the guards on applied patches. It does not pop
2295 guarded patches by default. Use --pop to pop back to the last
2295 guarded patches by default. Use --pop to pop back to the last
2296 applied patch that is not guarded. Use --reapply (which implies
2296 applied patch that is not guarded. Use --reapply (which implies
2297 --pop) to push back to the current patch afterwards, but skip
2297 --pop) to push back to the current patch afterwards, but skip
2298 guarded patches.
2298 guarded patches.
2299
2299
2300 Use -s/--series to print a list of all guards in the series file
2300 Use -s/--series to print a list of all guards in the series file
2301 (no other arguments needed). Use -v for more information.'''
2301 (no other arguments needed). Use -v for more information.'''
2302
2302
2303 q = repo.mq
2303 q = repo.mq
2304 guards = q.active()
2304 guards = q.active()
2305 if args or opts['none']:
2305 if args or opts['none']:
2306 old_unapplied = q.unapplied(repo)
2306 old_unapplied = q.unapplied(repo)
2307 old_guarded = [i for i in xrange(len(q.applied)) if
2307 old_guarded = [i for i in xrange(len(q.applied)) if
2308 not q.pushable(i)[0]]
2308 not q.pushable(i)[0]]
2309 q.set_active(args)
2309 q.set_active(args)
2310 q.save_dirty()
2310 q.save_dirty()
2311 if not args:
2311 if not args:
2312 ui.status(_('guards deactivated\n'))
2312 ui.status(_('guards deactivated\n'))
2313 if not opts['pop'] and not opts['reapply']:
2313 if not opts['pop'] and not opts['reapply']:
2314 unapplied = q.unapplied(repo)
2314 unapplied = q.unapplied(repo)
2315 guarded = [i for i in xrange(len(q.applied))
2315 guarded = [i for i in xrange(len(q.applied))
2316 if not q.pushable(i)[0]]
2316 if not q.pushable(i)[0]]
2317 if len(unapplied) != len(old_unapplied):
2317 if len(unapplied) != len(old_unapplied):
2318 ui.status(_('number of unguarded, unapplied patches has '
2318 ui.status(_('number of unguarded, unapplied patches has '
2319 'changed from %d to %d\n') %
2319 'changed from %d to %d\n') %
2320 (len(old_unapplied), len(unapplied)))
2320 (len(old_unapplied), len(unapplied)))
2321 if len(guarded) != len(old_guarded):
2321 if len(guarded) != len(old_guarded):
2322 ui.status(_('number of guarded, applied patches has changed '
2322 ui.status(_('number of guarded, applied patches has changed '
2323 'from %d to %d\n') %
2323 'from %d to %d\n') %
2324 (len(old_guarded), len(guarded)))
2324 (len(old_guarded), len(guarded)))
2325 elif opts['series']:
2325 elif opts['series']:
2326 guards = {}
2326 guards = {}
2327 noguards = 0
2327 noguards = 0
2328 for gs in q.series_guards:
2328 for gs in q.series_guards:
2329 if not gs:
2329 if not gs:
2330 noguards += 1
2330 noguards += 1
2331 for g in gs:
2331 for g in gs:
2332 guards.setdefault(g, 0)
2332 guards.setdefault(g, 0)
2333 guards[g] += 1
2333 guards[g] += 1
2334 if ui.verbose:
2334 if ui.verbose:
2335 guards['NONE'] = noguards
2335 guards['NONE'] = noguards
2336 guards = guards.items()
2336 guards = guards.items()
2337 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2337 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2338 if guards:
2338 if guards:
2339 ui.note(_('guards in series file:\n'))
2339 ui.note(_('guards in series file:\n'))
2340 for guard, count in guards:
2340 for guard, count in guards:
2341 ui.note('%2d ' % count)
2341 ui.note('%2d ' % count)
2342 ui.write(guard, '\n')
2342 ui.write(guard, '\n')
2343 else:
2343 else:
2344 ui.note(_('no guards in series file\n'))
2344 ui.note(_('no guards in series file\n'))
2345 else:
2345 else:
2346 if guards:
2346 if guards:
2347 ui.note(_('active guards:\n'))
2347 ui.note(_('active guards:\n'))
2348 for g in guards:
2348 for g in guards:
2349 ui.write(g, '\n')
2349 ui.write(g, '\n')
2350 else:
2350 else:
2351 ui.write(_('no active guards\n'))
2351 ui.write(_('no active guards\n'))
2352 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2352 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2353 popped = False
2353 popped = False
2354 if opts['pop'] or opts['reapply']:
2354 if opts['pop'] or opts['reapply']:
2355 for i in xrange(len(q.applied)):
2355 for i in xrange(len(q.applied)):
2356 pushable, reason = q.pushable(i)
2356 pushable, reason = q.pushable(i)
2357 if not pushable:
2357 if not pushable:
2358 ui.status(_('popping guarded patches\n'))
2358 ui.status(_('popping guarded patches\n'))
2359 popped = True
2359 popped = True
2360 if i == 0:
2360 if i == 0:
2361 q.pop(repo, all=True)
2361 q.pop(repo, all=True)
2362 else:
2362 else:
2363 q.pop(repo, i-1)
2363 q.pop(repo, i-1)
2364 break
2364 break
2365 if popped:
2365 if popped:
2366 try:
2366 try:
2367 if reapply:
2367 if reapply:
2368 ui.status(_('reapplying unguarded patches\n'))
2368 ui.status(_('reapplying unguarded patches\n'))
2369 q.push(repo, reapply)
2369 q.push(repo, reapply)
2370 finally:
2370 finally:
2371 q.save_dirty()
2371 q.save_dirty()
2372
2372
2373 def finish(ui, repo, *revrange, **opts):
2373 def finish(ui, repo, *revrange, **opts):
2374 """move applied patches into repository history
2374 """move applied patches into repository history
2375
2375
2376 Finishes the specified revisions (corresponding to applied
2376 Finishes the specified revisions (corresponding to applied
2377 patches) by moving them out of mq control into regular repository
2377 patches) by moving them out of mq control into regular repository
2378 history.
2378 history.
2379
2379
2380 Accepts a revision range or the -a/--applied option. If --applied
2380 Accepts a revision range or the -a/--applied option. If --applied
2381 is specified, all applied mq revisions are removed from mq
2381 is specified, all applied mq revisions are removed from mq
2382 control. Otherwise, the given revisions must be at the base of the
2382 control. Otherwise, the given revisions must be at the base of the
2383 stack of applied patches.
2383 stack of applied patches.
2384
2384
2385 This can be especially useful if your changes have been applied to
2385 This can be especially useful if your changes have been applied to
2386 an upstream repository, or if you are about to push your changes
2386 an upstream repository, or if you are about to push your changes
2387 to upstream.
2387 to upstream.
2388 """
2388 """
2389 if not opts['applied'] and not revrange:
2389 if not opts['applied'] and not revrange:
2390 raise util.Abort(_('no revisions specified'))
2390 raise util.Abort(_('no revisions specified'))
2391 elif opts['applied']:
2391 elif opts['applied']:
2392 revrange = ('qbase:qtip',) + revrange
2392 revrange = ('qbase:qtip',) + revrange
2393
2393
2394 q = repo.mq
2394 q = repo.mq
2395 if not q.applied:
2395 if not q.applied:
2396 ui.status(_('no patches applied\n'))
2396 ui.status(_('no patches applied\n'))
2397 return 0
2397 return 0
2398
2398
2399 revs = cmdutil.revrange(repo, revrange)
2399 revs = cmdutil.revrange(repo, revrange)
2400 q.finish(repo, revs)
2400 q.finish(repo, revs)
2401 q.save_dirty()
2401 q.save_dirty()
2402 return 0
2402 return 0
2403
2403
2404 def reposetup(ui, repo):
2404 def reposetup(ui, repo):
2405 class mqrepo(repo.__class__):
2405 class mqrepo(repo.__class__):
2406 @util.propertycache
2406 @util.propertycache
2407 def mq(self):
2407 def mq(self):
2408 return queue(self.ui, self.join(""))
2408 return queue(self.ui, self.join(""))
2409
2409
2410 def abort_if_wdir_patched(self, errmsg, force=False):
2410 def abort_if_wdir_patched(self, errmsg, force=False):
2411 if self.mq.applied and not force:
2411 if self.mq.applied and not force:
2412 parent = hex(self.dirstate.parents()[0])
2412 parent = hex(self.dirstate.parents()[0])
2413 if parent in [s.rev for s in self.mq.applied]:
2413 if parent in [s.rev for s in self.mq.applied]:
2414 raise util.Abort(errmsg)
2414 raise util.Abort(errmsg)
2415
2415
2416 def commit(self, text="", user=None, date=None, match=None,
2416 def commit(self, text="", user=None, date=None, match=None,
2417 force=False, editor=False, extra={}):
2417 force=False, editor=False, extra={}):
2418 self.abort_if_wdir_patched(
2418 self.abort_if_wdir_patched(
2419 _('cannot commit over an applied mq patch'),
2419 _('cannot commit over an applied mq patch'),
2420 force)
2420 force)
2421
2421
2422 return super(mqrepo, self).commit(text, user, date, match, force,
2422 return super(mqrepo, self).commit(text, user, date, match, force,
2423 editor, extra)
2423 editor, extra)
2424
2424
2425 def push(self, remote, force=False, revs=None):
2425 def push(self, remote, force=False, revs=None):
2426 if self.mq.applied and not force and not revs:
2426 if self.mq.applied and not force and not revs:
2427 raise util.Abort(_('source has mq patches applied'))
2427 raise util.Abort(_('source has mq patches applied'))
2428 return super(mqrepo, self).push(remote, force, revs)
2428 return super(mqrepo, self).push(remote, force, revs)
2429
2429
2430 def tags(self):
2430 def tags(self):
2431 if self.tagscache:
2431 if self.tagscache:
2432 return self.tagscache
2432 return self.tagscache
2433
2433
2434 tagscache = super(mqrepo, self).tags()
2434 tagscache = super(mqrepo, self).tags()
2435
2435
2436 q = self.mq
2436 q = self.mq
2437 if not q.applied:
2437 if not q.applied:
2438 return tagscache
2438 return tagscache
2439
2439
2440 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2440 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2441
2441
2442 if mqtags[-1][0] not in self.changelog.nodemap:
2442 if mqtags[-1][0] not in self.changelog.nodemap:
2443 self.ui.warn(_('mq status file refers to unknown node %s\n')
2443 self.ui.warn(_('mq status file refers to unknown node %s\n')
2444 % short(mqtags[-1][0]))
2444 % short(mqtags[-1][0]))
2445 return tagscache
2445 return tagscache
2446
2446
2447 mqtags.append((mqtags[-1][0], 'qtip'))
2447 mqtags.append((mqtags[-1][0], 'qtip'))
2448 mqtags.append((mqtags[0][0], 'qbase'))
2448 mqtags.append((mqtags[0][0], 'qbase'))
2449 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2449 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2450 for patch in mqtags:
2450 for patch in mqtags:
2451 if patch[1] in tagscache:
2451 if patch[1] in tagscache:
2452 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2452 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2453 % patch[1])
2453 % patch[1])
2454 else:
2454 else:
2455 tagscache[patch[1]] = patch[0]
2455 tagscache[patch[1]] = patch[0]
2456
2456
2457 return tagscache
2457 return tagscache
2458
2458
2459 def _branchtags(self, partial, lrev):
2459 def _branchtags(self, partial, lrev):
2460 q = self.mq
2460 q = self.mq
2461 if not q.applied:
2461 if not q.applied:
2462 return super(mqrepo, self)._branchtags(partial, lrev)
2462 return super(mqrepo, self)._branchtags(partial, lrev)
2463
2463
2464 cl = self.changelog
2464 cl = self.changelog
2465 qbasenode = bin(q.applied[0].rev)
2465 qbasenode = bin(q.applied[0].rev)
2466 if qbasenode not in cl.nodemap:
2466 if qbasenode not in cl.nodemap:
2467 self.ui.warn(_('mq status file refers to unknown node %s\n')
2467 self.ui.warn(_('mq status file refers to unknown node %s\n')
2468 % short(qbasenode))
2468 % short(qbasenode))
2469 return super(mqrepo, self)._branchtags(partial, lrev)
2469 return super(mqrepo, self)._branchtags(partial, lrev)
2470
2470
2471 qbase = cl.rev(qbasenode)
2471 qbase = cl.rev(qbasenode)
2472 start = lrev + 1
2472 start = lrev + 1
2473 if start < qbase:
2473 if start < qbase:
2474 # update the cache (excluding the patches) and save it
2474 # update the cache (excluding the patches) and save it
2475 self._updatebranchcache(partial, lrev+1, qbase)
2475 self._updatebranchcache(partial, lrev+1, qbase)
2476 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2476 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2477 start = qbase
2477 start = qbase
2478 # if start = qbase, the cache is as updated as it should be.
2478 # if start = qbase, the cache is as updated as it should be.
2479 # if start > qbase, the cache includes (part of) the patches.
2479 # if start > qbase, the cache includes (part of) the patches.
2480 # we might as well use it, but we won't save it.
2480 # we might as well use it, but we won't save it.
2481
2481
2482 # update the cache up to the tip
2482 # update the cache up to the tip
2483 self._updatebranchcache(partial, start, len(cl))
2483 self._updatebranchcache(partial, start, len(cl))
2484
2484
2485 return partial
2485 return partial
2486
2486
2487 if repo.local():
2487 if repo.local():
2488 repo.__class__ = mqrepo
2488 repo.__class__ = mqrepo
2489
2489
2490 def mqimport(orig, ui, repo, *args, **kwargs):
2490 def mqimport(orig, ui, repo, *args, **kwargs):
2491 if hasattr(repo, 'abort_if_wdir_patched'):
2491 if hasattr(repo, 'abort_if_wdir_patched'):
2492 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2492 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2493 kwargs.get('force'))
2493 kwargs.get('force'))
2494 return orig(ui, repo, *args, **kwargs)
2494 return orig(ui, repo, *args, **kwargs)
2495
2495
2496 def uisetup(ui):
2496 def uisetup(ui):
2497 extensions.wrapcommand(commands.table, 'import', mqimport)
2497 extensions.wrapcommand(commands.table, 'import', mqimport)
2498
2498
2499 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2499 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2500
2500
2501 cmdtable = {
2501 cmdtable = {
2502 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2502 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2503 "qclone":
2503 "qclone":
2504 (clone,
2504 (clone,
2505 [('', 'pull', None, _('use pull protocol to copy metadata')),
2505 [('', 'pull', None, _('use pull protocol to copy metadata')),
2506 ('U', 'noupdate', None, _('do not update the new working directories')),
2506 ('U', 'noupdate', None, _('do not update the new working directories')),
2507 ('', 'uncompressed', None,
2507 ('', 'uncompressed', None,
2508 _('use uncompressed transfer (fast over LAN)')),
2508 _('use uncompressed transfer (fast over LAN)')),
2509 ('p', 'patches', '', _('location of source patch repository')),
2509 ('p', 'patches', '', _('location of source patch repository')),
2510 ] + commands.remoteopts,
2510 ] + commands.remoteopts,
2511 _('hg qclone [OPTION]... SOURCE [DEST]')),
2511 _('hg qclone [OPTION]... SOURCE [DEST]')),
2512 "qcommit|qci":
2512 "qcommit|qci":
2513 (commit,
2513 (commit,
2514 commands.table["^commit|ci"][1],
2514 commands.table["^commit|ci"][1],
2515 _('hg qcommit [OPTION]... [FILE]...')),
2515 _('hg qcommit [OPTION]... [FILE]...')),
2516 "^qdiff":
2516 "^qdiff":
2517 (diff,
2517 (diff,
2518 commands.diffopts + commands.diffopts2 + commands.walkopts,
2518 commands.diffopts + commands.diffopts2 + commands.walkopts,
2519 _('hg qdiff [OPTION]... [FILE]...')),
2519 _('hg qdiff [OPTION]... [FILE]...')),
2520 "qdelete|qremove|qrm":
2520 "qdelete|qremove|qrm":
2521 (delete,
2521 (delete,
2522 [('k', 'keep', None, _('keep patch file')),
2522 [('k', 'keep', None, _('keep patch file')),
2523 ('r', 'rev', [], _('stop managing a revision'))],
2523 ('r', 'rev', [], _('stop managing a revision'))],
2524 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2524 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2525 'qfold':
2525 'qfold':
2526 (fold,
2526 (fold,
2527 [('e', 'edit', None, _('edit patch header')),
2527 [('e', 'edit', None, _('edit patch header')),
2528 ('k', 'keep', None, _('keep folded patch files')),
2528 ('k', 'keep', None, _('keep folded patch files')),
2529 ] + commands.commitopts,
2529 ] + commands.commitopts,
2530 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2530 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2531 'qgoto':
2531 'qgoto':
2532 (goto,
2532 (goto,
2533 [('f', 'force', None, _('overwrite any local changes'))],
2533 [('f', 'force', None, _('overwrite any local changes'))],
2534 _('hg qgoto [OPTION]... PATCH')),
2534 _('hg qgoto [OPTION]... PATCH')),
2535 'qguard':
2535 'qguard':
2536 (guard,
2536 (guard,
2537 [('l', 'list', None, _('list all patches and guards')),
2537 [('l', 'list', None, _('list all patches and guards')),
2538 ('n', 'none', None, _('drop all guards'))],
2538 ('n', 'none', None, _('drop all guards'))],
2539 _('hg qguard [-l] [-n] -- [PATCH] [+GUARD]... [-GUARD]...')),
2539 _('hg qguard [-l] [-n] -- [PATCH] [+GUARD]... [-GUARD]...')),
2540 'qheader': (header, [], _('hg qheader [PATCH]')),
2540 'qheader': (header, [], _('hg qheader [PATCH]')),
2541 "^qimport":
2541 "^qimport":
2542 (qimport,
2542 (qimport,
2543 [('e', 'existing', None, _('import file in patch directory')),
2543 [('e', 'existing', None, _('import file in patch directory')),
2544 ('n', 'name', '', _('patch file name')),
2544 ('n', 'name', '', _('patch file name')),
2545 ('f', 'force', None, _('overwrite existing files')),
2545 ('f', 'force', None, _('overwrite existing files')),
2546 ('r', 'rev', [], _('place existing revisions under mq control')),
2546 ('r', 'rev', [], _('place existing revisions under mq control')),
2547 ('g', 'git', None, _('use git extended diff format')),
2547 ('g', 'git', None, _('use git extended diff format')),
2548 ('P', 'push', None, _('qpush after importing'))],
2548 ('P', 'push', None, _('qpush after importing'))],
2549 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2549 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2550 "^qinit":
2550 "^qinit":
2551 (init,
2551 (init,
2552 [('c', 'create-repo', None, _('create queue repository'))],
2552 [('c', 'create-repo', None, _('create queue repository'))],
2553 _('hg qinit [-c]')),
2553 _('hg qinit [-c]')),
2554 "qnew":
2554 "qnew":
2555 (new,
2555 (new,
2556 [('e', 'edit', None, _('edit commit message')),
2556 [('e', 'edit', None, _('edit commit message')),
2557 ('f', 'force', None, _('import uncommitted changes into patch')),
2557 ('f', 'force', None, _('import uncommitted changes into patch')),
2558 ('g', 'git', None, _('use git extended diff format')),
2558 ('g', 'git', None, _('use git extended diff format')),
2559 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2559 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2560 ('u', 'user', '', _('add "From: <given user>" to patch')),
2560 ('u', 'user', '', _('add "From: <given user>" to patch')),
2561 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2561 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2562 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2562 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2563 ] + commands.walkopts + commands.commitopts,
2563 ] + commands.walkopts + commands.commitopts,
2564 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2564 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2565 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2565 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2566 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2566 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2567 "^qpop":
2567 "^qpop":
2568 (pop,
2568 (pop,
2569 [('a', 'all', None, _('pop all patches')),
2569 [('a', 'all', None, _('pop all patches')),
2570 ('n', 'name', '', _('queue name to pop')),
2570 ('n', 'name', '', _('queue name to pop')),
2571 ('f', 'force', None, _('forget any local changes'))],
2571 ('f', 'force', None, _('forget any local changes'))],
2572 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2572 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2573 "^qpush":
2573 "^qpush":
2574 (push,
2574 (push,
2575 [('f', 'force', None, _('apply if the patch has rejects')),
2575 [('f', 'force', None, _('apply if the patch has rejects')),
2576 ('l', 'list', None, _('list patch name in commit text')),
2576 ('l', 'list', None, _('list patch name in commit text')),
2577 ('a', 'all', None, _('apply all patches')),
2577 ('a', 'all', None, _('apply all patches')),
2578 ('m', 'merge', None, _('merge from another queue')),
2578 ('m', 'merge', None, _('merge from another queue')),
2579 ('n', 'name', '', _('merge queue name'))],
2579 ('n', 'name', '', _('merge queue name'))],
2580 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2580 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2581 "^qrefresh":
2581 "^qrefresh":
2582 (refresh,
2582 (refresh,
2583 [('e', 'edit', None, _('edit commit message')),
2583 [('e', 'edit', None, _('edit commit message')),
2584 ('g', 'git', None, _('use git extended diff format')),
2584 ('g', 'git', None, _('use git extended diff format')),
2585 ('s', 'short', None, _('refresh only files already in the patch and specified files')),
2585 ('s', 'short', None, _('refresh only files already in the patch and specified files')),
2586 ('U', 'currentuser', None, _('add/update "From: <current user>" in patch')),
2586 ('U', 'currentuser', None, _('add/update "From: <current user>" in patch')),
2587 ('u', 'user', '', _('add/update "From: <given user>" in patch')),
2587 ('u', 'user', '', _('add/update "From: <given user>" in patch')),
2588 ('D', 'currentdate', None, _('update "Date: <current date>" in patch (if present)')),
2588 ('D', 'currentdate', None, _('update "Date: <current date>" in patch (if present)')),
2589 ('d', 'date', '', _('update "Date: <given date>" in patch (if present)'))
2589 ('d', 'date', '', _('update "Date: <given date>" in patch (if present)'))
2590 ] + commands.walkopts + commands.commitopts,
2590 ] + commands.walkopts + commands.commitopts,
2591 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2591 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2592 'qrename|qmv':
2592 'qrename|qmv':
2593 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2593 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2594 "qrestore":
2594 "qrestore":
2595 (restore,
2595 (restore,
2596 [('d', 'delete', None, _('delete save entry')),
2596 [('d', 'delete', None, _('delete save entry')),
2597 ('u', 'update', None, _('update queue working directory'))],
2597 ('u', 'update', None, _('update queue working directory'))],
2598 _('hg qrestore [-d] [-u] REV')),
2598 _('hg qrestore [-d] [-u] REV')),
2599 "qsave":
2599 "qsave":
2600 (save,
2600 (save,
2601 [('c', 'copy', None, _('copy patch directory')),
2601 [('c', 'copy', None, _('copy patch directory')),
2602 ('n', 'name', '', _('copy directory name')),
2602 ('n', 'name', '', _('copy directory name')),
2603 ('e', 'empty', None, _('clear queue status file')),
2603 ('e', 'empty', None, _('clear queue status file')),
2604 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2604 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2605 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2605 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2606 "qselect":
2606 "qselect":
2607 (select,
2607 (select,
2608 [('n', 'none', None, _('disable all guards')),
2608 [('n', 'none', None, _('disable all guards')),
2609 ('s', 'series', None, _('list all guards in series file')),
2609 ('s', 'series', None, _('list all guards in series file')),
2610 ('', 'pop', None, _('pop to before first guarded applied patch')),
2610 ('', 'pop', None, _('pop to before first guarded applied patch')),
2611 ('', 'reapply', None, _('pop, then reapply patches'))],
2611 ('', 'reapply', None, _('pop, then reapply patches'))],
2612 _('hg qselect [OPTION]... [GUARD]...')),
2612 _('hg qselect [OPTION]... [GUARD]...')),
2613 "qseries":
2613 "qseries":
2614 (series,
2614 (series,
2615 [('m', 'missing', None, _('print patches not in series')),
2615 [('m', 'missing', None, _('print patches not in series')),
2616 ] + seriesopts,
2616 ] + seriesopts,
2617 _('hg qseries [-ms]')),
2617 _('hg qseries [-ms]')),
2618 "^strip":
2618 "^strip":
2619 (strip,
2619 (strip,
2620 [('f', 'force', None, _('force removal with local changes')),
2620 [('f', 'force', None, _('force removal with local changes')),
2621 ('b', 'backup', None, _('bundle unrelated changesets')),
2621 ('b', 'backup', None, _('bundle unrelated changesets')),
2622 ('n', 'nobackup', None, _('no backups'))],
2622 ('n', 'nobackup', None, _('no backups'))],
2623 _('hg strip [-f] [-b] [-n] REV')),
2623 _('hg strip [-f] [-b] [-n] REV')),
2624 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2624 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2625 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2625 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2626 "qfinish":
2626 "qfinish":
2627 (finish,
2627 (finish,
2628 [('a', 'applied', None, _('finish all applied changesets'))],
2628 [('a', 'applied', None, _('finish all applied changesets'))],
2629 _('hg qfinish [-a] [REV...]')),
2629 _('hg qfinish [-a] [REV...]')),
2630 }
2630 }
@@ -1,126 +1,126 b''
1 # win32mbcs.py -- MBCS filename support for Mercurial
1 # win32mbcs.py -- MBCS filename support for Mercurial
2 #
2 #
3 # Copyright (c) 2008 Shun-ichi Goto <shunichi.goto@gmail.com>
3 # Copyright (c) 2008 Shun-ichi Goto <shunichi.goto@gmail.com>
4 #
4 #
5 # Version: 0.2
5 # Version: 0.2
6 # Author: Shun-ichi Goto <shunichi.goto@gmail.com>
6 # Author: Shun-ichi Goto <shunichi.goto@gmail.com>
7 #
7 #
8 # This software may be used and distributed according to the terms of the
8 # This software may be used and distributed according to the terms of the
9 # GNU General Public License version 2, incorporated herein by reference.
9 # GNU General Public License version 2, incorporated herein by reference.
10 #
10 #
11
11
12 """allow to use MBCS path with problematic encoding.
12 """allow to use MBCS path with problematic encoding.
13
13
14 Some MBCS encodings are not good for some path operations (i.e.
14 Some MBCS encodings are not good for some path operations (i.e.
15 splitting path, case conversion, etc.) with its encoded bytes. We call
15 splitting path, case conversion, etc.) with its encoded bytes. We call
16 such a encoding (i.e. shift_jis and big5) as "problematic encoding".
16 such a encoding (i.e. shift_jis and big5) as "problematic encoding".
17 This extension can be used to fix the issue with those encodings by
17 This extension can be used to fix the issue with those encodings by
18 wrapping some functions to convert to Unicode string before path
18 wrapping some functions to convert to Unicode string before path
19 operation.
19 operation.
20
20
21 This extension is useful for:
21 This extension is useful for:
22 * Japanese Windows users using shift_jis encoding.
22 * Japanese Windows users using shift_jis encoding.
23 * Chinese Windows users using big5 encoding.
23 * Chinese Windows users using big5 encoding.
24 * All users who use a repository with one of problematic encodings on
24 * All users who use a repository with one of problematic encodings on
25 case-insensitive file system.
25 case-insensitive file system.
26
26
27 This extension is not needed for:
27 This extension is not needed for:
28 * Any user who use only ASCII chars in path.
28 * Any user who use only ASCII chars in path.
29 * Any user who do not use any of problematic encodings.
29 * Any user who do not use any of problematic encodings.
30
30
31 Note that there are some limitations on using this extension:
31 Note that there are some limitations on using this extension:
32 * You should use single encoding in one repository.
32 * You should use single encoding in one repository.
33 * You should set same encoding for the repository by locale or
33 * You should set same encoding for the repository by locale or
34 HGENCODING.
34 HGENCODING.
35
35
36 To use this extension, enable the extension in .hg/hgrc or ~/.hgrc:
36 To use this extension, enable the extension in .hg/hgrc or ~/.hgrc:
37
37
38 [extensions]
38 [extensions]
39 hgext.win32mbcs =
39 hgext.win32mbcs =
40
40
41 Path encoding conversion are done between Unicode and
41 Path encoding conversion are done between Unicode and
42 encoding.encoding which is decided by mercurial from current locale
42 encoding.encoding which is decided by Mercurial from current locale
43 setting or HGENCODING.
43 setting or HGENCODING.
44
44
45 """
45 """
46
46
47 import os
47 import os
48 from mercurial.i18n import _
48 from mercurial.i18n import _
49 from mercurial import util, encoding
49 from mercurial import util, encoding
50
50
51 def decode(arg):
51 def decode(arg):
52 if isinstance(arg, str):
52 if isinstance(arg, str):
53 uarg = arg.decode(encoding.encoding)
53 uarg = arg.decode(encoding.encoding)
54 if arg == uarg.encode(encoding.encoding):
54 if arg == uarg.encode(encoding.encoding):
55 return uarg
55 return uarg
56 raise UnicodeError("Not local encoding")
56 raise UnicodeError("Not local encoding")
57 elif isinstance(arg, tuple):
57 elif isinstance(arg, tuple):
58 return tuple(map(decode, arg))
58 return tuple(map(decode, arg))
59 elif isinstance(arg, list):
59 elif isinstance(arg, list):
60 return map(decode, arg)
60 return map(decode, arg)
61 return arg
61 return arg
62
62
63 def encode(arg):
63 def encode(arg):
64 if isinstance(arg, unicode):
64 if isinstance(arg, unicode):
65 return arg.encode(encoding.encoding)
65 return arg.encode(encoding.encoding)
66 elif isinstance(arg, tuple):
66 elif isinstance(arg, tuple):
67 return tuple(map(encode, arg))
67 return tuple(map(encode, arg))
68 elif isinstance(arg, list):
68 elif isinstance(arg, list):
69 return map(encode, arg)
69 return map(encode, arg)
70 return arg
70 return arg
71
71
72 def wrapper(func, args):
72 def wrapper(func, args):
73 # check argument is unicode, then call original
73 # check argument is unicode, then call original
74 for arg in args:
74 for arg in args:
75 if isinstance(arg, unicode):
75 if isinstance(arg, unicode):
76 return func(*args)
76 return func(*args)
77
77
78 try:
78 try:
79 # convert arguments to unicode, call func, then convert back
79 # convert arguments to unicode, call func, then convert back
80 return encode(func(*decode(args)))
80 return encode(func(*decode(args)))
81 except UnicodeError:
81 except UnicodeError:
82 # If not encoded with encoding.encoding, report it then
82 # If not encoded with encoding.encoding, report it then
83 # continue with calling original function.
83 # continue with calling original function.
84 raise util.Abort(_("[win32mbcs] filename conversion fail with"
84 raise util.Abort(_("[win32mbcs] filename conversion fail with"
85 " %s encoding\n") % (encoding.encoding))
85 " %s encoding\n") % (encoding.encoding))
86
86
87 def wrapname(name):
87 def wrapname(name):
88 idx = name.rfind('.')
88 idx = name.rfind('.')
89 module = name[:idx]
89 module = name[:idx]
90 name = name[idx+1:]
90 name = name[idx+1:]
91 module = globals()[module]
91 module = globals()[module]
92 func = getattr(module, name)
92 func = getattr(module, name)
93 def f(*args):
93 def f(*args):
94 return wrapper(func, args)
94 return wrapper(func, args)
95 try:
95 try:
96 f.__name__ = func.__name__ # fail with python23
96 f.__name__ = func.__name__ # fail with python23
97 except Exception:
97 except Exception:
98 pass
98 pass
99 setattr(module, name, f)
99 setattr(module, name, f)
100
100
101 # List of functions to be wrapped.
101 # List of functions to be wrapped.
102 # NOTE: os.path.dirname() and os.path.basename() are safe because
102 # NOTE: os.path.dirname() and os.path.basename() are safe because
103 # they use result of os.path.split()
103 # they use result of os.path.split()
104 funcs = '''os.path.join os.path.split os.path.splitext
104 funcs = '''os.path.join os.path.split os.path.splitext
105 os.path.splitunc os.path.normpath os.path.normcase os.makedirs
105 os.path.splitunc os.path.normpath os.path.normcase os.makedirs
106 util.endswithsep util.splitpath util.checkcase util.fspath'''
106 util.endswithsep util.splitpath util.checkcase util.fspath'''
107
107
108 # codec and alias names of sjis and big5 to be faked.
108 # codec and alias names of sjis and big5 to be faked.
109 problematic_encodings = '''big5 big5-tw csbig5 big5hkscs big5-hkscs
109 problematic_encodings = '''big5 big5-tw csbig5 big5hkscs big5-hkscs
110 hkscs cp932 932 ms932 mskanji ms-kanji shift_jis csshiftjis shiftjis
110 hkscs cp932 932 ms932 mskanji ms-kanji shift_jis csshiftjis shiftjis
111 sjis s_jis shift_jis_2004 shiftjis2004 sjis_2004 sjis2004
111 sjis s_jis shift_jis_2004 shiftjis2004 sjis_2004 sjis2004
112 shift_jisx0213 shiftjisx0213 sjisx0213 s_jisx0213 950 cp950 ms950 '''
112 shift_jisx0213 shiftjisx0213 sjisx0213 s_jisx0213 950 cp950 ms950 '''
113
113
114 def reposetup(ui, repo):
114 def reposetup(ui, repo):
115 # TODO: decide use of config section for this extension
115 # TODO: decide use of config section for this extension
116 if not os.path.supports_unicode_filenames:
116 if not os.path.supports_unicode_filenames:
117 ui.warn(_("[win32mbcs] cannot activate on this platform.\n"))
117 ui.warn(_("[win32mbcs] cannot activate on this platform.\n"))
118 return
118 return
119
119
120 # fake is only for relevant environment.
120 # fake is only for relevant environment.
121 if encoding.encoding.lower() in problematic_encodings.split():
121 if encoding.encoding.lower() in problematic_encodings.split():
122 for f in funcs.split():
122 for f in funcs.split():
123 wrapname(f)
123 wrapname(f)
124 ui.debug(_("[win32mbcs] activated with encoding: %s\n")
124 ui.debug(_("[win32mbcs] activated with encoding: %s\n")
125 % encoding.encoding)
125 % encoding.encoding)
126
126
@@ -1,165 +1,165 b''
1 # zeroconf.py - zeroconf support for Mercurial
1 # zeroconf.py - zeroconf support for Mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 '''zeroconf support for mercurial repositories
8 '''zeroconf support for Mercurial repositories
9
9
10 Zeroconf enabled repositories will be announced in a network without
10 Zeroconf enabled repositories will be announced in a network without
11 the need to configure a server or a service. They can be discovered
11 the need to configure a server or a service. They can be discovered
12 without knowing their actual IP address.
12 without knowing their actual IP address.
13
13
14 To use the zeroconf extension add the following entry to your hgrc
14 To use the zeroconf extension add the following entry to your hgrc
15 file:
15 file:
16
16
17 [extensions]
17 [extensions]
18 hgext.zeroconf =
18 hgext.zeroconf =
19
19
20 To allow other people to discover your repository using run "hg serve"
20 To allow other people to discover your repository using run "hg serve"
21 in your repository.
21 in your repository.
22
22
23 $ cd test
23 $ cd test
24 $ hg serve
24 $ hg serve
25
25
26 You can discover zeroconf enabled repositories by running "hg paths".
26 You can discover zeroconf enabled repositories by running "hg paths".
27
27
28 $ hg paths
28 $ hg paths
29 zc-test = http://example.com:8000/test
29 zc-test = http://example.com:8000/test
30 '''
30 '''
31
31
32 import Zeroconf, socket, time, os
32 import Zeroconf, socket, time, os
33 from mercurial import ui
33 from mercurial import ui
34 from mercurial import extensions
34 from mercurial import extensions
35 from mercurial.hgweb import hgweb_mod
35 from mercurial.hgweb import hgweb_mod
36 from mercurial.hgweb import hgwebdir_mod
36 from mercurial.hgweb import hgwebdir_mod
37
37
38 # publish
38 # publish
39
39
40 server = None
40 server = None
41 localip = None
41 localip = None
42
42
43 def getip():
43 def getip():
44 # finds external-facing interface without sending any packets (Linux)
44 # finds external-facing interface without sending any packets (Linux)
45 try:
45 try:
46 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
46 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
47 s.connect(('1.0.0.1', 0))
47 s.connect(('1.0.0.1', 0))
48 ip = s.getsockname()[0]
48 ip = s.getsockname()[0]
49 return ip
49 return ip
50 except:
50 except:
51 pass
51 pass
52
52
53 # Generic method, sometimes gives useless results
53 # Generic method, sometimes gives useless results
54 try:
54 try:
55 dumbip = socket.gethostbyaddr(socket.gethostname())[2][0]
55 dumbip = socket.gethostbyaddr(socket.gethostname())[2][0]
56 if not dumbip.startswith('127.') and ':' not in dumbip:
56 if not dumbip.startswith('127.') and ':' not in dumbip:
57 return dumbip
57 return dumbip
58 except socket.gaierror:
58 except socket.gaierror:
59 dumbip = '127.0.0.1'
59 dumbip = '127.0.0.1'
60
60
61 # works elsewhere, but actually sends a packet
61 # works elsewhere, but actually sends a packet
62 try:
62 try:
63 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
63 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
64 s.connect(('1.0.0.1', 1))
64 s.connect(('1.0.0.1', 1))
65 ip = s.getsockname()[0]
65 ip = s.getsockname()[0]
66 return ip
66 return ip
67 except:
67 except:
68 pass
68 pass
69
69
70 return dumbip
70 return dumbip
71
71
72 def publish(name, desc, path, port):
72 def publish(name, desc, path, port):
73 global server, localip
73 global server, localip
74 if not server:
74 if not server:
75 ip = getip()
75 ip = getip()
76 if ip.startswith('127.'):
76 if ip.startswith('127.'):
77 # if we have no internet connection, this can happen.
77 # if we have no internet connection, this can happen.
78 return
78 return
79 localip = socket.inet_aton(ip)
79 localip = socket.inet_aton(ip)
80 server = Zeroconf.Zeroconf(ip)
80 server = Zeroconf.Zeroconf(ip)
81
81
82 hostname = socket.gethostname().split('.')[0]
82 hostname = socket.gethostname().split('.')[0]
83 host = hostname + ".local"
83 host = hostname + ".local"
84 name = "%s-%s" % (hostname, name)
84 name = "%s-%s" % (hostname, name)
85
85
86 # advertise to browsers
86 # advertise to browsers
87 svc = Zeroconf.ServiceInfo('_http._tcp.local.',
87 svc = Zeroconf.ServiceInfo('_http._tcp.local.',
88 name + '._http._tcp.local.',
88 name + '._http._tcp.local.',
89 server = host,
89 server = host,
90 port = port,
90 port = port,
91 properties = {'description': desc,
91 properties = {'description': desc,
92 'path': "/" + path},
92 'path': "/" + path},
93 address = localip, weight = 0, priority = 0)
93 address = localip, weight = 0, priority = 0)
94 server.registerService(svc)
94 server.registerService(svc)
95
95
96 # advertise to Mercurial clients
96 # advertise to Mercurial clients
97 svc = Zeroconf.ServiceInfo('_hg._tcp.local.',
97 svc = Zeroconf.ServiceInfo('_hg._tcp.local.',
98 name + '._hg._tcp.local.',
98 name + '._hg._tcp.local.',
99 server = host,
99 server = host,
100 port = port,
100 port = port,
101 properties = {'description': desc,
101 properties = {'description': desc,
102 'path': "/" + path},
102 'path': "/" + path},
103 address = localip, weight = 0, priority = 0)
103 address = localip, weight = 0, priority = 0)
104 server.registerService(svc)
104 server.registerService(svc)
105
105
106 class hgwebzc(hgweb_mod.hgweb):
106 class hgwebzc(hgweb_mod.hgweb):
107 def __init__(self, repo, name=None):
107 def __init__(self, repo, name=None):
108 super(hgwebzc, self).__init__(repo, name)
108 super(hgwebzc, self).__init__(repo, name)
109 name = self.reponame or os.path.basename(repo.root)
109 name = self.reponame or os.path.basename(repo.root)
110 desc = self.repo.ui.config("web", "description", name)
110 desc = self.repo.ui.config("web", "description", name)
111 publish(name, desc, name, int(repo.ui.config("web", "port", 8000)))
111 publish(name, desc, name, int(repo.ui.config("web", "port", 8000)))
112
112
113 class hgwebdirzc(hgwebdir_mod.hgwebdir):
113 class hgwebdirzc(hgwebdir_mod.hgwebdir):
114 def run(self):
114 def run(self):
115 for r, p in self.repos:
115 for r, p in self.repos:
116 u = self.ui.copy()
116 u = self.ui.copy()
117 u.readconfig(os.path.join(p, '.hg', 'hgrc'))
117 u.readconfig(os.path.join(p, '.hg', 'hgrc'))
118 n = os.path.basename(r)
118 n = os.path.basename(r)
119 publish(n, "hgweb", p, int(u.config("web", "port", 8000)))
119 publish(n, "hgweb", p, int(u.config("web", "port", 8000)))
120 return super(hgwebdirzc, self).run()
120 return super(hgwebdirzc, self).run()
121
121
122 # listen
122 # listen
123
123
124 class listener(object):
124 class listener(object):
125 def __init__(self):
125 def __init__(self):
126 self.found = {}
126 self.found = {}
127 def removeService(self, server, type, name):
127 def removeService(self, server, type, name):
128 if repr(name) in self.found:
128 if repr(name) in self.found:
129 del self.found[repr(name)]
129 del self.found[repr(name)]
130 def addService(self, server, type, name):
130 def addService(self, server, type, name):
131 self.found[repr(name)] = server.getServiceInfo(type, name)
131 self.found[repr(name)] = server.getServiceInfo(type, name)
132
132
133 def getzcpaths():
133 def getzcpaths():
134 ip = getip()
134 ip = getip()
135 if ip.startswith('127.'):
135 if ip.startswith('127.'):
136 return
136 return
137 server = Zeroconf.Zeroconf(ip)
137 server = Zeroconf.Zeroconf(ip)
138 l = listener()
138 l = listener()
139 Zeroconf.ServiceBrowser(server, "_hg._tcp.local.", l)
139 Zeroconf.ServiceBrowser(server, "_hg._tcp.local.", l)
140 time.sleep(1)
140 time.sleep(1)
141 server.close()
141 server.close()
142 for v in l.found.values():
142 for v in l.found.values():
143 n = v.name[:v.name.index('.')]
143 n = v.name[:v.name.index('.')]
144 n.replace(" ", "-")
144 n.replace(" ", "-")
145 u = "http://%s:%s%s" % (socket.inet_ntoa(v.address), v.port,
145 u = "http://%s:%s%s" % (socket.inet_ntoa(v.address), v.port,
146 v.properties.get("path", "/"))
146 v.properties.get("path", "/"))
147 yield "zc-" + n, u
147 yield "zc-" + n, u
148
148
149 def config(orig, self, section, key, default=None, untrusted=False):
149 def config(orig, self, section, key, default=None, untrusted=False):
150 if section == "paths" and key.startswith("zc-"):
150 if section == "paths" and key.startswith("zc-"):
151 for n, p in getzcpaths():
151 for n, p in getzcpaths():
152 if n == key:
152 if n == key:
153 return p
153 return p
154 return orig(self, section, key, default, untrusted)
154 return orig(self, section, key, default, untrusted)
155
155
156 def configitems(orig, self, section, untrusted=False):
156 def configitems(orig, self, section, untrusted=False):
157 r = orig(self, section, untrusted)
157 r = orig(self, section, untrusted)
158 if section == "paths":
158 if section == "paths":
159 r += getzcpaths()
159 r += getzcpaths()
160 return r
160 return r
161
161
162 extensions.wrapfunction(ui.ui, 'config', config)
162 extensions.wrapfunction(ui.ui, 'config', config)
163 extensions.wrapfunction(ui.ui, 'configitems', configitems)
163 extensions.wrapfunction(ui.ui, 'configitems', configitems)
164 hgweb_mod.hgweb = hgwebzc
164 hgweb_mod.hgweb = hgwebzc
165 hgwebdir_mod.hgwebdir = hgwebdirzc
165 hgwebdir_mod.hgwebdir = hgwebdirzc
@@ -1,3467 +1,3467 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, sys, textwrap, subprocess, difflib, time
11 import os, re, sys, textwrap, subprocess, difflib, time
12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
13 import patch, help, mdiff, tempfile, url, encoding
13 import patch, help, mdiff, tempfile, url, encoding
14 import archival, changegroup, cmdutil, sshserver, hbisect
14 import archival, changegroup, cmdutil, sshserver, hbisect
15 from hgweb import server
15 from hgweb import server
16 import merge as merge_
16 import merge as merge_
17
17
18 # Commands start here, listed alphabetically
18 # Commands start here, listed alphabetically
19
19
20 def add(ui, repo, *pats, **opts):
20 def add(ui, repo, *pats, **opts):
21 """add the specified files on the next commit
21 """add the specified files on the next commit
22
22
23 Schedule files to be version controlled and added to the
23 Schedule files to be version controlled and added to the
24 repository.
24 repository.
25
25
26 The files will be added to the repository at the next commit. To
26 The files will be added to the repository at the next commit. To
27 undo an add before that, see hg revert.
27 undo an add before that, see hg revert.
28
28
29 If no names are given, add all files to the repository.
29 If no names are given, add all files to the repository.
30 """
30 """
31
31
32 bad = []
32 bad = []
33 exacts = {}
33 exacts = {}
34 names = []
34 names = []
35 m = cmdutil.match(repo, pats, opts)
35 m = cmdutil.match(repo, pats, opts)
36 oldbad = m.bad
36 oldbad = m.bad
37 m.bad = lambda x,y: bad.append(x) or oldbad(x,y)
37 m.bad = lambda x,y: bad.append(x) or oldbad(x,y)
38
38
39 for f in repo.walk(m):
39 for f in repo.walk(m):
40 exact = m.exact(f)
40 exact = m.exact(f)
41 if exact or f not in repo.dirstate:
41 if exact or f not in repo.dirstate:
42 names.append(f)
42 names.append(f)
43 if ui.verbose or not exact:
43 if ui.verbose or not exact:
44 ui.status(_('adding %s\n') % m.rel(f))
44 ui.status(_('adding %s\n') % m.rel(f))
45 if not opts.get('dry_run'):
45 if not opts.get('dry_run'):
46 bad += [f for f in repo.add(names) if f in m.files()]
46 bad += [f for f in repo.add(names) if f in m.files()]
47 return bad and 1 or 0
47 return bad and 1 or 0
48
48
49 def addremove(ui, repo, *pats, **opts):
49 def addremove(ui, repo, *pats, **opts):
50 """add all new files, delete all missing files
50 """add all new files, delete all missing files
51
51
52 Add all new files and remove all missing files from the
52 Add all new files and remove all missing files from the
53 repository.
53 repository.
54
54
55 New files are ignored if they match any of the patterns in
55 New files are ignored if they match any of the patterns in
56 .hgignore. As with add, these changes take effect at the next
56 .hgignore. As with add, these changes take effect at the next
57 commit.
57 commit.
58
58
59 Use the -s/--similarity option to detect renamed files. With a
59 Use the -s/--similarity option to detect renamed files. With a
60 parameter > 0, this compares every removed file with every added
60 parameter > 0, this compares every removed file with every added
61 file and records those similar enough as renames. This option
61 file and records those similar enough as renames. This option
62 takes a percentage between 0 (disabled) and 100 (files must be
62 takes a percentage between 0 (disabled) and 100 (files must be
63 identical) as its parameter. Detecting renamed files this way can
63 identical) as its parameter. Detecting renamed files this way can
64 be expensive.
64 be expensive.
65 """
65 """
66 try:
66 try:
67 sim = float(opts.get('similarity') or 0)
67 sim = float(opts.get('similarity') or 0)
68 except ValueError:
68 except ValueError:
69 raise util.Abort(_('similarity must be a number'))
69 raise util.Abort(_('similarity must be a number'))
70 if sim < 0 or sim > 100:
70 if sim < 0 or sim > 100:
71 raise util.Abort(_('similarity must be between 0 and 100'))
71 raise util.Abort(_('similarity must be between 0 and 100'))
72 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
72 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
73
73
74 def annotate(ui, repo, *pats, **opts):
74 def annotate(ui, repo, *pats, **opts):
75 """show changeset information per file line
75 """show changeset information per file line
76
76
77 List changes in files, showing the revision id responsible for
77 List changes in files, showing the revision id responsible for
78 each line
78 each line
79
79
80 This command is useful to discover who did a change or when a
80 This command is useful to discover who did a change or when a
81 change took place.
81 change took place.
82
82
83 Without the -a/--text option, annotate will avoid processing files
83 Without the -a/--text option, annotate will avoid processing files
84 it detects as binary. With -a, annotate will generate an
84 it detects as binary. With -a, annotate will generate an
85 annotation anyway, probably with undesirable results.
85 annotation anyway, probably with undesirable results.
86 """
86 """
87 datefunc = ui.quiet and util.shortdate or util.datestr
87 datefunc = ui.quiet and util.shortdate or util.datestr
88 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
88 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
89
89
90 if not pats:
90 if not pats:
91 raise util.Abort(_('at least one file name or pattern required'))
91 raise util.Abort(_('at least one file name or pattern required'))
92
92
93 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
93 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
94 ('number', lambda x: str(x[0].rev())),
94 ('number', lambda x: str(x[0].rev())),
95 ('changeset', lambda x: short(x[0].node())),
95 ('changeset', lambda x: short(x[0].node())),
96 ('date', getdate),
96 ('date', getdate),
97 ('follow', lambda x: x[0].path()),
97 ('follow', lambda x: x[0].path()),
98 ]
98 ]
99
99
100 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
100 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
101 and not opts.get('follow')):
101 and not opts.get('follow')):
102 opts['number'] = 1
102 opts['number'] = 1
103
103
104 linenumber = opts.get('line_number') is not None
104 linenumber = opts.get('line_number') is not None
105 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
105 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
106 raise util.Abort(_('at least one of -n/-c is required for -l'))
106 raise util.Abort(_('at least one of -n/-c is required for -l'))
107
107
108 funcmap = [func for op, func in opmap if opts.get(op)]
108 funcmap = [func for op, func in opmap if opts.get(op)]
109 if linenumber:
109 if linenumber:
110 lastfunc = funcmap[-1]
110 lastfunc = funcmap[-1]
111 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
111 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
112
112
113 ctx = repo[opts.get('rev')]
113 ctx = repo[opts.get('rev')]
114
114
115 m = cmdutil.match(repo, pats, opts)
115 m = cmdutil.match(repo, pats, opts)
116 for abs in ctx.walk(m):
116 for abs in ctx.walk(m):
117 fctx = ctx[abs]
117 fctx = ctx[abs]
118 if not opts.get('text') and util.binary(fctx.data()):
118 if not opts.get('text') and util.binary(fctx.data()):
119 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
119 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
120 continue
120 continue
121
121
122 lines = fctx.annotate(follow=opts.get('follow'),
122 lines = fctx.annotate(follow=opts.get('follow'),
123 linenumber=linenumber)
123 linenumber=linenumber)
124 pieces = []
124 pieces = []
125
125
126 for f in funcmap:
126 for f in funcmap:
127 l = [f(n) for n, dummy in lines]
127 l = [f(n) for n, dummy in lines]
128 if l:
128 if l:
129 ml = max(map(len, l))
129 ml = max(map(len, l))
130 pieces.append(["%*s" % (ml, x) for x in l])
130 pieces.append(["%*s" % (ml, x) for x in l])
131
131
132 if pieces:
132 if pieces:
133 for p, l in zip(zip(*pieces), lines):
133 for p, l in zip(zip(*pieces), lines):
134 ui.write("%s: %s" % (" ".join(p), l[1]))
134 ui.write("%s: %s" % (" ".join(p), l[1]))
135
135
136 def archive(ui, repo, dest, **opts):
136 def archive(ui, repo, dest, **opts):
137 '''create unversioned archive of a repository revision
137 '''create unversioned archive of a repository revision
138
138
139 By default, the revision used is the parent of the working
139 By default, the revision used is the parent of the working
140 directory; use -r/--rev to specify a different revision.
140 directory; use -r/--rev to specify a different revision.
141
141
142 To specify the type of archive to create, use -t/--type. Valid
142 To specify the type of archive to create, use -t/--type. Valid
143 types are:
143 types are:
144
144
145 "files" (default): a directory full of files
145 "files" (default): a directory full of files
146 "tar": tar archive, uncompressed
146 "tar": tar archive, uncompressed
147 "tbz2": tar archive, compressed using bzip2
147 "tbz2": tar archive, compressed using bzip2
148 "tgz": tar archive, compressed using gzip
148 "tgz": tar archive, compressed using gzip
149 "uzip": zip archive, uncompressed
149 "uzip": zip archive, uncompressed
150 "zip": zip archive, compressed using deflate
150 "zip": zip archive, compressed using deflate
151
151
152 The exact name of the destination archive or directory is given
152 The exact name of the destination archive or directory is given
153 using a format string; see 'hg help export' for details.
153 using a format string; see 'hg help export' for details.
154
154
155 Each member added to an archive file has a directory prefix
155 Each member added to an archive file has a directory prefix
156 prepended. Use -p/--prefix to specify a format string for the
156 prepended. Use -p/--prefix to specify a format string for the
157 prefix. The default is the basename of the archive, with suffixes
157 prefix. The default is the basename of the archive, with suffixes
158 removed.
158 removed.
159 '''
159 '''
160
160
161 ctx = repo[opts.get('rev')]
161 ctx = repo[opts.get('rev')]
162 if not ctx:
162 if not ctx:
163 raise util.Abort(_('no working directory: please specify a revision'))
163 raise util.Abort(_('no working directory: please specify a revision'))
164 node = ctx.node()
164 node = ctx.node()
165 dest = cmdutil.make_filename(repo, dest, node)
165 dest = cmdutil.make_filename(repo, dest, node)
166 if os.path.realpath(dest) == repo.root:
166 if os.path.realpath(dest) == repo.root:
167 raise util.Abort(_('repository root cannot be destination'))
167 raise util.Abort(_('repository root cannot be destination'))
168 matchfn = cmdutil.match(repo, [], opts)
168 matchfn = cmdutil.match(repo, [], opts)
169 kind = opts.get('type') or 'files'
169 kind = opts.get('type') or 'files'
170 prefix = opts.get('prefix')
170 prefix = opts.get('prefix')
171 if dest == '-':
171 if dest == '-':
172 if kind == 'files':
172 if kind == 'files':
173 raise util.Abort(_('cannot archive plain files to stdout'))
173 raise util.Abort(_('cannot archive plain files to stdout'))
174 dest = sys.stdout
174 dest = sys.stdout
175 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
175 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
176 prefix = cmdutil.make_filename(repo, prefix, node)
176 prefix = cmdutil.make_filename(repo, prefix, node)
177 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
177 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
178 matchfn, prefix)
178 matchfn, prefix)
179
179
180 def backout(ui, repo, node=None, rev=None, **opts):
180 def backout(ui, repo, node=None, rev=None, **opts):
181 '''reverse effect of earlier changeset
181 '''reverse effect of earlier changeset
182
182
183 Commit the backed out changes as a new changeset. The new
183 Commit the backed out changes as a new changeset. The new
184 changeset is a child of the backed out changeset.
184 changeset is a child of the backed out changeset.
185
185
186 If you back out a changeset other than the tip, a new head is
186 If you back out a changeset other than the tip, a new head is
187 created. This head will be the new tip and you should merge this
187 created. This head will be the new tip and you should merge this
188 backout changeset with another head (current one by default).
188 backout changeset with another head (current one by default).
189
189
190 The --merge option remembers the parent of the working directory
190 The --merge option remembers the parent of the working directory
191 before starting the backout, then merges the new head with that
191 before starting the backout, then merges the new head with that
192 changeset afterwards. This saves you from doing the merge by hand.
192 changeset afterwards. This saves you from doing the merge by hand.
193 The result of this merge is not committed, as with a normal merge.
193 The result of this merge is not committed, as with a normal merge.
194
194
195 See 'hg help dates' for a list of formats valid for -d/--date.
195 See 'hg help dates' for a list of formats valid for -d/--date.
196 '''
196 '''
197 if rev and node:
197 if rev and node:
198 raise util.Abort(_("please specify just one revision"))
198 raise util.Abort(_("please specify just one revision"))
199
199
200 if not rev:
200 if not rev:
201 rev = node
201 rev = node
202
202
203 if not rev:
203 if not rev:
204 raise util.Abort(_("please specify a revision to backout"))
204 raise util.Abort(_("please specify a revision to backout"))
205
205
206 date = opts.get('date')
206 date = opts.get('date')
207 if date:
207 if date:
208 opts['date'] = util.parsedate(date)
208 opts['date'] = util.parsedate(date)
209
209
210 cmdutil.bail_if_changed(repo)
210 cmdutil.bail_if_changed(repo)
211 node = repo.lookup(rev)
211 node = repo.lookup(rev)
212
212
213 op1, op2 = repo.dirstate.parents()
213 op1, op2 = repo.dirstate.parents()
214 a = repo.changelog.ancestor(op1, node)
214 a = repo.changelog.ancestor(op1, node)
215 if a != node:
215 if a != node:
216 raise util.Abort(_('cannot back out change on a different branch'))
216 raise util.Abort(_('cannot back out change on a different branch'))
217
217
218 p1, p2 = repo.changelog.parents(node)
218 p1, p2 = repo.changelog.parents(node)
219 if p1 == nullid:
219 if p1 == nullid:
220 raise util.Abort(_('cannot back out a change with no parents'))
220 raise util.Abort(_('cannot back out a change with no parents'))
221 if p2 != nullid:
221 if p2 != nullid:
222 if not opts.get('parent'):
222 if not opts.get('parent'):
223 raise util.Abort(_('cannot back out a merge changeset without '
223 raise util.Abort(_('cannot back out a merge changeset without '
224 '--parent'))
224 '--parent'))
225 p = repo.lookup(opts['parent'])
225 p = repo.lookup(opts['parent'])
226 if p not in (p1, p2):
226 if p not in (p1, p2):
227 raise util.Abort(_('%s is not a parent of %s') %
227 raise util.Abort(_('%s is not a parent of %s') %
228 (short(p), short(node)))
228 (short(p), short(node)))
229 parent = p
229 parent = p
230 else:
230 else:
231 if opts.get('parent'):
231 if opts.get('parent'):
232 raise util.Abort(_('cannot use --parent on non-merge changeset'))
232 raise util.Abort(_('cannot use --parent on non-merge changeset'))
233 parent = p1
233 parent = p1
234
234
235 # the backout should appear on the same branch
235 # the backout should appear on the same branch
236 branch = repo.dirstate.branch()
236 branch = repo.dirstate.branch()
237 hg.clean(repo, node, show_stats=False)
237 hg.clean(repo, node, show_stats=False)
238 repo.dirstate.setbranch(branch)
238 repo.dirstate.setbranch(branch)
239 revert_opts = opts.copy()
239 revert_opts = opts.copy()
240 revert_opts['date'] = None
240 revert_opts['date'] = None
241 revert_opts['all'] = True
241 revert_opts['all'] = True
242 revert_opts['rev'] = hex(parent)
242 revert_opts['rev'] = hex(parent)
243 revert_opts['no_backup'] = None
243 revert_opts['no_backup'] = None
244 revert(ui, repo, **revert_opts)
244 revert(ui, repo, **revert_opts)
245 commit_opts = opts.copy()
245 commit_opts = opts.copy()
246 commit_opts['addremove'] = False
246 commit_opts['addremove'] = False
247 if not commit_opts['message'] and not commit_opts['logfile']:
247 if not commit_opts['message'] and not commit_opts['logfile']:
248 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
248 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
249 commit_opts['force_editor'] = True
249 commit_opts['force_editor'] = True
250 commit(ui, repo, **commit_opts)
250 commit(ui, repo, **commit_opts)
251 def nice(node):
251 def nice(node):
252 return '%d:%s' % (repo.changelog.rev(node), short(node))
252 return '%d:%s' % (repo.changelog.rev(node), short(node))
253 ui.status(_('changeset %s backs out changeset %s\n') %
253 ui.status(_('changeset %s backs out changeset %s\n') %
254 (nice(repo.changelog.tip()), nice(node)))
254 (nice(repo.changelog.tip()), nice(node)))
255 if op1 != node:
255 if op1 != node:
256 hg.clean(repo, op1, show_stats=False)
256 hg.clean(repo, op1, show_stats=False)
257 if opts.get('merge'):
257 if opts.get('merge'):
258 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
258 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
259 hg.merge(repo, hex(repo.changelog.tip()))
259 hg.merge(repo, hex(repo.changelog.tip()))
260 else:
260 else:
261 ui.status(_('the backout changeset is a new head - '
261 ui.status(_('the backout changeset is a new head - '
262 'do not forget to merge\n'))
262 'do not forget to merge\n'))
263 ui.status(_('(use "backout --merge" '
263 ui.status(_('(use "backout --merge" '
264 'if you want to auto-merge)\n'))
264 'if you want to auto-merge)\n'))
265
265
266 def bisect(ui, repo, rev=None, extra=None, command=None,
266 def bisect(ui, repo, rev=None, extra=None, command=None,
267 reset=None, good=None, bad=None, skip=None, noupdate=None):
267 reset=None, good=None, bad=None, skip=None, noupdate=None):
268 """subdivision search of changesets
268 """subdivision search of changesets
269
269
270 This command helps to find changesets which introduce problems. To
270 This command helps to find changesets which introduce problems. To
271 use, mark the earliest changeset you know exhibits the problem as
271 use, mark the earliest changeset you know exhibits the problem as
272 bad, then mark the latest changeset which is free from the problem
272 bad, then mark the latest changeset which is free from the problem
273 as good. Bisect will update your working directory to a revision
273 as good. Bisect will update your working directory to a revision
274 for testing (unless the -U/--noupdate option is specified). Once
274 for testing (unless the -U/--noupdate option is specified). Once
275 you have performed tests, mark the working directory as bad or
275 you have performed tests, mark the working directory as bad or
276 good and bisect will either update to another candidate changeset
276 good and bisect will either update to another candidate changeset
277 or announce that it has found the bad revision.
277 or announce that it has found the bad revision.
278
278
279 As a shortcut, you can also use the revision argument to mark a
279 As a shortcut, you can also use the revision argument to mark a
280 revision as good or bad without checking it out first.
280 revision as good or bad without checking it out first.
281
281
282 If you supply a command it will be used for automatic bisection.
282 If you supply a command it will be used for automatic bisection.
283 Its exit status will be used as flag to mark revision as bad or
283 Its exit status will be used as flag to mark revision as bad or
284 good. In case exit status is 0 the revision is marked as good, 125
284 good. In case exit status is 0 the revision is marked as good, 125
285 - skipped, 127 (command not found) - bisection will be aborted;
285 - skipped, 127 (command not found) - bisection will be aborted;
286 any other status bigger than 0 will mark revision as bad.
286 any other status bigger than 0 will mark revision as bad.
287 """
287 """
288 def print_result(nodes, good):
288 def print_result(nodes, good):
289 displayer = cmdutil.show_changeset(ui, repo, {})
289 displayer = cmdutil.show_changeset(ui, repo, {})
290 if len(nodes) == 1:
290 if len(nodes) == 1:
291 # narrowed it down to a single revision
291 # narrowed it down to a single revision
292 if good:
292 if good:
293 ui.write(_("The first good revision is:\n"))
293 ui.write(_("The first good revision is:\n"))
294 else:
294 else:
295 ui.write(_("The first bad revision is:\n"))
295 ui.write(_("The first bad revision is:\n"))
296 displayer.show(repo[nodes[0]])
296 displayer.show(repo[nodes[0]])
297 else:
297 else:
298 # multiple possible revisions
298 # multiple possible revisions
299 if good:
299 if good:
300 ui.write(_("Due to skipped revisions, the first "
300 ui.write(_("Due to skipped revisions, the first "
301 "good revision could be any of:\n"))
301 "good revision could be any of:\n"))
302 else:
302 else:
303 ui.write(_("Due to skipped revisions, the first "
303 ui.write(_("Due to skipped revisions, the first "
304 "bad revision could be any of:\n"))
304 "bad revision could be any of:\n"))
305 for n in nodes:
305 for n in nodes:
306 displayer.show(repo[n])
306 displayer.show(repo[n])
307
307
308 def check_state(state, interactive=True):
308 def check_state(state, interactive=True):
309 if not state['good'] or not state['bad']:
309 if not state['good'] or not state['bad']:
310 if (good or bad or skip or reset) and interactive:
310 if (good or bad or skip or reset) and interactive:
311 return
311 return
312 if not state['good']:
312 if not state['good']:
313 raise util.Abort(_('cannot bisect (no known good revisions)'))
313 raise util.Abort(_('cannot bisect (no known good revisions)'))
314 else:
314 else:
315 raise util.Abort(_('cannot bisect (no known bad revisions)'))
315 raise util.Abort(_('cannot bisect (no known bad revisions)'))
316 return True
316 return True
317
317
318 # backward compatibility
318 # backward compatibility
319 if rev in "good bad reset init".split():
319 if rev in "good bad reset init".split():
320 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
320 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
321 cmd, rev, extra = rev, extra, None
321 cmd, rev, extra = rev, extra, None
322 if cmd == "good":
322 if cmd == "good":
323 good = True
323 good = True
324 elif cmd == "bad":
324 elif cmd == "bad":
325 bad = True
325 bad = True
326 else:
326 else:
327 reset = True
327 reset = True
328 elif extra or good + bad + skip + reset + bool(command) > 1:
328 elif extra or good + bad + skip + reset + bool(command) > 1:
329 raise util.Abort(_('incompatible arguments'))
329 raise util.Abort(_('incompatible arguments'))
330
330
331 if reset:
331 if reset:
332 p = repo.join("bisect.state")
332 p = repo.join("bisect.state")
333 if os.path.exists(p):
333 if os.path.exists(p):
334 os.unlink(p)
334 os.unlink(p)
335 return
335 return
336
336
337 state = hbisect.load_state(repo)
337 state = hbisect.load_state(repo)
338
338
339 if command:
339 if command:
340 commandpath = util.find_exe(command)
340 commandpath = util.find_exe(command)
341 changesets = 1
341 changesets = 1
342 try:
342 try:
343 while changesets:
343 while changesets:
344 # update state
344 # update state
345 status = subprocess.call([commandpath])
345 status = subprocess.call([commandpath])
346 if status == 125:
346 if status == 125:
347 transition = "skip"
347 transition = "skip"
348 elif status == 0:
348 elif status == 0:
349 transition = "good"
349 transition = "good"
350 # status < 0 means process was killed
350 # status < 0 means process was killed
351 elif status == 127:
351 elif status == 127:
352 raise util.Abort(_("failed to execute %s") % command)
352 raise util.Abort(_("failed to execute %s") % command)
353 elif status < 0:
353 elif status < 0:
354 raise util.Abort(_("%s killed") % command)
354 raise util.Abort(_("%s killed") % command)
355 else:
355 else:
356 transition = "bad"
356 transition = "bad"
357 node = repo.lookup(rev or '.')
357 node = repo.lookup(rev or '.')
358 state[transition].append(node)
358 state[transition].append(node)
359 ui.note(_('Changeset %s: %s\n') % (short(node), transition))
359 ui.note(_('Changeset %s: %s\n') % (short(node), transition))
360 check_state(state, interactive=False)
360 check_state(state, interactive=False)
361 # bisect
361 # bisect
362 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
362 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
363 # update to next check
363 # update to next check
364 cmdutil.bail_if_changed(repo)
364 cmdutil.bail_if_changed(repo)
365 hg.clean(repo, nodes[0], show_stats=False)
365 hg.clean(repo, nodes[0], show_stats=False)
366 finally:
366 finally:
367 hbisect.save_state(repo, state)
367 hbisect.save_state(repo, state)
368 return print_result(nodes, not status)
368 return print_result(nodes, not status)
369
369
370 # update state
370 # update state
371 node = repo.lookup(rev or '.')
371 node = repo.lookup(rev or '.')
372 if good:
372 if good:
373 state['good'].append(node)
373 state['good'].append(node)
374 elif bad:
374 elif bad:
375 state['bad'].append(node)
375 state['bad'].append(node)
376 elif skip:
376 elif skip:
377 state['skip'].append(node)
377 state['skip'].append(node)
378
378
379 hbisect.save_state(repo, state)
379 hbisect.save_state(repo, state)
380
380
381 if not check_state(state):
381 if not check_state(state):
382 return
382 return
383
383
384 # actually bisect
384 # actually bisect
385 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
385 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
386 if changesets == 0:
386 if changesets == 0:
387 print_result(nodes, good)
387 print_result(nodes, good)
388 else:
388 else:
389 assert len(nodes) == 1 # only a single node can be tested next
389 assert len(nodes) == 1 # only a single node can be tested next
390 node = nodes[0]
390 node = nodes[0]
391 # compute the approximate number of remaining tests
391 # compute the approximate number of remaining tests
392 tests, size = 0, 2
392 tests, size = 0, 2
393 while size <= changesets:
393 while size <= changesets:
394 tests, size = tests + 1, size * 2
394 tests, size = tests + 1, size * 2
395 rev = repo.changelog.rev(node)
395 rev = repo.changelog.rev(node)
396 ui.write(_("Testing changeset %s:%s "
396 ui.write(_("Testing changeset %s:%s "
397 "(%s changesets remaining, ~%s tests)\n")
397 "(%s changesets remaining, ~%s tests)\n")
398 % (rev, short(node), changesets, tests))
398 % (rev, short(node), changesets, tests))
399 if not noupdate:
399 if not noupdate:
400 cmdutil.bail_if_changed(repo)
400 cmdutil.bail_if_changed(repo)
401 return hg.clean(repo, node)
401 return hg.clean(repo, node)
402
402
403 def branch(ui, repo, label=None, **opts):
403 def branch(ui, repo, label=None, **opts):
404 """set or show the current branch name
404 """set or show the current branch name
405
405
406 With no argument, show the current branch name. With one argument,
406 With no argument, show the current branch name. With one argument,
407 set the working directory branch name (the branch does not exist
407 set the working directory branch name (the branch does not exist
408 in the repository until the next commit). It is recommended to use
408 in the repository until the next commit). It is recommended to use
409 the 'default' branch as your primary development branch.
409 the 'default' branch as your primary development branch.
410
410
411 Unless -f/--force is specified, branch will not let you set a
411 Unless -f/--force is specified, branch will not let you set a
412 branch name that shadows an existing branch.
412 branch name that shadows an existing branch.
413
413
414 Use -C/--clean to reset the working directory branch to that of
414 Use -C/--clean to reset the working directory branch to that of
415 the parent of the working directory, negating a previous branch
415 the parent of the working directory, negating a previous branch
416 change.
416 change.
417
417
418 Use the command 'hg update' to switch to an existing branch.
418 Use the command 'hg update' to switch to an existing branch.
419 """
419 """
420
420
421 if opts.get('clean'):
421 if opts.get('clean'):
422 label = repo[None].parents()[0].branch()
422 label = repo[None].parents()[0].branch()
423 repo.dirstate.setbranch(label)
423 repo.dirstate.setbranch(label)
424 ui.status(_('reset working directory to branch %s\n') % label)
424 ui.status(_('reset working directory to branch %s\n') % label)
425 elif label:
425 elif label:
426 if not opts.get('force') and label in repo.branchtags():
426 if not opts.get('force') and label in repo.branchtags():
427 if label not in [p.branch() for p in repo.parents()]:
427 if label not in [p.branch() for p in repo.parents()]:
428 raise util.Abort(_('a branch of the same name already exists'
428 raise util.Abort(_('a branch of the same name already exists'
429 ' (use --force to override)'))
429 ' (use --force to override)'))
430 repo.dirstate.setbranch(encoding.fromlocal(label))
430 repo.dirstate.setbranch(encoding.fromlocal(label))
431 ui.status(_('marked working directory as branch %s\n') % label)
431 ui.status(_('marked working directory as branch %s\n') % label)
432 else:
432 else:
433 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
433 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
434
434
435 def branches(ui, repo, active=False):
435 def branches(ui, repo, active=False):
436 """list repository named branches
436 """list repository named branches
437
437
438 List the repository's named branches, indicating which ones are
438 List the repository's named branches, indicating which ones are
439 inactive. If active is specified, only show active branches.
439 inactive. If active is specified, only show active branches.
440
440
441 A branch is considered active if it contains repository heads.
441 A branch is considered active if it contains repository heads.
442
442
443 Use the command 'hg update' to switch to an existing branch.
443 Use the command 'hg update' to switch to an existing branch.
444 """
444 """
445 hexfunc = ui.debugflag and hex or short
445 hexfunc = ui.debugflag and hex or short
446 activebranches = [encoding.tolocal(repo[n].branch())
446 activebranches = [encoding.tolocal(repo[n].branch())
447 for n in repo.heads(closed=False)]
447 for n in repo.heads(closed=False)]
448 branches = sorted([(tag in activebranches, repo.changelog.rev(node), tag)
448 branches = sorted([(tag in activebranches, repo.changelog.rev(node), tag)
449 for tag, node in repo.branchtags().items()],
449 for tag, node in repo.branchtags().items()],
450 reverse=True)
450 reverse=True)
451
451
452 for isactive, node, tag in branches:
452 for isactive, node, tag in branches:
453 if (not active) or isactive:
453 if (not active) or isactive:
454 if ui.quiet:
454 if ui.quiet:
455 ui.write("%s\n" % tag)
455 ui.write("%s\n" % tag)
456 else:
456 else:
457 hn = repo.lookup(node)
457 hn = repo.lookup(node)
458 if isactive:
458 if isactive:
459 notice = ''
459 notice = ''
460 elif hn not in repo.branchheads(tag, closed=False):
460 elif hn not in repo.branchheads(tag, closed=False):
461 notice = ' (closed)'
461 notice = ' (closed)'
462 else:
462 else:
463 notice = ' (inactive)'
463 notice = ' (inactive)'
464 rev = str(node).rjust(31 - encoding.colwidth(tag))
464 rev = str(node).rjust(31 - encoding.colwidth(tag))
465 data = tag, rev, hexfunc(hn), notice
465 data = tag, rev, hexfunc(hn), notice
466 ui.write("%s %s:%s%s\n" % data)
466 ui.write("%s %s:%s%s\n" % data)
467
467
468 def bundle(ui, repo, fname, dest=None, **opts):
468 def bundle(ui, repo, fname, dest=None, **opts):
469 """create a changegroup file
469 """create a changegroup file
470
470
471 Generate a compressed changegroup file collecting changesets not
471 Generate a compressed changegroup file collecting changesets not
472 known to be in another repository.
472 known to be in another repository.
473
473
474 If no destination repository is specified the destination is
474 If no destination repository is specified the destination is
475 assumed to have all the nodes specified by one or more --base
475 assumed to have all the nodes specified by one or more --base
476 parameters. To create a bundle containing all changesets, use
476 parameters. To create a bundle containing all changesets, use
477 -a/--all (or --base null). To change the compression method
477 -a/--all (or --base null). To change the compression method
478 applied, use the -t/--type option (by default, bundles are
478 applied, use the -t/--type option (by default, bundles are
479 compressed using bz2).
479 compressed using bz2).
480
480
481 The bundle file can then be transferred using conventional means
481 The bundle file can then be transferred using conventional means
482 and applied to another repository with the unbundle or pull
482 and applied to another repository with the unbundle or pull
483 command. This is useful when direct push and pull are not
483 command. This is useful when direct push and pull are not
484 available or when exporting an entire repository is undesirable.
484 available or when exporting an entire repository is undesirable.
485
485
486 Applying bundles preserves all changeset contents including
486 Applying bundles preserves all changeset contents including
487 permissions, copy/rename information, and revision history.
487 permissions, copy/rename information, and revision history.
488 """
488 """
489 revs = opts.get('rev') or None
489 revs = opts.get('rev') or None
490 if revs:
490 if revs:
491 revs = [repo.lookup(rev) for rev in revs]
491 revs = [repo.lookup(rev) for rev in revs]
492 if opts.get('all'):
492 if opts.get('all'):
493 base = ['null']
493 base = ['null']
494 else:
494 else:
495 base = opts.get('base')
495 base = opts.get('base')
496 if base:
496 if base:
497 if dest:
497 if dest:
498 raise util.Abort(_("--base is incompatible with specifying "
498 raise util.Abort(_("--base is incompatible with specifying "
499 "a destination"))
499 "a destination"))
500 base = [repo.lookup(rev) for rev in base]
500 base = [repo.lookup(rev) for rev in base]
501 # create the right base
501 # create the right base
502 # XXX: nodesbetween / changegroup* should be "fixed" instead
502 # XXX: nodesbetween / changegroup* should be "fixed" instead
503 o = []
503 o = []
504 has = set((nullid,))
504 has = set((nullid,))
505 for n in base:
505 for n in base:
506 has.update(repo.changelog.reachable(n))
506 has.update(repo.changelog.reachable(n))
507 if revs:
507 if revs:
508 visit = list(revs)
508 visit = list(revs)
509 else:
509 else:
510 visit = repo.changelog.heads()
510 visit = repo.changelog.heads()
511 seen = {}
511 seen = {}
512 while visit:
512 while visit:
513 n = visit.pop(0)
513 n = visit.pop(0)
514 parents = [p for p in repo.changelog.parents(n) if p not in has]
514 parents = [p for p in repo.changelog.parents(n) if p not in has]
515 if len(parents) == 0:
515 if len(parents) == 0:
516 o.insert(0, n)
516 o.insert(0, n)
517 else:
517 else:
518 for p in parents:
518 for p in parents:
519 if p not in seen:
519 if p not in seen:
520 seen[p] = 1
520 seen[p] = 1
521 visit.append(p)
521 visit.append(p)
522 else:
522 else:
523 dest, revs, checkout = hg.parseurl(
523 dest, revs, checkout = hg.parseurl(
524 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
524 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
525 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
525 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
526 o = repo.findoutgoing(other, force=opts.get('force'))
526 o = repo.findoutgoing(other, force=opts.get('force'))
527
527
528 if revs:
528 if revs:
529 cg = repo.changegroupsubset(o, revs, 'bundle')
529 cg = repo.changegroupsubset(o, revs, 'bundle')
530 else:
530 else:
531 cg = repo.changegroup(o, 'bundle')
531 cg = repo.changegroup(o, 'bundle')
532
532
533 bundletype = opts.get('type', 'bzip2').lower()
533 bundletype = opts.get('type', 'bzip2').lower()
534 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
534 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
535 bundletype = btypes.get(bundletype)
535 bundletype = btypes.get(bundletype)
536 if bundletype not in changegroup.bundletypes:
536 if bundletype not in changegroup.bundletypes:
537 raise util.Abort(_('unknown bundle type specified with --type'))
537 raise util.Abort(_('unknown bundle type specified with --type'))
538
538
539 changegroup.writebundle(cg, fname, bundletype)
539 changegroup.writebundle(cg, fname, bundletype)
540
540
541 def cat(ui, repo, file1, *pats, **opts):
541 def cat(ui, repo, file1, *pats, **opts):
542 """output the current or given revision of files
542 """output the current or given revision of files
543
543
544 Print the specified files as they were at the given revision. If
544 Print the specified files as they were at the given revision. If
545 no revision is given, the parent of the working directory is used,
545 no revision is given, the parent of the working directory is used,
546 or tip if no revision is checked out.
546 or tip if no revision is checked out.
547
547
548 Output may be to a file, in which case the name of the file is
548 Output may be to a file, in which case the name of the file is
549 given using a format string. The formatting rules are the same as
549 given using a format string. The formatting rules are the same as
550 for the export command, with the following additions:
550 for the export command, with the following additions:
551
551
552 %s basename of file being printed
552 %s basename of file being printed
553 %d dirname of file being printed, or '.' if in repository root
553 %d dirname of file being printed, or '.' if in repository root
554 %p root-relative path name of file being printed
554 %p root-relative path name of file being printed
555 """
555 """
556 ctx = repo[opts.get('rev')]
556 ctx = repo[opts.get('rev')]
557 err = 1
557 err = 1
558 m = cmdutil.match(repo, (file1,) + pats, opts)
558 m = cmdutil.match(repo, (file1,) + pats, opts)
559 for abs in ctx.walk(m):
559 for abs in ctx.walk(m):
560 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
560 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
561 data = ctx[abs].data()
561 data = ctx[abs].data()
562 if opts.get('decode'):
562 if opts.get('decode'):
563 data = repo.wwritedata(abs, data)
563 data = repo.wwritedata(abs, data)
564 fp.write(data)
564 fp.write(data)
565 err = 0
565 err = 0
566 return err
566 return err
567
567
568 def clone(ui, source, dest=None, **opts):
568 def clone(ui, source, dest=None, **opts):
569 """make a copy of an existing repository
569 """make a copy of an existing repository
570
570
571 Create a copy of an existing repository in a new directory.
571 Create a copy of an existing repository in a new directory.
572
572
573 If no destination directory name is specified, it defaults to the
573 If no destination directory name is specified, it defaults to the
574 basename of the source.
574 basename of the source.
575
575
576 The location of the source is added to the new repository's
576 The location of the source is added to the new repository's
577 .hg/hgrc file, as the default to be used for future pulls.
577 .hg/hgrc file, as the default to be used for future pulls.
578
578
579 If you use the -r/--rev option to clone up to a specific revision,
579 If you use the -r/--rev option to clone up to a specific revision,
580 no subsequent revisions (including subsequent tags) will be
580 no subsequent revisions (including subsequent tags) will be
581 present in the cloned repository. This option implies --pull, even
581 present in the cloned repository. This option implies --pull, even
582 on local repositories.
582 on local repositories.
583
583
584 By default, clone will check out the head of the 'default' branch.
584 By default, clone will check out the head of the 'default' branch.
585 If the -U/--noupdate option is used, the new clone will contain
585 If the -U/--noupdate option is used, the new clone will contain
586 only a repository (.hg) and no working copy (the working copy
586 only a repository (.hg) and no working copy (the working copy
587 parent is the null revision).
587 parent is the null revision).
588
588
589 See 'hg help urls' for valid source format details.
589 See 'hg help urls' for valid source format details.
590
590
591 It is possible to specify an ssh:// URL as the destination, but no
591 It is possible to specify an ssh:// URL as the destination, but no
592 .hg/hgrc and working directory will be created on the remote side.
592 .hg/hgrc and working directory will be created on the remote side.
593 Look at the help text for URLs for important details about ssh://
593 Look at the help text for URLs for important details about ssh://
594 URLs.
594 URLs.
595
595
596 For efficiency, hardlinks are used for cloning whenever the source
596 For efficiency, hardlinks are used for cloning whenever the source
597 and destination are on the same filesystem (note this applies only
597 and destination are on the same filesystem (note this applies only
598 to the repository data, not to the checked out files). Some
598 to the repository data, not to the checked out files). Some
599 filesystems, such as AFS, implement hardlinking incorrectly, but
599 filesystems, such as AFS, implement hardlinking incorrectly, but
600 do not report errors. In these cases, use the --pull option to
600 do not report errors. In these cases, use the --pull option to
601 avoid hardlinking.
601 avoid hardlinking.
602
602
603 In some cases, you can clone repositories and checked out files
603 In some cases, you can clone repositories and checked out files
604 using full hardlinks with
604 using full hardlinks with
605
605
606 $ cp -al REPO REPOCLONE
606 $ cp -al REPO REPOCLONE
607
607
608 This is the fastest way to clone, but it is not always safe. The
608 This is the fastest way to clone, but it is not always safe. The
609 operation is not atomic (making sure REPO is not modified during
609 operation is not atomic (making sure REPO is not modified during
610 the operation is up to you) and you have to make sure your editor
610 the operation is up to you) and you have to make sure your editor
611 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
611 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
612 this is not compatible with certain extensions that place their
612 this is not compatible with certain extensions that place their
613 metadata under the .hg directory, such as mq.
613 metadata under the .hg directory, such as mq.
614
614
615 """
615 """
616 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
616 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
617 pull=opts.get('pull'),
617 pull=opts.get('pull'),
618 stream=opts.get('uncompressed'),
618 stream=opts.get('uncompressed'),
619 rev=opts.get('rev'),
619 rev=opts.get('rev'),
620 update=not opts.get('noupdate'))
620 update=not opts.get('noupdate'))
621
621
622 def commit(ui, repo, *pats, **opts):
622 def commit(ui, repo, *pats, **opts):
623 """commit the specified files or all outstanding changes
623 """commit the specified files or all outstanding changes
624
624
625 Commit changes to the given files into the repository. Unlike a
625 Commit changes to the given files into the repository. Unlike a
626 centralized RCS, this operation is a local operation. See hg push
626 centralized RCS, this operation is a local operation. See hg push
627 for means to actively distribute your changes.
627 for means to actively distribute your changes.
628
628
629 If a list of files is omitted, all changes reported by "hg status"
629 If a list of files is omitted, all changes reported by "hg status"
630 will be committed.
630 will be committed.
631
631
632 If you are committing the result of a merge, do not provide any
632 If you are committing the result of a merge, do not provide any
633 file names or -I/-X filters.
633 file names or -I/-X filters.
634
634
635 If no commit message is specified, the configured editor is
635 If no commit message is specified, the configured editor is
636 started to prompt you for a message.
636 started to prompt you for a message.
637
637
638 See 'hg help dates' for a list of formats valid for -d/--date.
638 See 'hg help dates' for a list of formats valid for -d/--date.
639 """
639 """
640 extra = {}
640 extra = {}
641 if opts.get('close_branch'):
641 if opts.get('close_branch'):
642 extra['close'] = 1
642 extra['close'] = 1
643 e = cmdutil.commiteditor
643 e = cmdutil.commiteditor
644 if opts.get('force_editor'):
644 if opts.get('force_editor'):
645 e = cmdutil.commitforceeditor
645 e = cmdutil.commitforceeditor
646
646
647 def commitfunc(ui, repo, message, match, opts):
647 def commitfunc(ui, repo, message, match, opts):
648 return repo.commit(message, opts.get('user'), opts.get('date'), match,
648 return repo.commit(message, opts.get('user'), opts.get('date'), match,
649 editor=e, extra=extra)
649 editor=e, extra=extra)
650
650
651 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
651 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
652 if not node:
652 if not node:
653 return
653 return
654 cl = repo.changelog
654 cl = repo.changelog
655 rev = cl.rev(node)
655 rev = cl.rev(node)
656 parents = cl.parentrevs(rev)
656 parents = cl.parentrevs(rev)
657 if rev - 1 in parents:
657 if rev - 1 in parents:
658 # one of the parents was the old tip
658 # one of the parents was the old tip
659 pass
659 pass
660 elif (parents == (nullrev, nullrev) or
660 elif (parents == (nullrev, nullrev) or
661 len(cl.heads(cl.node(parents[0]))) > 1 and
661 len(cl.heads(cl.node(parents[0]))) > 1 and
662 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
662 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
663 ui.status(_('created new head\n'))
663 ui.status(_('created new head\n'))
664
664
665 if ui.debugflag:
665 if ui.debugflag:
666 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
666 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
667 elif ui.verbose:
667 elif ui.verbose:
668 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
668 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
669
669
670 def copy(ui, repo, *pats, **opts):
670 def copy(ui, repo, *pats, **opts):
671 """mark files as copied for the next commit
671 """mark files as copied for the next commit
672
672
673 Mark dest as having copies of source files. If dest is a
673 Mark dest as having copies of source files. If dest is a
674 directory, copies are put in that directory. If dest is a file,
674 directory, copies are put in that directory. If dest is a file,
675 the source must be a single file.
675 the source must be a single file.
676
676
677 By default, this command copies the contents of files as they
677 By default, this command copies the contents of files as they
678 stand in the working directory. If invoked with -A/--after, the
678 stand in the working directory. If invoked with -A/--after, the
679 operation is recorded, but no copying is performed.
679 operation is recorded, but no copying is performed.
680
680
681 This command takes effect with the next commit. To undo a copy
681 This command takes effect with the next commit. To undo a copy
682 before that, see hg revert.
682 before that, see hg revert.
683 """
683 """
684 wlock = repo.wlock(False)
684 wlock = repo.wlock(False)
685 try:
685 try:
686 return cmdutil.copy(ui, repo, pats, opts)
686 return cmdutil.copy(ui, repo, pats, opts)
687 finally:
687 finally:
688 wlock.release()
688 wlock.release()
689
689
690 def debugancestor(ui, repo, *args):
690 def debugancestor(ui, repo, *args):
691 """find the ancestor revision of two revisions in a given index"""
691 """find the ancestor revision of two revisions in a given index"""
692 if len(args) == 3:
692 if len(args) == 3:
693 index, rev1, rev2 = args
693 index, rev1, rev2 = args
694 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
694 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
695 lookup = r.lookup
695 lookup = r.lookup
696 elif len(args) == 2:
696 elif len(args) == 2:
697 if not repo:
697 if not repo:
698 raise util.Abort(_("There is no Mercurial repository here "
698 raise util.Abort(_("There is no Mercurial repository here "
699 "(.hg not found)"))
699 "(.hg not found)"))
700 rev1, rev2 = args
700 rev1, rev2 = args
701 r = repo.changelog
701 r = repo.changelog
702 lookup = repo.lookup
702 lookup = repo.lookup
703 else:
703 else:
704 raise util.Abort(_('either two or three arguments required'))
704 raise util.Abort(_('either two or three arguments required'))
705 a = r.ancestor(lookup(rev1), lookup(rev2))
705 a = r.ancestor(lookup(rev1), lookup(rev2))
706 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
706 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
707
707
708 def debugcommands(ui, cmd='', *args):
708 def debugcommands(ui, cmd='', *args):
709 for cmd, vals in sorted(table.iteritems()):
709 for cmd, vals in sorted(table.iteritems()):
710 cmd = cmd.split('|')[0].strip('^')
710 cmd = cmd.split('|')[0].strip('^')
711 opts = ', '.join([i[1] for i in vals[1]])
711 opts = ', '.join([i[1] for i in vals[1]])
712 ui.write('%s: %s\n' % (cmd, opts))
712 ui.write('%s: %s\n' % (cmd, opts))
713
713
714 def debugcomplete(ui, cmd='', **opts):
714 def debugcomplete(ui, cmd='', **opts):
715 """returns the completion list associated with the given command"""
715 """returns the completion list associated with the given command"""
716
716
717 if opts.get('options'):
717 if opts.get('options'):
718 options = []
718 options = []
719 otables = [globalopts]
719 otables = [globalopts]
720 if cmd:
720 if cmd:
721 aliases, entry = cmdutil.findcmd(cmd, table, False)
721 aliases, entry = cmdutil.findcmd(cmd, table, False)
722 otables.append(entry[1])
722 otables.append(entry[1])
723 for t in otables:
723 for t in otables:
724 for o in t:
724 for o in t:
725 if o[0]:
725 if o[0]:
726 options.append('-%s' % o[0])
726 options.append('-%s' % o[0])
727 options.append('--%s' % o[1])
727 options.append('--%s' % o[1])
728 ui.write("%s\n" % "\n".join(options))
728 ui.write("%s\n" % "\n".join(options))
729 return
729 return
730
730
731 cmdlist = cmdutil.findpossible(cmd, table)
731 cmdlist = cmdutil.findpossible(cmd, table)
732 if ui.verbose:
732 if ui.verbose:
733 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
733 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
734 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
734 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
735
735
736 def debugfsinfo(ui, path = "."):
736 def debugfsinfo(ui, path = "."):
737 file('.debugfsinfo', 'w').write('')
737 file('.debugfsinfo', 'w').write('')
738 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
738 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
739 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
739 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
740 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
740 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
741 and 'yes' or 'no'))
741 and 'yes' or 'no'))
742 os.unlink('.debugfsinfo')
742 os.unlink('.debugfsinfo')
743
743
744 def debugrebuildstate(ui, repo, rev="tip"):
744 def debugrebuildstate(ui, repo, rev="tip"):
745 """rebuild the dirstate as it would look like for the given revision"""
745 """rebuild the dirstate as it would look like for the given revision"""
746 ctx = repo[rev]
746 ctx = repo[rev]
747 wlock = repo.wlock()
747 wlock = repo.wlock()
748 try:
748 try:
749 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
749 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
750 finally:
750 finally:
751 wlock.release()
751 wlock.release()
752
752
753 def debugcheckstate(ui, repo):
753 def debugcheckstate(ui, repo):
754 """validate the correctness of the current dirstate"""
754 """validate the correctness of the current dirstate"""
755 parent1, parent2 = repo.dirstate.parents()
755 parent1, parent2 = repo.dirstate.parents()
756 m1 = repo[parent1].manifest()
756 m1 = repo[parent1].manifest()
757 m2 = repo[parent2].manifest()
757 m2 = repo[parent2].manifest()
758 errors = 0
758 errors = 0
759 for f in repo.dirstate:
759 for f in repo.dirstate:
760 state = repo.dirstate[f]
760 state = repo.dirstate[f]
761 if state in "nr" and f not in m1:
761 if state in "nr" and f not in m1:
762 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
762 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
763 errors += 1
763 errors += 1
764 if state in "a" and f in m1:
764 if state in "a" and f in m1:
765 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
765 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
766 errors += 1
766 errors += 1
767 if state in "m" and f not in m1 and f not in m2:
767 if state in "m" and f not in m1 and f not in m2:
768 ui.warn(_("%s in state %s, but not in either manifest\n") %
768 ui.warn(_("%s in state %s, but not in either manifest\n") %
769 (f, state))
769 (f, state))
770 errors += 1
770 errors += 1
771 for f in m1:
771 for f in m1:
772 state = repo.dirstate[f]
772 state = repo.dirstate[f]
773 if state not in "nrm":
773 if state not in "nrm":
774 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
774 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
775 errors += 1
775 errors += 1
776 if errors:
776 if errors:
777 error = _(".hg/dirstate inconsistent with current parent's manifest")
777 error = _(".hg/dirstate inconsistent with current parent's manifest")
778 raise util.Abort(error)
778 raise util.Abort(error)
779
779
780 def showconfig(ui, repo, *values, **opts):
780 def showconfig(ui, repo, *values, **opts):
781 """show combined config settings from all hgrc files
781 """show combined config settings from all hgrc files
782
782
783 With no arguments, print names and values of all config items.
783 With no arguments, print names and values of all config items.
784
784
785 With one argument of the form section.name, print just the value
785 With one argument of the form section.name, print just the value
786 of that config item.
786 of that config item.
787
787
788 With multiple arguments, print names and values of all config
788 With multiple arguments, print names and values of all config
789 items with matching section names.
789 items with matching section names.
790
790
791 With the --debug flag, the source (filename and line number) is
791 With the --debug flag, the source (filename and line number) is
792 printed for each config item.
792 printed for each config item.
793 """
793 """
794
794
795 untrusted = bool(opts.get('untrusted'))
795 untrusted = bool(opts.get('untrusted'))
796 if values:
796 if values:
797 if len([v for v in values if '.' in v]) > 1:
797 if len([v for v in values if '.' in v]) > 1:
798 raise util.Abort(_('only one config item permitted'))
798 raise util.Abort(_('only one config item permitted'))
799 for section, name, value in ui.walkconfig(untrusted=untrusted):
799 for section, name, value in ui.walkconfig(untrusted=untrusted):
800 sectname = section + '.' + name
800 sectname = section + '.' + name
801 if values:
801 if values:
802 for v in values:
802 for v in values:
803 if v == section:
803 if v == section:
804 ui.debug('%s: ' %
804 ui.debug('%s: ' %
805 ui.configsource(section, name, untrusted))
805 ui.configsource(section, name, untrusted))
806 ui.write('%s=%s\n' % (sectname, value))
806 ui.write('%s=%s\n' % (sectname, value))
807 elif v == sectname:
807 elif v == sectname:
808 ui.debug('%s: ' %
808 ui.debug('%s: ' %
809 ui.configsource(section, name, untrusted))
809 ui.configsource(section, name, untrusted))
810 ui.write(value, '\n')
810 ui.write(value, '\n')
811 else:
811 else:
812 ui.debug('%s: ' %
812 ui.debug('%s: ' %
813 ui.configsource(section, name, untrusted))
813 ui.configsource(section, name, untrusted))
814 ui.write('%s=%s\n' % (sectname, value))
814 ui.write('%s=%s\n' % (sectname, value))
815
815
816 def debugsetparents(ui, repo, rev1, rev2=None):
816 def debugsetparents(ui, repo, rev1, rev2=None):
817 """manually set the parents of the current working directory
817 """manually set the parents of the current working directory
818
818
819 This is useful for writing repository conversion tools, but should
819 This is useful for writing repository conversion tools, but should
820 be used with care.
820 be used with care.
821 """
821 """
822
822
823 if not rev2:
823 if not rev2:
824 rev2 = hex(nullid)
824 rev2 = hex(nullid)
825
825
826 wlock = repo.wlock()
826 wlock = repo.wlock()
827 try:
827 try:
828 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
828 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
829 finally:
829 finally:
830 wlock.release()
830 wlock.release()
831
831
832 def debugstate(ui, repo, nodates=None):
832 def debugstate(ui, repo, nodates=None):
833 """show the contents of the current dirstate"""
833 """show the contents of the current dirstate"""
834 timestr = ""
834 timestr = ""
835 showdate = not nodates
835 showdate = not nodates
836 for file_, ent in sorted(repo.dirstate._map.iteritems()):
836 for file_, ent in sorted(repo.dirstate._map.iteritems()):
837 if showdate:
837 if showdate:
838 if ent[3] == -1:
838 if ent[3] == -1:
839 # Pad or slice to locale representation
839 # Pad or slice to locale representation
840 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
840 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
841 timestr = 'unset'
841 timestr = 'unset'
842 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
842 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
843 else:
843 else:
844 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
844 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
845 if ent[1] & 020000:
845 if ent[1] & 020000:
846 mode = 'lnk'
846 mode = 'lnk'
847 else:
847 else:
848 mode = '%3o' % (ent[1] & 0777)
848 mode = '%3o' % (ent[1] & 0777)
849 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
849 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
850 for f in repo.dirstate.copies():
850 for f in repo.dirstate.copies():
851 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
851 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
852
852
853 def debugdata(ui, file_, rev):
853 def debugdata(ui, file_, rev):
854 """dump the contents of a data file revision"""
854 """dump the contents of a data file revision"""
855 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
855 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
856 try:
856 try:
857 ui.write(r.revision(r.lookup(rev)))
857 ui.write(r.revision(r.lookup(rev)))
858 except KeyError:
858 except KeyError:
859 raise util.Abort(_('invalid revision identifier %s') % rev)
859 raise util.Abort(_('invalid revision identifier %s') % rev)
860
860
861 def debugdate(ui, date, range=None, **opts):
861 def debugdate(ui, date, range=None, **opts):
862 """parse and display a date"""
862 """parse and display a date"""
863 if opts["extended"]:
863 if opts["extended"]:
864 d = util.parsedate(date, util.extendeddateformats)
864 d = util.parsedate(date, util.extendeddateformats)
865 else:
865 else:
866 d = util.parsedate(date)
866 d = util.parsedate(date)
867 ui.write("internal: %s %s\n" % d)
867 ui.write("internal: %s %s\n" % d)
868 ui.write("standard: %s\n" % util.datestr(d))
868 ui.write("standard: %s\n" % util.datestr(d))
869 if range:
869 if range:
870 m = util.matchdate(range)
870 m = util.matchdate(range)
871 ui.write("match: %s\n" % m(d[0]))
871 ui.write("match: %s\n" % m(d[0]))
872
872
873 def debugindex(ui, file_):
873 def debugindex(ui, file_):
874 """dump the contents of an index file"""
874 """dump the contents of an index file"""
875 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
875 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
876 ui.write(" rev offset length base linkrev"
876 ui.write(" rev offset length base linkrev"
877 " nodeid p1 p2\n")
877 " nodeid p1 p2\n")
878 for i in r:
878 for i in r:
879 node = r.node(i)
879 node = r.node(i)
880 try:
880 try:
881 pp = r.parents(node)
881 pp = r.parents(node)
882 except:
882 except:
883 pp = [nullid, nullid]
883 pp = [nullid, nullid]
884 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
884 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
885 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
885 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
886 short(node), short(pp[0]), short(pp[1])))
886 short(node), short(pp[0]), short(pp[1])))
887
887
888 def debugindexdot(ui, file_):
888 def debugindexdot(ui, file_):
889 """dump an index DAG as a .dot file"""
889 """dump an index DAG as a .dot file"""
890 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
890 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
891 ui.write("digraph G {\n")
891 ui.write("digraph G {\n")
892 for i in r:
892 for i in r:
893 node = r.node(i)
893 node = r.node(i)
894 pp = r.parents(node)
894 pp = r.parents(node)
895 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
895 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
896 if pp[1] != nullid:
896 if pp[1] != nullid:
897 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
897 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
898 ui.write("}\n")
898 ui.write("}\n")
899
899
900 def debuginstall(ui):
900 def debuginstall(ui):
901 '''test Mercurial installation'''
901 '''test Mercurial installation'''
902
902
903 def writetemp(contents):
903 def writetemp(contents):
904 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
904 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
905 f = os.fdopen(fd, "wb")
905 f = os.fdopen(fd, "wb")
906 f.write(contents)
906 f.write(contents)
907 f.close()
907 f.close()
908 return name
908 return name
909
909
910 problems = 0
910 problems = 0
911
911
912 # encoding
912 # encoding
913 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
913 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
914 try:
914 try:
915 encoding.fromlocal("test")
915 encoding.fromlocal("test")
916 except util.Abort, inst:
916 except util.Abort, inst:
917 ui.write(" %s\n" % inst)
917 ui.write(" %s\n" % inst)
918 ui.write(_(" (check that your locale is properly set)\n"))
918 ui.write(_(" (check that your locale is properly set)\n"))
919 problems += 1
919 problems += 1
920
920
921 # compiled modules
921 # compiled modules
922 ui.status(_("Checking extensions...\n"))
922 ui.status(_("Checking extensions...\n"))
923 try:
923 try:
924 import bdiff, mpatch, base85
924 import bdiff, mpatch, base85
925 except Exception, inst:
925 except Exception, inst:
926 ui.write(" %s\n" % inst)
926 ui.write(" %s\n" % inst)
927 ui.write(_(" One or more extensions could not be found"))
927 ui.write(_(" One or more extensions could not be found"))
928 ui.write(_(" (check that you compiled the extensions)\n"))
928 ui.write(_(" (check that you compiled the extensions)\n"))
929 problems += 1
929 problems += 1
930
930
931 # templates
931 # templates
932 ui.status(_("Checking templates...\n"))
932 ui.status(_("Checking templates...\n"))
933 try:
933 try:
934 import templater
934 import templater
935 templater.templater(templater.templatepath("map-cmdline.default"))
935 templater.templater(templater.templatepath("map-cmdline.default"))
936 except Exception, inst:
936 except Exception, inst:
937 ui.write(" %s\n" % inst)
937 ui.write(" %s\n" % inst)
938 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
938 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
939 problems += 1
939 problems += 1
940
940
941 # patch
941 # patch
942 ui.status(_("Checking patch...\n"))
942 ui.status(_("Checking patch...\n"))
943 patchproblems = 0
943 patchproblems = 0
944 a = "1\n2\n3\n4\n"
944 a = "1\n2\n3\n4\n"
945 b = "1\n2\n3\ninsert\n4\n"
945 b = "1\n2\n3\ninsert\n4\n"
946 fa = writetemp(a)
946 fa = writetemp(a)
947 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
947 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
948 os.path.basename(fa))
948 os.path.basename(fa))
949 fd = writetemp(d)
949 fd = writetemp(d)
950
950
951 files = {}
951 files = {}
952 try:
952 try:
953 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
953 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
954 except util.Abort, e:
954 except util.Abort, e:
955 ui.write(_(" patch call failed:\n"))
955 ui.write(_(" patch call failed:\n"))
956 ui.write(" " + str(e) + "\n")
956 ui.write(" " + str(e) + "\n")
957 patchproblems += 1
957 patchproblems += 1
958 else:
958 else:
959 if list(files) != [os.path.basename(fa)]:
959 if list(files) != [os.path.basename(fa)]:
960 ui.write(_(" unexpected patch output!\n"))
960 ui.write(_(" unexpected patch output!\n"))
961 patchproblems += 1
961 patchproblems += 1
962 a = file(fa).read()
962 a = file(fa).read()
963 if a != b:
963 if a != b:
964 ui.write(_(" patch test failed!\n"))
964 ui.write(_(" patch test failed!\n"))
965 patchproblems += 1
965 patchproblems += 1
966
966
967 if patchproblems:
967 if patchproblems:
968 if ui.config('ui', 'patch'):
968 if ui.config('ui', 'patch'):
969 ui.write(_(" (Current patch tool may be incompatible with patch,"
969 ui.write(_(" (Current patch tool may be incompatible with patch,"
970 " or misconfigured. Please check your .hgrc file)\n"))
970 " or misconfigured. Please check your .hgrc file)\n"))
971 else:
971 else:
972 ui.write(_(" Internal patcher failure, please report this error"
972 ui.write(_(" Internal patcher failure, please report this error"
973 " to http://www.selenic.com/mercurial/bts\n"))
973 " to http://www.selenic.com/mercurial/bts\n"))
974 problems += patchproblems
974 problems += patchproblems
975
975
976 os.unlink(fa)
976 os.unlink(fa)
977 os.unlink(fd)
977 os.unlink(fd)
978
978
979 # editor
979 # editor
980 ui.status(_("Checking commit editor...\n"))
980 ui.status(_("Checking commit editor...\n"))
981 editor = ui.geteditor()
981 editor = ui.geteditor()
982 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
982 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
983 if not cmdpath:
983 if not cmdpath:
984 if editor == 'vi':
984 if editor == 'vi':
985 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
985 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
986 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
986 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
987 else:
987 else:
988 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
988 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
989 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
989 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
990 problems += 1
990 problems += 1
991
991
992 # check username
992 # check username
993 ui.status(_("Checking username...\n"))
993 ui.status(_("Checking username...\n"))
994 user = os.environ.get("HGUSER")
994 user = os.environ.get("HGUSER")
995 if user is None:
995 if user is None:
996 user = ui.config("ui", "username")
996 user = ui.config("ui", "username")
997 if user is None:
997 if user is None:
998 user = os.environ.get("EMAIL")
998 user = os.environ.get("EMAIL")
999 if not user:
999 if not user:
1000 ui.warn(" ")
1000 ui.warn(" ")
1001 ui.username()
1001 ui.username()
1002 ui.write(_(" (specify a username in your .hgrc file)\n"))
1002 ui.write(_(" (specify a username in your .hgrc file)\n"))
1003
1003
1004 if not problems:
1004 if not problems:
1005 ui.status(_("No problems detected\n"))
1005 ui.status(_("No problems detected\n"))
1006 else:
1006 else:
1007 ui.write(_("%s problems detected,"
1007 ui.write(_("%s problems detected,"
1008 " please check your install!\n") % problems)
1008 " please check your install!\n") % problems)
1009
1009
1010 return problems
1010 return problems
1011
1011
1012 def debugrename(ui, repo, file1, *pats, **opts):
1012 def debugrename(ui, repo, file1, *pats, **opts):
1013 """dump rename information"""
1013 """dump rename information"""
1014
1014
1015 ctx = repo[opts.get('rev')]
1015 ctx = repo[opts.get('rev')]
1016 m = cmdutil.match(repo, (file1,) + pats, opts)
1016 m = cmdutil.match(repo, (file1,) + pats, opts)
1017 for abs in ctx.walk(m):
1017 for abs in ctx.walk(m):
1018 fctx = ctx[abs]
1018 fctx = ctx[abs]
1019 o = fctx.filelog().renamed(fctx.filenode())
1019 o = fctx.filelog().renamed(fctx.filenode())
1020 rel = m.rel(abs)
1020 rel = m.rel(abs)
1021 if o:
1021 if o:
1022 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1022 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1023 else:
1023 else:
1024 ui.write(_("%s not renamed\n") % rel)
1024 ui.write(_("%s not renamed\n") % rel)
1025
1025
1026 def debugwalk(ui, repo, *pats, **opts):
1026 def debugwalk(ui, repo, *pats, **opts):
1027 """show how files match on given patterns"""
1027 """show how files match on given patterns"""
1028 m = cmdutil.match(repo, pats, opts)
1028 m = cmdutil.match(repo, pats, opts)
1029 items = list(repo.walk(m))
1029 items = list(repo.walk(m))
1030 if not items:
1030 if not items:
1031 return
1031 return
1032 fmt = 'f %%-%ds %%-%ds %%s' % (
1032 fmt = 'f %%-%ds %%-%ds %%s' % (
1033 max([len(abs) for abs in items]),
1033 max([len(abs) for abs in items]),
1034 max([len(m.rel(abs)) for abs in items]))
1034 max([len(m.rel(abs)) for abs in items]))
1035 for abs in items:
1035 for abs in items:
1036 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1036 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1037 ui.write("%s\n" % line.rstrip())
1037 ui.write("%s\n" % line.rstrip())
1038
1038
1039 def diff(ui, repo, *pats, **opts):
1039 def diff(ui, repo, *pats, **opts):
1040 """diff repository (or selected files)
1040 """diff repository (or selected files)
1041
1041
1042 Show differences between revisions for the specified files.
1042 Show differences between revisions for the specified files.
1043
1043
1044 Differences between files are shown using the unified diff format.
1044 Differences between files are shown using the unified diff format.
1045
1045
1046 NOTE: diff may generate unexpected results for merges, as it will
1046 NOTE: diff may generate unexpected results for merges, as it will
1047 default to comparing against the working directory's first parent
1047 default to comparing against the working directory's first parent
1048 changeset if no revisions are specified.
1048 changeset if no revisions are specified.
1049
1049
1050 When two revision arguments are given, then changes are shown
1050 When two revision arguments are given, then changes are shown
1051 between those revisions. If only one revision is specified then
1051 between those revisions. If only one revision is specified then
1052 that revision is compared to the working directory, and, when no
1052 that revision is compared to the working directory, and, when no
1053 revisions are specified, the working directory files are compared
1053 revisions are specified, the working directory files are compared
1054 to its parent.
1054 to its parent.
1055
1055
1056 Without the -a/--text option, diff will avoid generating diffs of
1056 Without the -a/--text option, diff will avoid generating diffs of
1057 files it detects as binary. With -a, diff will generate a diff
1057 files it detects as binary. With -a, diff will generate a diff
1058 anyway, probably with undesirable results.
1058 anyway, probably with undesirable results.
1059
1059
1060 Use the -g/--git option to generate diffs in the git extended diff
1060 Use the -g/--git option to generate diffs in the git extended diff
1061 format. For more information, read 'hg help diffs'.
1061 format. For more information, read 'hg help diffs'.
1062 """
1062 """
1063
1063
1064 revs = opts.get('rev')
1064 revs = opts.get('rev')
1065 change = opts.get('change')
1065 change = opts.get('change')
1066
1066
1067 if revs and change:
1067 if revs and change:
1068 msg = _('cannot specify --rev and --change at the same time')
1068 msg = _('cannot specify --rev and --change at the same time')
1069 raise util.Abort(msg)
1069 raise util.Abort(msg)
1070 elif change:
1070 elif change:
1071 node2 = repo.lookup(change)
1071 node2 = repo.lookup(change)
1072 node1 = repo[node2].parents()[0].node()
1072 node1 = repo[node2].parents()[0].node()
1073 else:
1073 else:
1074 node1, node2 = cmdutil.revpair(repo, revs)
1074 node1, node2 = cmdutil.revpair(repo, revs)
1075
1075
1076 m = cmdutil.match(repo, pats, opts)
1076 m = cmdutil.match(repo, pats, opts)
1077 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1077 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1078 for chunk in it:
1078 for chunk in it:
1079 ui.write(chunk)
1079 ui.write(chunk)
1080
1080
1081 def export(ui, repo, *changesets, **opts):
1081 def export(ui, repo, *changesets, **opts):
1082 """dump the header and diffs for one or more changesets
1082 """dump the header and diffs for one or more changesets
1083
1083
1084 Print the changeset header and diffs for one or more revisions.
1084 Print the changeset header and diffs for one or more revisions.
1085
1085
1086 The information shown in the changeset header is: author,
1086 The information shown in the changeset header is: author,
1087 changeset hash, parent(s) and commit comment.
1087 changeset hash, parent(s) and commit comment.
1088
1088
1089 NOTE: export may generate unexpected diff output for merge
1089 NOTE: export may generate unexpected diff output for merge
1090 changesets, as it will compare the merge changeset against its
1090 changesets, as it will compare the merge changeset against its
1091 first parent only.
1091 first parent only.
1092
1092
1093 Output may be to a file, in which case the name of the file is
1093 Output may be to a file, in which case the name of the file is
1094 given using a format string. The formatting rules are as follows:
1094 given using a format string. The formatting rules are as follows:
1095
1095
1096 %% literal "%" character
1096 %% literal "%" character
1097 %H changeset hash (40 bytes of hexadecimal)
1097 %H changeset hash (40 bytes of hexadecimal)
1098 %N number of patches being generated
1098 %N number of patches being generated
1099 %R changeset revision number
1099 %R changeset revision number
1100 %b basename of the exporting repository
1100 %b basename of the exporting repository
1101 %h short-form changeset hash (12 bytes of hexadecimal)
1101 %h short-form changeset hash (12 bytes of hexadecimal)
1102 %n zero-padded sequence number, starting at 1
1102 %n zero-padded sequence number, starting at 1
1103 %r zero-padded changeset revision number
1103 %r zero-padded changeset revision number
1104
1104
1105 Without the -a/--text option, export will avoid generating diffs
1105 Without the -a/--text option, export will avoid generating diffs
1106 of files it detects as binary. With -a, export will generate a
1106 of files it detects as binary. With -a, export will generate a
1107 diff anyway, probably with undesirable results.
1107 diff anyway, probably with undesirable results.
1108
1108
1109 Use the -g/--git option to generate diffs in the git extended diff
1109 Use the -g/--git option to generate diffs in the git extended diff
1110 format. Read the diffs help topic for more information.
1110 format. Read the diffs help topic for more information.
1111
1111
1112 With the --switch-parent option, the diff will be against the
1112 With the --switch-parent option, the diff will be against the
1113 second parent. It can be useful to review a merge.
1113 second parent. It can be useful to review a merge.
1114 """
1114 """
1115 if not changesets:
1115 if not changesets:
1116 raise util.Abort(_("export requires at least one changeset"))
1116 raise util.Abort(_("export requires at least one changeset"))
1117 revs = cmdutil.revrange(repo, changesets)
1117 revs = cmdutil.revrange(repo, changesets)
1118 if len(revs) > 1:
1118 if len(revs) > 1:
1119 ui.note(_('exporting patches:\n'))
1119 ui.note(_('exporting patches:\n'))
1120 else:
1120 else:
1121 ui.note(_('exporting patch:\n'))
1121 ui.note(_('exporting patch:\n'))
1122 patch.export(repo, revs, template=opts.get('output'),
1122 patch.export(repo, revs, template=opts.get('output'),
1123 switch_parent=opts.get('switch_parent'),
1123 switch_parent=opts.get('switch_parent'),
1124 opts=patch.diffopts(ui, opts))
1124 opts=patch.diffopts(ui, opts))
1125
1125
1126 def grep(ui, repo, pattern, *pats, **opts):
1126 def grep(ui, repo, pattern, *pats, **opts):
1127 """search for a pattern in specified files and revisions
1127 """search for a pattern in specified files and revisions
1128
1128
1129 Search revisions of files for a regular expression.
1129 Search revisions of files for a regular expression.
1130
1130
1131 This command behaves differently than Unix grep. It only accepts
1131 This command behaves differently than Unix grep. It only accepts
1132 Python/Perl regexps. It searches repository history, not the
1132 Python/Perl regexps. It searches repository history, not the
1133 working directory. It always prints the revision number in which a
1133 working directory. It always prints the revision number in which a
1134 match appears.
1134 match appears.
1135
1135
1136 By default, grep only prints output for the first revision of a
1136 By default, grep only prints output for the first revision of a
1137 file in which it finds a match. To get it to print every revision
1137 file in which it finds a match. To get it to print every revision
1138 that contains a change in match status ("-" for a match that
1138 that contains a change in match status ("-" for a match that
1139 becomes a non-match, or "+" for a non-match that becomes a match),
1139 becomes a non-match, or "+" for a non-match that becomes a match),
1140 use the --all flag.
1140 use the --all flag.
1141 """
1141 """
1142 reflags = 0
1142 reflags = 0
1143 if opts.get('ignore_case'):
1143 if opts.get('ignore_case'):
1144 reflags |= re.I
1144 reflags |= re.I
1145 try:
1145 try:
1146 regexp = re.compile(pattern, reflags)
1146 regexp = re.compile(pattern, reflags)
1147 except Exception, inst:
1147 except Exception, inst:
1148 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1148 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1149 return None
1149 return None
1150 sep, eol = ':', '\n'
1150 sep, eol = ':', '\n'
1151 if opts.get('print0'):
1151 if opts.get('print0'):
1152 sep = eol = '\0'
1152 sep = eol = '\0'
1153
1153
1154 fcache = {}
1154 fcache = {}
1155 forder = []
1155 forder = []
1156 def getfile(fn):
1156 def getfile(fn):
1157 if fn not in fcache:
1157 if fn not in fcache:
1158 if len(fcache) > 20:
1158 if len(fcache) > 20:
1159 del fcache[forder.pop(0)]
1159 del fcache[forder.pop(0)]
1160 fcache[fn] = repo.file(fn)
1160 fcache[fn] = repo.file(fn)
1161 else:
1161 else:
1162 forder.remove(fn)
1162 forder.remove(fn)
1163
1163
1164 forder.append(fn)
1164 forder.append(fn)
1165 return fcache[fn]
1165 return fcache[fn]
1166
1166
1167 def matchlines(body):
1167 def matchlines(body):
1168 begin = 0
1168 begin = 0
1169 linenum = 0
1169 linenum = 0
1170 while True:
1170 while True:
1171 match = regexp.search(body, begin)
1171 match = regexp.search(body, begin)
1172 if not match:
1172 if not match:
1173 break
1173 break
1174 mstart, mend = match.span()
1174 mstart, mend = match.span()
1175 linenum += body.count('\n', begin, mstart) + 1
1175 linenum += body.count('\n', begin, mstart) + 1
1176 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1176 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1177 begin = body.find('\n', mend) + 1 or len(body)
1177 begin = body.find('\n', mend) + 1 or len(body)
1178 lend = begin - 1
1178 lend = begin - 1
1179 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1179 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1180
1180
1181 class linestate(object):
1181 class linestate(object):
1182 def __init__(self, line, linenum, colstart, colend):
1182 def __init__(self, line, linenum, colstart, colend):
1183 self.line = line
1183 self.line = line
1184 self.linenum = linenum
1184 self.linenum = linenum
1185 self.colstart = colstart
1185 self.colstart = colstart
1186 self.colend = colend
1186 self.colend = colend
1187
1187
1188 def __hash__(self):
1188 def __hash__(self):
1189 return hash((self.linenum, self.line))
1189 return hash((self.linenum, self.line))
1190
1190
1191 def __eq__(self, other):
1191 def __eq__(self, other):
1192 return self.line == other.line
1192 return self.line == other.line
1193
1193
1194 matches = {}
1194 matches = {}
1195 copies = {}
1195 copies = {}
1196 def grepbody(fn, rev, body):
1196 def grepbody(fn, rev, body):
1197 matches[rev].setdefault(fn, [])
1197 matches[rev].setdefault(fn, [])
1198 m = matches[rev][fn]
1198 m = matches[rev][fn]
1199 for lnum, cstart, cend, line in matchlines(body):
1199 for lnum, cstart, cend, line in matchlines(body):
1200 s = linestate(line, lnum, cstart, cend)
1200 s = linestate(line, lnum, cstart, cend)
1201 m.append(s)
1201 m.append(s)
1202
1202
1203 def difflinestates(a, b):
1203 def difflinestates(a, b):
1204 sm = difflib.SequenceMatcher(None, a, b)
1204 sm = difflib.SequenceMatcher(None, a, b)
1205 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1205 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1206 if tag == 'insert':
1206 if tag == 'insert':
1207 for i in xrange(blo, bhi):
1207 for i in xrange(blo, bhi):
1208 yield ('+', b[i])
1208 yield ('+', b[i])
1209 elif tag == 'delete':
1209 elif tag == 'delete':
1210 for i in xrange(alo, ahi):
1210 for i in xrange(alo, ahi):
1211 yield ('-', a[i])
1211 yield ('-', a[i])
1212 elif tag == 'replace':
1212 elif tag == 'replace':
1213 for i in xrange(alo, ahi):
1213 for i in xrange(alo, ahi):
1214 yield ('-', a[i])
1214 yield ('-', a[i])
1215 for i in xrange(blo, bhi):
1215 for i in xrange(blo, bhi):
1216 yield ('+', b[i])
1216 yield ('+', b[i])
1217
1217
1218 prev = {}
1218 prev = {}
1219 def display(fn, rev, states, prevstates):
1219 def display(fn, rev, states, prevstates):
1220 datefunc = ui.quiet and util.shortdate or util.datestr
1220 datefunc = ui.quiet and util.shortdate or util.datestr
1221 found = False
1221 found = False
1222 filerevmatches = {}
1222 filerevmatches = {}
1223 r = prev.get(fn, -1)
1223 r = prev.get(fn, -1)
1224 if opts.get('all'):
1224 if opts.get('all'):
1225 iter = difflinestates(states, prevstates)
1225 iter = difflinestates(states, prevstates)
1226 else:
1226 else:
1227 iter = [('', l) for l in prevstates]
1227 iter = [('', l) for l in prevstates]
1228 for change, l in iter:
1228 for change, l in iter:
1229 cols = [fn, str(r)]
1229 cols = [fn, str(r)]
1230 if opts.get('line_number'):
1230 if opts.get('line_number'):
1231 cols.append(str(l.linenum))
1231 cols.append(str(l.linenum))
1232 if opts.get('all'):
1232 if opts.get('all'):
1233 cols.append(change)
1233 cols.append(change)
1234 if opts.get('user'):
1234 if opts.get('user'):
1235 cols.append(ui.shortuser(get(r)[1]))
1235 cols.append(ui.shortuser(get(r)[1]))
1236 if opts.get('date'):
1236 if opts.get('date'):
1237 cols.append(datefunc(get(r)[2]))
1237 cols.append(datefunc(get(r)[2]))
1238 if opts.get('files_with_matches'):
1238 if opts.get('files_with_matches'):
1239 c = (fn, r)
1239 c = (fn, r)
1240 if c in filerevmatches:
1240 if c in filerevmatches:
1241 continue
1241 continue
1242 filerevmatches[c] = 1
1242 filerevmatches[c] = 1
1243 else:
1243 else:
1244 cols.append(l.line)
1244 cols.append(l.line)
1245 ui.write(sep.join(cols), eol)
1245 ui.write(sep.join(cols), eol)
1246 found = True
1246 found = True
1247 return found
1247 return found
1248
1248
1249 fstate = {}
1249 fstate = {}
1250 skip = {}
1250 skip = {}
1251 get = util.cachefunc(lambda r: repo[r].changeset())
1251 get = util.cachefunc(lambda r: repo[r].changeset())
1252 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1252 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1253 found = False
1253 found = False
1254 follow = opts.get('follow')
1254 follow = opts.get('follow')
1255 for st, rev, fns in changeiter:
1255 for st, rev, fns in changeiter:
1256 if st == 'window':
1256 if st == 'window':
1257 matches.clear()
1257 matches.clear()
1258 elif st == 'add':
1258 elif st == 'add':
1259 ctx = repo[rev]
1259 ctx = repo[rev]
1260 matches[rev] = {}
1260 matches[rev] = {}
1261 for fn in fns:
1261 for fn in fns:
1262 if fn in skip:
1262 if fn in skip:
1263 continue
1263 continue
1264 try:
1264 try:
1265 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1265 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1266 fstate.setdefault(fn, [])
1266 fstate.setdefault(fn, [])
1267 if follow:
1267 if follow:
1268 copied = getfile(fn).renamed(ctx.filenode(fn))
1268 copied = getfile(fn).renamed(ctx.filenode(fn))
1269 if copied:
1269 if copied:
1270 copies.setdefault(rev, {})[fn] = copied[0]
1270 copies.setdefault(rev, {})[fn] = copied[0]
1271 except error.LookupError:
1271 except error.LookupError:
1272 pass
1272 pass
1273 elif st == 'iter':
1273 elif st == 'iter':
1274 for fn, m in sorted(matches[rev].items()):
1274 for fn, m in sorted(matches[rev].items()):
1275 copy = copies.get(rev, {}).get(fn)
1275 copy = copies.get(rev, {}).get(fn)
1276 if fn in skip:
1276 if fn in skip:
1277 if copy:
1277 if copy:
1278 skip[copy] = True
1278 skip[copy] = True
1279 continue
1279 continue
1280 if fn in prev or fstate[fn]:
1280 if fn in prev or fstate[fn]:
1281 r = display(fn, rev, m, fstate[fn])
1281 r = display(fn, rev, m, fstate[fn])
1282 found = found or r
1282 found = found or r
1283 if r and not opts.get('all'):
1283 if r and not opts.get('all'):
1284 skip[fn] = True
1284 skip[fn] = True
1285 if copy:
1285 if copy:
1286 skip[copy] = True
1286 skip[copy] = True
1287 fstate[fn] = m
1287 fstate[fn] = m
1288 if copy:
1288 if copy:
1289 fstate[copy] = m
1289 fstate[copy] = m
1290 prev[fn] = rev
1290 prev[fn] = rev
1291
1291
1292 for fn, state in sorted(fstate.items()):
1292 for fn, state in sorted(fstate.items()):
1293 if fn in skip:
1293 if fn in skip:
1294 continue
1294 continue
1295 if fn not in copies.get(prev[fn], {}):
1295 if fn not in copies.get(prev[fn], {}):
1296 found = display(fn, rev, {}, state) or found
1296 found = display(fn, rev, {}, state) or found
1297 return (not found and 1) or 0
1297 return (not found and 1) or 0
1298
1298
1299 def heads(ui, repo, *branchrevs, **opts):
1299 def heads(ui, repo, *branchrevs, **opts):
1300 """show current repository heads or show branch heads
1300 """show current repository heads or show branch heads
1301
1301
1302 With no arguments, show all repository head changesets.
1302 With no arguments, show all repository head changesets.
1303
1303
1304 If branch or revisions names are given this will show the heads of
1304 If branch or revisions names are given this will show the heads of
1305 the specified branches or the branches those revisions are tagged
1305 the specified branches or the branches those revisions are tagged
1306 with.
1306 with.
1307
1307
1308 Repository "heads" are changesets that don't have child
1308 Repository "heads" are changesets that don't have child
1309 changesets. They are where development generally takes place and
1309 changesets. They are where development generally takes place and
1310 are the usual targets for update and merge operations.
1310 are the usual targets for update and merge operations.
1311
1311
1312 Branch heads are changesets that have a given branch tag, but have
1312 Branch heads are changesets that have a given branch tag, but have
1313 no child changesets with that tag. They are usually where
1313 no child changesets with that tag. They are usually where
1314 development on the given branch takes place.
1314 development on the given branch takes place.
1315 """
1315 """
1316 if opts.get('rev'):
1316 if opts.get('rev'):
1317 start = repo.lookup(opts['rev'])
1317 start = repo.lookup(opts['rev'])
1318 else:
1318 else:
1319 start = None
1319 start = None
1320 closed = opts.get('closed')
1320 closed = opts.get('closed')
1321 hideinactive, _heads = opts.get('active'), None
1321 hideinactive, _heads = opts.get('active'), None
1322 if not branchrevs:
1322 if not branchrevs:
1323 # Assume we're looking repo-wide heads if no revs were specified.
1323 # Assume we're looking repo-wide heads if no revs were specified.
1324 heads = repo.heads(start, closed=closed)
1324 heads = repo.heads(start, closed=closed)
1325 else:
1325 else:
1326 if hideinactive:
1326 if hideinactive:
1327 _heads = repo.heads(start, closed=closed)
1327 _heads = repo.heads(start, closed=closed)
1328 heads = []
1328 heads = []
1329 visitedset = set()
1329 visitedset = set()
1330 for branchrev in branchrevs:
1330 for branchrev in branchrevs:
1331 branch = repo[branchrev].branch()
1331 branch = repo[branchrev].branch()
1332 if branch in visitedset:
1332 if branch in visitedset:
1333 continue
1333 continue
1334 visitedset.add(branch)
1334 visitedset.add(branch)
1335 bheads = repo.branchheads(branch, start, closed=closed)
1335 bheads = repo.branchheads(branch, start, closed=closed)
1336 if not bheads:
1336 if not bheads:
1337 if branch != branchrev:
1337 if branch != branchrev:
1338 ui.warn(_("no changes on branch %s containing %s are "
1338 ui.warn(_("no changes on branch %s containing %s are "
1339 "reachable from %s\n")
1339 "reachable from %s\n")
1340 % (branch, branchrev, opts.get('rev')))
1340 % (branch, branchrev, opts.get('rev')))
1341 else:
1341 else:
1342 ui.warn(_("no changes on branch %s are reachable from %s\n")
1342 ui.warn(_("no changes on branch %s are reachable from %s\n")
1343 % (branch, opts.get('rev')))
1343 % (branch, opts.get('rev')))
1344 if hideinactive:
1344 if hideinactive:
1345 bheads = [bhead for bhead in bheads if bhead in _heads]
1345 bheads = [bhead for bhead in bheads if bhead in _heads]
1346 heads.extend(bheads)
1346 heads.extend(bheads)
1347 if not heads:
1347 if not heads:
1348 return 1
1348 return 1
1349 displayer = cmdutil.show_changeset(ui, repo, opts)
1349 displayer = cmdutil.show_changeset(ui, repo, opts)
1350 for n in heads:
1350 for n in heads:
1351 displayer.show(repo[n])
1351 displayer.show(repo[n])
1352
1352
1353 def help_(ui, name=None, with_version=False):
1353 def help_(ui, name=None, with_version=False):
1354 """show help for a given topic or a help overview
1354 """show help for a given topic or a help overview
1355
1355
1356 With no arguments, print a list of commands and short help.
1356 With no arguments, print a list of commands and short help.
1357
1357
1358 Given a topic, extension, or command name, print help for that
1358 Given a topic, extension, or command name, print help for that
1359 topic."""
1359 topic."""
1360 option_lists = []
1360 option_lists = []
1361
1361
1362 def addglobalopts(aliases):
1362 def addglobalopts(aliases):
1363 if ui.verbose:
1363 if ui.verbose:
1364 option_lists.append((_("global options:"), globalopts))
1364 option_lists.append((_("global options:"), globalopts))
1365 if name == 'shortlist':
1365 if name == 'shortlist':
1366 option_lists.append((_('use "hg help" for the full list '
1366 option_lists.append((_('use "hg help" for the full list '
1367 'of commands'), ()))
1367 'of commands'), ()))
1368 else:
1368 else:
1369 if name == 'shortlist':
1369 if name == 'shortlist':
1370 msg = _('use "hg help" for the full list of commands '
1370 msg = _('use "hg help" for the full list of commands '
1371 'or "hg -v" for details')
1371 'or "hg -v" for details')
1372 elif aliases:
1372 elif aliases:
1373 msg = _('use "hg -v help%s" to show aliases and '
1373 msg = _('use "hg -v help%s" to show aliases and '
1374 'global options') % (name and " " + name or "")
1374 'global options') % (name and " " + name or "")
1375 else:
1375 else:
1376 msg = _('use "hg -v help %s" to show global options') % name
1376 msg = _('use "hg -v help %s" to show global options') % name
1377 option_lists.append((msg, ()))
1377 option_lists.append((msg, ()))
1378
1378
1379 def helpcmd(name):
1379 def helpcmd(name):
1380 if with_version:
1380 if with_version:
1381 version_(ui)
1381 version_(ui)
1382 ui.write('\n')
1382 ui.write('\n')
1383
1383
1384 try:
1384 try:
1385 aliases, i = cmdutil.findcmd(name, table, False)
1385 aliases, i = cmdutil.findcmd(name, table, False)
1386 except error.AmbiguousCommand, inst:
1386 except error.AmbiguousCommand, inst:
1387 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1387 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1388 helplist(_('list of commands:\n\n'), select)
1388 helplist(_('list of commands:\n\n'), select)
1389 return
1389 return
1390
1390
1391 # synopsis
1391 # synopsis
1392 if len(i) > 2:
1392 if len(i) > 2:
1393 if i[2].startswith('hg'):
1393 if i[2].startswith('hg'):
1394 ui.write("%s\n" % i[2])
1394 ui.write("%s\n" % i[2])
1395 else:
1395 else:
1396 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1396 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1397 else:
1397 else:
1398 ui.write('hg %s\n' % aliases[0])
1398 ui.write('hg %s\n' % aliases[0])
1399
1399
1400 # aliases
1400 # aliases
1401 if not ui.quiet and len(aliases) > 1:
1401 if not ui.quiet and len(aliases) > 1:
1402 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1402 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1403
1403
1404 # description
1404 # description
1405 doc = gettext(i[0].__doc__)
1405 doc = gettext(i[0].__doc__)
1406 if not doc:
1406 if not doc:
1407 doc = _("(no help text available)")
1407 doc = _("(no help text available)")
1408 if ui.quiet:
1408 if ui.quiet:
1409 doc = doc.splitlines(0)[0]
1409 doc = doc.splitlines(0)[0]
1410 ui.write("\n%s\n" % doc.rstrip())
1410 ui.write("\n%s\n" % doc.rstrip())
1411
1411
1412 if not ui.quiet:
1412 if not ui.quiet:
1413 # options
1413 # options
1414 if i[1]:
1414 if i[1]:
1415 option_lists.append((_("options:\n"), i[1]))
1415 option_lists.append((_("options:\n"), i[1]))
1416
1416
1417 addglobalopts(False)
1417 addglobalopts(False)
1418
1418
1419 def helplist(header, select=None):
1419 def helplist(header, select=None):
1420 h = {}
1420 h = {}
1421 cmds = {}
1421 cmds = {}
1422 for c, e in table.iteritems():
1422 for c, e in table.iteritems():
1423 f = c.split("|", 1)[0]
1423 f = c.split("|", 1)[0]
1424 if select and not select(f):
1424 if select and not select(f):
1425 continue
1425 continue
1426 if (not select and name != 'shortlist' and
1426 if (not select and name != 'shortlist' and
1427 e[0].__module__ != __name__):
1427 e[0].__module__ != __name__):
1428 continue
1428 continue
1429 if name == "shortlist" and not f.startswith("^"):
1429 if name == "shortlist" and not f.startswith("^"):
1430 continue
1430 continue
1431 f = f.lstrip("^")
1431 f = f.lstrip("^")
1432 if not ui.debugflag and f.startswith("debug"):
1432 if not ui.debugflag and f.startswith("debug"):
1433 continue
1433 continue
1434 doc = gettext(e[0].__doc__)
1434 doc = gettext(e[0].__doc__)
1435 if not doc:
1435 if not doc:
1436 doc = _("(no help text available)")
1436 doc = _("(no help text available)")
1437 h[f] = doc.splitlines(0)[0].rstrip()
1437 h[f] = doc.splitlines(0)[0].rstrip()
1438 cmds[f] = c.lstrip("^")
1438 cmds[f] = c.lstrip("^")
1439
1439
1440 if not h:
1440 if not h:
1441 ui.status(_('no commands defined\n'))
1441 ui.status(_('no commands defined\n'))
1442 return
1442 return
1443
1443
1444 ui.status(header)
1444 ui.status(header)
1445 fns = sorted(h)
1445 fns = sorted(h)
1446 m = max(map(len, fns))
1446 m = max(map(len, fns))
1447 for f in fns:
1447 for f in fns:
1448 if ui.verbose:
1448 if ui.verbose:
1449 commands = cmds[f].replace("|",", ")
1449 commands = cmds[f].replace("|",", ")
1450 ui.write(" %s:\n %s\n"%(commands, h[f]))
1450 ui.write(" %s:\n %s\n"%(commands, h[f]))
1451 else:
1451 else:
1452 ui.write(' %-*s %s\n' % (m, f, h[f]))
1452 ui.write(' %-*s %s\n' % (m, f, h[f]))
1453
1453
1454 exts = list(extensions.extensions())
1454 exts = list(extensions.extensions())
1455 if exts and name != 'shortlist':
1455 if exts and name != 'shortlist':
1456 ui.write(_('\nenabled extensions:\n\n'))
1456 ui.write(_('\nenabled extensions:\n\n'))
1457 maxlength = 0
1457 maxlength = 0
1458 exthelps = []
1458 exthelps = []
1459 for ename, ext in exts:
1459 for ename, ext in exts:
1460 doc = (gettext(ext.__doc__) or _('(no help text available)'))
1460 doc = (gettext(ext.__doc__) or _('(no help text available)'))
1461 ename = ename.split('.')[-1]
1461 ename = ename.split('.')[-1]
1462 maxlength = max(len(ename), maxlength)
1462 maxlength = max(len(ename), maxlength)
1463 exthelps.append((ename, doc.splitlines(0)[0].strip()))
1463 exthelps.append((ename, doc.splitlines(0)[0].strip()))
1464 for ename, text in exthelps:
1464 for ename, text in exthelps:
1465 ui.write(_(' %s %s\n') % (ename.ljust(maxlength), text))
1465 ui.write(_(' %s %s\n') % (ename.ljust(maxlength), text))
1466
1466
1467 if not ui.quiet:
1467 if not ui.quiet:
1468 addglobalopts(True)
1468 addglobalopts(True)
1469
1469
1470 def helptopic(name):
1470 def helptopic(name):
1471 for names, header, doc in help.helptable:
1471 for names, header, doc in help.helptable:
1472 if name in names:
1472 if name in names:
1473 break
1473 break
1474 else:
1474 else:
1475 raise error.UnknownCommand(name)
1475 raise error.UnknownCommand(name)
1476
1476
1477 # description
1477 # description
1478 if not doc:
1478 if not doc:
1479 doc = _("(no help text available)")
1479 doc = _("(no help text available)")
1480 if hasattr(doc, '__call__'):
1480 if hasattr(doc, '__call__'):
1481 doc = doc()
1481 doc = doc()
1482
1482
1483 ui.write("%s\n" % header)
1483 ui.write("%s\n" % header)
1484 ui.write("%s\n" % doc.rstrip())
1484 ui.write("%s\n" % doc.rstrip())
1485
1485
1486 def helpext(name):
1486 def helpext(name):
1487 try:
1487 try:
1488 mod = extensions.find(name)
1488 mod = extensions.find(name)
1489 except KeyError:
1489 except KeyError:
1490 raise error.UnknownCommand(name)
1490 raise error.UnknownCommand(name)
1491
1491
1492 doc = gettext(mod.__doc__) or _('no help text available')
1492 doc = gettext(mod.__doc__) or _('no help text available')
1493 doc = doc.splitlines(0)
1493 doc = doc.splitlines(0)
1494 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1494 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1495 for d in doc[1:]:
1495 for d in doc[1:]:
1496 ui.write(d, '\n')
1496 ui.write(d, '\n')
1497
1497
1498 ui.status('\n')
1498 ui.status('\n')
1499
1499
1500 try:
1500 try:
1501 ct = mod.cmdtable
1501 ct = mod.cmdtable
1502 except AttributeError:
1502 except AttributeError:
1503 ct = {}
1503 ct = {}
1504
1504
1505 modcmds = set([c.split('|', 1)[0] for c in ct])
1505 modcmds = set([c.split('|', 1)[0] for c in ct])
1506 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1506 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1507
1507
1508 if name and name != 'shortlist':
1508 if name and name != 'shortlist':
1509 i = None
1509 i = None
1510 for f in (helptopic, helpcmd, helpext):
1510 for f in (helptopic, helpcmd, helpext):
1511 try:
1511 try:
1512 f(name)
1512 f(name)
1513 i = None
1513 i = None
1514 break
1514 break
1515 except error.UnknownCommand, inst:
1515 except error.UnknownCommand, inst:
1516 i = inst
1516 i = inst
1517 if i:
1517 if i:
1518 raise i
1518 raise i
1519
1519
1520 else:
1520 else:
1521 # program name
1521 # program name
1522 if ui.verbose or with_version:
1522 if ui.verbose or with_version:
1523 version_(ui)
1523 version_(ui)
1524 else:
1524 else:
1525 ui.status(_("Mercurial Distributed SCM\n"))
1525 ui.status(_("Mercurial Distributed SCM\n"))
1526 ui.status('\n')
1526 ui.status('\n')
1527
1527
1528 # list of commands
1528 # list of commands
1529 if name == "shortlist":
1529 if name == "shortlist":
1530 header = _('basic commands:\n\n')
1530 header = _('basic commands:\n\n')
1531 else:
1531 else:
1532 header = _('list of commands:\n\n')
1532 header = _('list of commands:\n\n')
1533
1533
1534 helplist(header)
1534 helplist(header)
1535
1535
1536 # list all option lists
1536 # list all option lists
1537 opt_output = []
1537 opt_output = []
1538 for title, options in option_lists:
1538 for title, options in option_lists:
1539 opt_output.append(("\n%s" % title, None))
1539 opt_output.append(("\n%s" % title, None))
1540 for shortopt, longopt, default, desc in options:
1540 for shortopt, longopt, default, desc in options:
1541 if "DEPRECATED" in desc and not ui.verbose: continue
1541 if "DEPRECATED" in desc and not ui.verbose: continue
1542 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1542 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1543 longopt and " --%s" % longopt),
1543 longopt and " --%s" % longopt),
1544 "%s%s" % (desc,
1544 "%s%s" % (desc,
1545 default
1545 default
1546 and _(" (default: %s)") % default
1546 and _(" (default: %s)") % default
1547 or "")))
1547 or "")))
1548
1548
1549 if not name:
1549 if not name:
1550 ui.write(_("\nadditional help topics:\n\n"))
1550 ui.write(_("\nadditional help topics:\n\n"))
1551 topics = []
1551 topics = []
1552 for names, header, doc in help.helptable:
1552 for names, header, doc in help.helptable:
1553 names = [(-len(name), name) for name in names]
1553 names = [(-len(name), name) for name in names]
1554 names.sort()
1554 names.sort()
1555 topics.append((names[0][1], header))
1555 topics.append((names[0][1], header))
1556 topics_len = max([len(s[0]) for s in topics])
1556 topics_len = max([len(s[0]) for s in topics])
1557 for t, desc in topics:
1557 for t, desc in topics:
1558 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1558 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1559
1559
1560 if opt_output:
1560 if opt_output:
1561 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1561 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1562 for first, second in opt_output:
1562 for first, second in opt_output:
1563 if second:
1563 if second:
1564 # wrap descriptions at 70 characters, just like the
1564 # wrap descriptions at 70 characters, just like the
1565 # main help texts
1565 # main help texts
1566 second = textwrap.wrap(second, width=70 - opts_len - 3)
1566 second = textwrap.wrap(second, width=70 - opts_len - 3)
1567 pad = '\n' + ' ' * (opts_len + 3)
1567 pad = '\n' + ' ' * (opts_len + 3)
1568 ui.write(" %-*s %s\n" % (opts_len, first, pad.join(second)))
1568 ui.write(" %-*s %s\n" % (opts_len, first, pad.join(second)))
1569 else:
1569 else:
1570 ui.write("%s\n" % first)
1570 ui.write("%s\n" % first)
1571
1571
1572 def identify(ui, repo, source=None,
1572 def identify(ui, repo, source=None,
1573 rev=None, num=None, id=None, branch=None, tags=None):
1573 rev=None, num=None, id=None, branch=None, tags=None):
1574 """identify the working copy or specified revision
1574 """identify the working copy or specified revision
1575
1575
1576 With no revision, print a summary of the current state of the
1576 With no revision, print a summary of the current state of the
1577 repository.
1577 repository.
1578
1578
1579 With a path, do a lookup in another repository.
1579 With a path, do a lookup in another repository.
1580
1580
1581 This summary identifies the repository state using one or two
1581 This summary identifies the repository state using one or two
1582 parent hash identifiers, followed by a "+" if there are
1582 parent hash identifiers, followed by a "+" if there are
1583 uncommitted changes in the working directory, a list of tags for
1583 uncommitted changes in the working directory, a list of tags for
1584 this revision and a branch name for non-default branches.
1584 this revision and a branch name for non-default branches.
1585 """
1585 """
1586
1586
1587 if not repo and not source:
1587 if not repo and not source:
1588 raise util.Abort(_("There is no Mercurial repository here "
1588 raise util.Abort(_("There is no Mercurial repository here "
1589 "(.hg not found)"))
1589 "(.hg not found)"))
1590
1590
1591 hexfunc = ui.debugflag and hex or short
1591 hexfunc = ui.debugflag and hex or short
1592 default = not (num or id or branch or tags)
1592 default = not (num or id or branch or tags)
1593 output = []
1593 output = []
1594
1594
1595 revs = []
1595 revs = []
1596 if source:
1596 if source:
1597 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1597 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1598 repo = hg.repository(ui, source)
1598 repo = hg.repository(ui, source)
1599
1599
1600 if not repo.local():
1600 if not repo.local():
1601 if not rev and revs:
1601 if not rev and revs:
1602 rev = revs[0]
1602 rev = revs[0]
1603 if not rev:
1603 if not rev:
1604 rev = "tip"
1604 rev = "tip"
1605 if num or branch or tags:
1605 if num or branch or tags:
1606 raise util.Abort(
1606 raise util.Abort(
1607 "can't query remote revision number, branch, or tags")
1607 "can't query remote revision number, branch, or tags")
1608 output = [hexfunc(repo.lookup(rev))]
1608 output = [hexfunc(repo.lookup(rev))]
1609 elif not rev:
1609 elif not rev:
1610 ctx = repo[None]
1610 ctx = repo[None]
1611 parents = ctx.parents()
1611 parents = ctx.parents()
1612 changed = False
1612 changed = False
1613 if default or id or num:
1613 if default or id or num:
1614 changed = ctx.files() + ctx.deleted()
1614 changed = ctx.files() + ctx.deleted()
1615 if default or id:
1615 if default or id:
1616 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1616 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1617 (changed) and "+" or "")]
1617 (changed) and "+" or "")]
1618 if num:
1618 if num:
1619 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1619 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1620 (changed) and "+" or ""))
1620 (changed) and "+" or ""))
1621 else:
1621 else:
1622 ctx = repo[rev]
1622 ctx = repo[rev]
1623 if default or id:
1623 if default or id:
1624 output = [hexfunc(ctx.node())]
1624 output = [hexfunc(ctx.node())]
1625 if num:
1625 if num:
1626 output.append(str(ctx.rev()))
1626 output.append(str(ctx.rev()))
1627
1627
1628 if repo.local() and default and not ui.quiet:
1628 if repo.local() and default and not ui.quiet:
1629 b = encoding.tolocal(ctx.branch())
1629 b = encoding.tolocal(ctx.branch())
1630 if b != 'default':
1630 if b != 'default':
1631 output.append("(%s)" % b)
1631 output.append("(%s)" % b)
1632
1632
1633 # multiple tags for a single parent separated by '/'
1633 # multiple tags for a single parent separated by '/'
1634 t = "/".join(ctx.tags())
1634 t = "/".join(ctx.tags())
1635 if t:
1635 if t:
1636 output.append(t)
1636 output.append(t)
1637
1637
1638 if branch:
1638 if branch:
1639 output.append(encoding.tolocal(ctx.branch()))
1639 output.append(encoding.tolocal(ctx.branch()))
1640
1640
1641 if tags:
1641 if tags:
1642 output.extend(ctx.tags())
1642 output.extend(ctx.tags())
1643
1643
1644 ui.write("%s\n" % ' '.join(output))
1644 ui.write("%s\n" % ' '.join(output))
1645
1645
1646 def import_(ui, repo, patch1, *patches, **opts):
1646 def import_(ui, repo, patch1, *patches, **opts):
1647 """import an ordered set of patches
1647 """import an ordered set of patches
1648
1648
1649 Import a list of patches and commit them individually.
1649 Import a list of patches and commit them individually.
1650
1650
1651 If there are outstanding changes in the working directory, import
1651 If there are outstanding changes in the working directory, import
1652 will abort unless given the -f/--force flag.
1652 will abort unless given the -f/--force flag.
1653
1653
1654 You can import a patch straight from a mail message. Even patches
1654 You can import a patch straight from a mail message. Even patches
1655 as attachments work (body part must be type text/plain or
1655 as attachments work (body part must be type text/plain or
1656 text/x-patch to be used). From and Subject headers of email
1656 text/x-patch to be used). From and Subject headers of email
1657 message are used as default committer and commit message. All
1657 message are used as default committer and commit message. All
1658 text/plain body parts before first diff are added to commit
1658 text/plain body parts before first diff are added to commit
1659 message.
1659 message.
1660
1660
1661 If the imported patch was generated by hg export, user and
1661 If the imported patch was generated by hg export, user and
1662 description from patch override values from message headers and
1662 description from patch override values from message headers and
1663 body. Values given on command line with -m/--message and -u/--user
1663 body. Values given on command line with -m/--message and -u/--user
1664 override these.
1664 override these.
1665
1665
1666 If --exact is specified, import will set the working directory to
1666 If --exact is specified, import will set the working directory to
1667 the parent of each patch before applying it, and will abort if the
1667 the parent of each patch before applying it, and will abort if the
1668 resulting changeset has a different ID than the one recorded in
1668 resulting changeset has a different ID than the one recorded in
1669 the patch. This may happen due to character set problems or other
1669 the patch. This may happen due to character set problems or other
1670 deficiencies in the text patch format.
1670 deficiencies in the text patch format.
1671
1671
1672 With -s/--similarity, hg will attempt to discover renames and
1672 With -s/--similarity, hg will attempt to discover renames and
1673 copies in the patch in the same way as 'addremove'.
1673 copies in the patch in the same way as 'addremove'.
1674
1674
1675 To read a patch from standard input, use patch name "-". See 'hg
1675 To read a patch from standard input, use patch name "-". See 'hg
1676 help dates' for a list of formats valid for -d/--date.
1676 help dates' for a list of formats valid for -d/--date.
1677 """
1677 """
1678 patches = (patch1,) + patches
1678 patches = (patch1,) + patches
1679
1679
1680 date = opts.get('date')
1680 date = opts.get('date')
1681 if date:
1681 if date:
1682 opts['date'] = util.parsedate(date)
1682 opts['date'] = util.parsedate(date)
1683
1683
1684 try:
1684 try:
1685 sim = float(opts.get('similarity') or 0)
1685 sim = float(opts.get('similarity') or 0)
1686 except ValueError:
1686 except ValueError:
1687 raise util.Abort(_('similarity must be a number'))
1687 raise util.Abort(_('similarity must be a number'))
1688 if sim < 0 or sim > 100:
1688 if sim < 0 or sim > 100:
1689 raise util.Abort(_('similarity must be between 0 and 100'))
1689 raise util.Abort(_('similarity must be between 0 and 100'))
1690
1690
1691 if opts.get('exact') or not opts.get('force'):
1691 if opts.get('exact') or not opts.get('force'):
1692 cmdutil.bail_if_changed(repo)
1692 cmdutil.bail_if_changed(repo)
1693
1693
1694 d = opts["base"]
1694 d = opts["base"]
1695 strip = opts["strip"]
1695 strip = opts["strip"]
1696 wlock = lock = None
1696 wlock = lock = None
1697 try:
1697 try:
1698 wlock = repo.wlock()
1698 wlock = repo.wlock()
1699 lock = repo.lock()
1699 lock = repo.lock()
1700 for p in patches:
1700 for p in patches:
1701 pf = os.path.join(d, p)
1701 pf = os.path.join(d, p)
1702
1702
1703 if pf == '-':
1703 if pf == '-':
1704 ui.status(_("applying patch from stdin\n"))
1704 ui.status(_("applying patch from stdin\n"))
1705 pf = sys.stdin
1705 pf = sys.stdin
1706 else:
1706 else:
1707 ui.status(_("applying %s\n") % p)
1707 ui.status(_("applying %s\n") % p)
1708 pf = url.open(ui, pf)
1708 pf = url.open(ui, pf)
1709 data = patch.extract(ui, pf)
1709 data = patch.extract(ui, pf)
1710 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1710 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1711
1711
1712 if tmpname is None:
1712 if tmpname is None:
1713 raise util.Abort(_('no diffs found'))
1713 raise util.Abort(_('no diffs found'))
1714
1714
1715 try:
1715 try:
1716 cmdline_message = cmdutil.logmessage(opts)
1716 cmdline_message = cmdutil.logmessage(opts)
1717 if cmdline_message:
1717 if cmdline_message:
1718 # pickup the cmdline msg
1718 # pickup the cmdline msg
1719 message = cmdline_message
1719 message = cmdline_message
1720 elif message:
1720 elif message:
1721 # pickup the patch msg
1721 # pickup the patch msg
1722 message = message.strip()
1722 message = message.strip()
1723 else:
1723 else:
1724 # launch the editor
1724 # launch the editor
1725 message = None
1725 message = None
1726 ui.debug(_('message:\n%s\n') % message)
1726 ui.debug(_('message:\n%s\n') % message)
1727
1727
1728 wp = repo.parents()
1728 wp = repo.parents()
1729 if opts.get('exact'):
1729 if opts.get('exact'):
1730 if not nodeid or not p1:
1730 if not nodeid or not p1:
1731 raise util.Abort(_('not a mercurial patch'))
1731 raise util.Abort(_('not a Mercurial patch'))
1732 p1 = repo.lookup(p1)
1732 p1 = repo.lookup(p1)
1733 p2 = repo.lookup(p2 or hex(nullid))
1733 p2 = repo.lookup(p2 or hex(nullid))
1734
1734
1735 if p1 != wp[0].node():
1735 if p1 != wp[0].node():
1736 hg.clean(repo, p1)
1736 hg.clean(repo, p1)
1737 repo.dirstate.setparents(p1, p2)
1737 repo.dirstate.setparents(p1, p2)
1738 elif p2:
1738 elif p2:
1739 try:
1739 try:
1740 p1 = repo.lookup(p1)
1740 p1 = repo.lookup(p1)
1741 p2 = repo.lookup(p2)
1741 p2 = repo.lookup(p2)
1742 if p1 == wp[0].node():
1742 if p1 == wp[0].node():
1743 repo.dirstate.setparents(p1, p2)
1743 repo.dirstate.setparents(p1, p2)
1744 except error.RepoError:
1744 except error.RepoError:
1745 pass
1745 pass
1746 if opts.get('exact') or opts.get('import_branch'):
1746 if opts.get('exact') or opts.get('import_branch'):
1747 repo.dirstate.setbranch(branch or 'default')
1747 repo.dirstate.setbranch(branch or 'default')
1748
1748
1749 files = {}
1749 files = {}
1750 try:
1750 try:
1751 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1751 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1752 files=files)
1752 files=files)
1753 finally:
1753 finally:
1754 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1754 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1755 if not opts.get('no_commit'):
1755 if not opts.get('no_commit'):
1756 m = cmdutil.matchfiles(repo, files or [])
1756 m = cmdutil.matchfiles(repo, files or [])
1757 n = repo.commit(message, opts.get('user') or user,
1757 n = repo.commit(message, opts.get('user') or user,
1758 opts.get('date') or date, match=m,
1758 opts.get('date') or date, match=m,
1759 editor=cmdutil.commiteditor)
1759 editor=cmdutil.commiteditor)
1760 if opts.get('exact'):
1760 if opts.get('exact'):
1761 if hex(n) != nodeid:
1761 if hex(n) != nodeid:
1762 repo.rollback()
1762 repo.rollback()
1763 raise util.Abort(_('patch is damaged'
1763 raise util.Abort(_('patch is damaged'
1764 ' or loses information'))
1764 ' or loses information'))
1765 # Force a dirstate write so that the next transaction
1765 # Force a dirstate write so that the next transaction
1766 # backups an up-do-date file.
1766 # backups an up-do-date file.
1767 repo.dirstate.write()
1767 repo.dirstate.write()
1768 finally:
1768 finally:
1769 os.unlink(tmpname)
1769 os.unlink(tmpname)
1770 finally:
1770 finally:
1771 release(lock, wlock)
1771 release(lock, wlock)
1772
1772
1773 def incoming(ui, repo, source="default", **opts):
1773 def incoming(ui, repo, source="default", **opts):
1774 """show new changesets found in source
1774 """show new changesets found in source
1775
1775
1776 Show new changesets found in the specified path/URL or the default
1776 Show new changesets found in the specified path/URL or the default
1777 pull location. These are the changesets that would be pulled if a
1777 pull location. These are the changesets that would be pulled if a
1778 pull was requested.
1778 pull was requested.
1779
1779
1780 For remote repository, using --bundle avoids downloading the
1780 For remote repository, using --bundle avoids downloading the
1781 changesets twice if the incoming is followed by a pull.
1781 changesets twice if the incoming is followed by a pull.
1782
1782
1783 See pull for valid source format details.
1783 See pull for valid source format details.
1784 """
1784 """
1785 limit = cmdutil.loglimit(opts)
1785 limit = cmdutil.loglimit(opts)
1786 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1786 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1787 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1787 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1788 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1788 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1789 if revs:
1789 if revs:
1790 revs = [other.lookup(rev) for rev in revs]
1790 revs = [other.lookup(rev) for rev in revs]
1791 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1791 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1792 force=opts["force"])
1792 force=opts["force"])
1793 if not incoming:
1793 if not incoming:
1794 try:
1794 try:
1795 os.unlink(opts["bundle"])
1795 os.unlink(opts["bundle"])
1796 except:
1796 except:
1797 pass
1797 pass
1798 ui.status(_("no changes found\n"))
1798 ui.status(_("no changes found\n"))
1799 return 1
1799 return 1
1800
1800
1801 cleanup = None
1801 cleanup = None
1802 try:
1802 try:
1803 fname = opts["bundle"]
1803 fname = opts["bundle"]
1804 if fname or not other.local():
1804 if fname or not other.local():
1805 # create a bundle (uncompressed if other repo is not local)
1805 # create a bundle (uncompressed if other repo is not local)
1806
1806
1807 if revs is None and other.capable('changegroupsubset'):
1807 if revs is None and other.capable('changegroupsubset'):
1808 revs = rheads
1808 revs = rheads
1809
1809
1810 if revs is None:
1810 if revs is None:
1811 cg = other.changegroup(incoming, "incoming")
1811 cg = other.changegroup(incoming, "incoming")
1812 else:
1812 else:
1813 cg = other.changegroupsubset(incoming, revs, 'incoming')
1813 cg = other.changegroupsubset(incoming, revs, 'incoming')
1814 bundletype = other.local() and "HG10BZ" or "HG10UN"
1814 bundletype = other.local() and "HG10BZ" or "HG10UN"
1815 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1815 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1816 # keep written bundle?
1816 # keep written bundle?
1817 if opts["bundle"]:
1817 if opts["bundle"]:
1818 cleanup = None
1818 cleanup = None
1819 if not other.local():
1819 if not other.local():
1820 # use the created uncompressed bundlerepo
1820 # use the created uncompressed bundlerepo
1821 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1821 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1822
1822
1823 o = other.changelog.nodesbetween(incoming, revs)[0]
1823 o = other.changelog.nodesbetween(incoming, revs)[0]
1824 if opts.get('newest_first'):
1824 if opts.get('newest_first'):
1825 o.reverse()
1825 o.reverse()
1826 displayer = cmdutil.show_changeset(ui, other, opts)
1826 displayer = cmdutil.show_changeset(ui, other, opts)
1827 count = 0
1827 count = 0
1828 for n in o:
1828 for n in o:
1829 if count >= limit:
1829 if count >= limit:
1830 break
1830 break
1831 parents = [p for p in other.changelog.parents(n) if p != nullid]
1831 parents = [p for p in other.changelog.parents(n) if p != nullid]
1832 if opts.get('no_merges') and len(parents) == 2:
1832 if opts.get('no_merges') and len(parents) == 2:
1833 continue
1833 continue
1834 count += 1
1834 count += 1
1835 displayer.show(other[n])
1835 displayer.show(other[n])
1836 finally:
1836 finally:
1837 if hasattr(other, 'close'):
1837 if hasattr(other, 'close'):
1838 other.close()
1838 other.close()
1839 if cleanup:
1839 if cleanup:
1840 os.unlink(cleanup)
1840 os.unlink(cleanup)
1841
1841
1842 def init(ui, dest=".", **opts):
1842 def init(ui, dest=".", **opts):
1843 """create a new repository in the given directory
1843 """create a new repository in the given directory
1844
1844
1845 Initialize a new repository in the given directory. If the given
1845 Initialize a new repository in the given directory. If the given
1846 directory does not exist, it is created.
1846 directory does not exist, it is created.
1847
1847
1848 If no directory is given, the current directory is used.
1848 If no directory is given, the current directory is used.
1849
1849
1850 It is possible to specify an ssh:// URL as the destination.
1850 It is possible to specify an ssh:// URL as the destination.
1851 See 'hg help urls' for more information.
1851 See 'hg help urls' for more information.
1852 """
1852 """
1853 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1853 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1854
1854
1855 def locate(ui, repo, *pats, **opts):
1855 def locate(ui, repo, *pats, **opts):
1856 """locate files matching specific patterns
1856 """locate files matching specific patterns
1857
1857
1858 Print all files under Mercurial control whose names match the
1858 Print all files under Mercurial control whose names match the
1859 given patterns.
1859 given patterns.
1860
1860
1861 This command searches the entire repository by default. To search
1861 This command searches the entire repository by default. To search
1862 just the current directory and its subdirectories, use
1862 just the current directory and its subdirectories, use
1863 "--include .".
1863 "--include .".
1864
1864
1865 If no patterns are given to match, this command prints all file
1865 If no patterns are given to match, this command prints all file
1866 names.
1866 names.
1867
1867
1868 If you want to feed the output of this command into the "xargs"
1868 If you want to feed the output of this command into the "xargs"
1869 command, use the -0 option to both this command and "xargs". This
1869 command, use the -0 option to both this command and "xargs". This
1870 will avoid the problem of "xargs" treating single filenames that
1870 will avoid the problem of "xargs" treating single filenames that
1871 contain white space as multiple filenames.
1871 contain white space as multiple filenames.
1872 """
1872 """
1873 end = opts.get('print0') and '\0' or '\n'
1873 end = opts.get('print0') and '\0' or '\n'
1874 rev = opts.get('rev') or None
1874 rev = opts.get('rev') or None
1875
1875
1876 ret = 1
1876 ret = 1
1877 m = cmdutil.match(repo, pats, opts, default='relglob')
1877 m = cmdutil.match(repo, pats, opts, default='relglob')
1878 m.bad = lambda x,y: False
1878 m.bad = lambda x,y: False
1879 for abs in repo[rev].walk(m):
1879 for abs in repo[rev].walk(m):
1880 if not rev and abs not in repo.dirstate:
1880 if not rev and abs not in repo.dirstate:
1881 continue
1881 continue
1882 if opts.get('fullpath'):
1882 if opts.get('fullpath'):
1883 ui.write(repo.wjoin(abs), end)
1883 ui.write(repo.wjoin(abs), end)
1884 else:
1884 else:
1885 ui.write(((pats and m.rel(abs)) or abs), end)
1885 ui.write(((pats and m.rel(abs)) or abs), end)
1886 ret = 0
1886 ret = 0
1887
1887
1888 return ret
1888 return ret
1889
1889
1890 def log(ui, repo, *pats, **opts):
1890 def log(ui, repo, *pats, **opts):
1891 """show revision history of entire repository or files
1891 """show revision history of entire repository or files
1892
1892
1893 Print the revision history of the specified files or the entire
1893 Print the revision history of the specified files or the entire
1894 project.
1894 project.
1895
1895
1896 File history is shown without following rename or copy history of
1896 File history is shown without following rename or copy history of
1897 files. Use -f/--follow with a file name to follow history across
1897 files. Use -f/--follow with a file name to follow history across
1898 renames and copies. --follow without a file name will only show
1898 renames and copies. --follow without a file name will only show
1899 ancestors or descendants of the starting revision. --follow-first
1899 ancestors or descendants of the starting revision. --follow-first
1900 only follows the first parent of merge revisions.
1900 only follows the first parent of merge revisions.
1901
1901
1902 If no revision range is specified, the default is tip:0 unless
1902 If no revision range is specified, the default is tip:0 unless
1903 --follow is set, in which case the working directory parent is
1903 --follow is set, in which case the working directory parent is
1904 used as the starting revision.
1904 used as the starting revision.
1905
1905
1906 See 'hg help dates' for a list of formats valid for -d/--date.
1906 See 'hg help dates' for a list of formats valid for -d/--date.
1907
1907
1908 By default this command outputs: changeset id and hash, tags,
1908 By default this command outputs: changeset id and hash, tags,
1909 non-trivial parents, user, date and time, and a summary for each
1909 non-trivial parents, user, date and time, and a summary for each
1910 commit. When the -v/--verbose switch is used, the list of changed
1910 commit. When the -v/--verbose switch is used, the list of changed
1911 files and full commit message is shown.
1911 files and full commit message is shown.
1912
1912
1913 NOTE: log -p/--patch may generate unexpected diff output for merge
1913 NOTE: log -p/--patch may generate unexpected diff output for merge
1914 changesets, as it will only compare the merge changeset against
1914 changesets, as it will only compare the merge changeset against
1915 its first parent. Also, the files: list will only reflect files
1915 its first parent. Also, the files: list will only reflect files
1916 that are different from BOTH parents.
1916 that are different from BOTH parents.
1917
1917
1918 """
1918 """
1919
1919
1920 get = util.cachefunc(lambda r: repo[r].changeset())
1920 get = util.cachefunc(lambda r: repo[r].changeset())
1921 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1921 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1922
1922
1923 limit = cmdutil.loglimit(opts)
1923 limit = cmdutil.loglimit(opts)
1924 count = 0
1924 count = 0
1925
1925
1926 if opts.get('copies') and opts.get('rev'):
1926 if opts.get('copies') and opts.get('rev'):
1927 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1927 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1928 else:
1928 else:
1929 endrev = len(repo)
1929 endrev = len(repo)
1930 rcache = {}
1930 rcache = {}
1931 ncache = {}
1931 ncache = {}
1932 def getrenamed(fn, rev):
1932 def getrenamed(fn, rev):
1933 '''looks up all renames for a file (up to endrev) the first
1933 '''looks up all renames for a file (up to endrev) the first
1934 time the file is given. It indexes on the changerev and only
1934 time the file is given. It indexes on the changerev and only
1935 parses the manifest if linkrev != changerev.
1935 parses the manifest if linkrev != changerev.
1936 Returns rename info for fn at changerev rev.'''
1936 Returns rename info for fn at changerev rev.'''
1937 if fn not in rcache:
1937 if fn not in rcache:
1938 rcache[fn] = {}
1938 rcache[fn] = {}
1939 ncache[fn] = {}
1939 ncache[fn] = {}
1940 fl = repo.file(fn)
1940 fl = repo.file(fn)
1941 for i in fl:
1941 for i in fl:
1942 node = fl.node(i)
1942 node = fl.node(i)
1943 lr = fl.linkrev(i)
1943 lr = fl.linkrev(i)
1944 renamed = fl.renamed(node)
1944 renamed = fl.renamed(node)
1945 rcache[fn][lr] = renamed
1945 rcache[fn][lr] = renamed
1946 if renamed:
1946 if renamed:
1947 ncache[fn][node] = renamed
1947 ncache[fn][node] = renamed
1948 if lr >= endrev:
1948 if lr >= endrev:
1949 break
1949 break
1950 if rev in rcache[fn]:
1950 if rev in rcache[fn]:
1951 return rcache[fn][rev]
1951 return rcache[fn][rev]
1952
1952
1953 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1953 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1954 # filectx logic.
1954 # filectx logic.
1955
1955
1956 try:
1956 try:
1957 return repo[rev][fn].renamed()
1957 return repo[rev][fn].renamed()
1958 except error.LookupError:
1958 except error.LookupError:
1959 pass
1959 pass
1960 return None
1960 return None
1961
1961
1962 df = False
1962 df = False
1963 if opts["date"]:
1963 if opts["date"]:
1964 df = util.matchdate(opts["date"])
1964 df = util.matchdate(opts["date"])
1965
1965
1966 only_branches = opts.get('only_branch')
1966 only_branches = opts.get('only_branch')
1967
1967
1968 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1968 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1969 for st, rev, fns in changeiter:
1969 for st, rev, fns in changeiter:
1970 if st == 'add':
1970 if st == 'add':
1971 parents = [p for p in repo.changelog.parentrevs(rev)
1971 parents = [p for p in repo.changelog.parentrevs(rev)
1972 if p != nullrev]
1972 if p != nullrev]
1973 if opts.get('no_merges') and len(parents) == 2:
1973 if opts.get('no_merges') and len(parents) == 2:
1974 continue
1974 continue
1975 if opts.get('only_merges') and len(parents) != 2:
1975 if opts.get('only_merges') and len(parents) != 2:
1976 continue
1976 continue
1977
1977
1978 if only_branches:
1978 if only_branches:
1979 revbranch = get(rev)[5]['branch']
1979 revbranch = get(rev)[5]['branch']
1980 if revbranch not in only_branches:
1980 if revbranch not in only_branches:
1981 continue
1981 continue
1982
1982
1983 if df:
1983 if df:
1984 changes = get(rev)
1984 changes = get(rev)
1985 if not df(changes[2][0]):
1985 if not df(changes[2][0]):
1986 continue
1986 continue
1987
1987
1988 if opts.get('keyword'):
1988 if opts.get('keyword'):
1989 changes = get(rev)
1989 changes = get(rev)
1990 miss = 0
1990 miss = 0
1991 for k in [kw.lower() for kw in opts['keyword']]:
1991 for k in [kw.lower() for kw in opts['keyword']]:
1992 if not (k in changes[1].lower() or
1992 if not (k in changes[1].lower() or
1993 k in changes[4].lower() or
1993 k in changes[4].lower() or
1994 k in " ".join(changes[3]).lower()):
1994 k in " ".join(changes[3]).lower()):
1995 miss = 1
1995 miss = 1
1996 break
1996 break
1997 if miss:
1997 if miss:
1998 continue
1998 continue
1999
1999
2000 if opts['user']:
2000 if opts['user']:
2001 changes = get(rev)
2001 changes = get(rev)
2002 if not [k for k in opts['user'] if k in changes[1]]:
2002 if not [k for k in opts['user'] if k in changes[1]]:
2003 continue
2003 continue
2004
2004
2005 copies = []
2005 copies = []
2006 if opts.get('copies') and rev:
2006 if opts.get('copies') and rev:
2007 for fn in get(rev)[3]:
2007 for fn in get(rev)[3]:
2008 rename = getrenamed(fn, rev)
2008 rename = getrenamed(fn, rev)
2009 if rename:
2009 if rename:
2010 copies.append((fn, rename[0]))
2010 copies.append((fn, rename[0]))
2011 displayer.show(context.changectx(repo, rev), copies=copies)
2011 displayer.show(context.changectx(repo, rev), copies=copies)
2012 elif st == 'iter':
2012 elif st == 'iter':
2013 if count == limit: break
2013 if count == limit: break
2014 if displayer.flush(rev):
2014 if displayer.flush(rev):
2015 count += 1
2015 count += 1
2016
2016
2017 def manifest(ui, repo, node=None, rev=None):
2017 def manifest(ui, repo, node=None, rev=None):
2018 """output the current or given revision of the project manifest
2018 """output the current or given revision of the project manifest
2019
2019
2020 Print a list of version controlled files for the given revision.
2020 Print a list of version controlled files for the given revision.
2021 If no revision is given, the first parent of the working directory
2021 If no revision is given, the first parent of the working directory
2022 is used, or the null revision if none is checked out.
2022 is used, or the null revision if none is checked out.
2023
2023
2024 With -v flag, print file permissions, symlink and executable bits.
2024 With -v flag, print file permissions, symlink and executable bits.
2025 With --debug flag, print file revision hashes.
2025 With --debug flag, print file revision hashes.
2026 """
2026 """
2027
2027
2028 if rev and node:
2028 if rev and node:
2029 raise util.Abort(_("please specify just one revision"))
2029 raise util.Abort(_("please specify just one revision"))
2030
2030
2031 if not node:
2031 if not node:
2032 node = rev
2032 node = rev
2033
2033
2034 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2034 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2035 ctx = repo[node]
2035 ctx = repo[node]
2036 for f in ctx:
2036 for f in ctx:
2037 if ui.debugflag:
2037 if ui.debugflag:
2038 ui.write("%40s " % hex(ctx.manifest()[f]))
2038 ui.write("%40s " % hex(ctx.manifest()[f]))
2039 if ui.verbose:
2039 if ui.verbose:
2040 ui.write(decor[ctx.flags(f)])
2040 ui.write(decor[ctx.flags(f)])
2041 ui.write("%s\n" % f)
2041 ui.write("%s\n" % f)
2042
2042
2043 def merge(ui, repo, node=None, **opts):
2043 def merge(ui, repo, node=None, **opts):
2044 """merge working directory with another revision
2044 """merge working directory with another revision
2045
2045
2046 The contents of the current working directory is updated with all
2046 The contents of the current working directory is updated with all
2047 changes made in the requested revision since the last common
2047 changes made in the requested revision since the last common
2048 predecessor revision.
2048 predecessor revision.
2049
2049
2050 Files that changed between either parent are marked as changed for
2050 Files that changed between either parent are marked as changed for
2051 the next commit and a commit must be performed before any further
2051 the next commit and a commit must be performed before any further
2052 updates are allowed. The next commit has two parents.
2052 updates are allowed. The next commit has two parents.
2053
2053
2054 If no revision is specified, the working directory's parent is a
2054 If no revision is specified, the working directory's parent is a
2055 head revision, and the current branch contains exactly one other
2055 head revision, and the current branch contains exactly one other
2056 head, the other head is merged with by default. Otherwise, an
2056 head, the other head is merged with by default. Otherwise, an
2057 explicit revision to merge with must be provided.
2057 explicit revision to merge with must be provided.
2058 """
2058 """
2059
2059
2060 if opts.get('rev') and node:
2060 if opts.get('rev') and node:
2061 raise util.Abort(_("please specify just one revision"))
2061 raise util.Abort(_("please specify just one revision"))
2062 if not node:
2062 if not node:
2063 node = opts.get('rev')
2063 node = opts.get('rev')
2064
2064
2065 if not node:
2065 if not node:
2066 branch = repo.changectx(None).branch()
2066 branch = repo.changectx(None).branch()
2067 bheads = repo.branchheads(branch)
2067 bheads = repo.branchheads(branch)
2068 if len(bheads) > 2:
2068 if len(bheads) > 2:
2069 raise util.Abort(_("branch '%s' has %d heads - "
2069 raise util.Abort(_("branch '%s' has %d heads - "
2070 "please merge with an explicit rev") %
2070 "please merge with an explicit rev") %
2071 (branch, len(bheads)))
2071 (branch, len(bheads)))
2072
2072
2073 parent = repo.dirstate.parents()[0]
2073 parent = repo.dirstate.parents()[0]
2074 if len(bheads) == 1:
2074 if len(bheads) == 1:
2075 if len(repo.heads()) > 1:
2075 if len(repo.heads()) > 1:
2076 raise util.Abort(_("branch '%s' has one head - "
2076 raise util.Abort(_("branch '%s' has one head - "
2077 "please merge with an explicit rev") %
2077 "please merge with an explicit rev") %
2078 branch)
2078 branch)
2079 msg = _('there is nothing to merge')
2079 msg = _('there is nothing to merge')
2080 if parent != repo.lookup(repo[None].branch()):
2080 if parent != repo.lookup(repo[None].branch()):
2081 msg = _('%s - use "hg update" instead') % msg
2081 msg = _('%s - use "hg update" instead') % msg
2082 raise util.Abort(msg)
2082 raise util.Abort(msg)
2083
2083
2084 if parent not in bheads:
2084 if parent not in bheads:
2085 raise util.Abort(_('working dir not at a head rev - '
2085 raise util.Abort(_('working dir not at a head rev - '
2086 'use "hg update" or merge with an explicit rev'))
2086 'use "hg update" or merge with an explicit rev'))
2087 node = parent == bheads[0] and bheads[-1] or bheads[0]
2087 node = parent == bheads[0] and bheads[-1] or bheads[0]
2088
2088
2089 if opts.get('show'):
2089 if opts.get('show'):
2090 p1 = repo['.']
2090 p1 = repo['.']
2091 p2 = repo[node]
2091 p2 = repo[node]
2092 common = p1.ancestor(p2)
2092 common = p1.ancestor(p2)
2093 roots, heads = [common.node()], [p2.node()]
2093 roots, heads = [common.node()], [p2.node()]
2094 displayer = cmdutil.show_changeset(ui, repo, opts)
2094 displayer = cmdutil.show_changeset(ui, repo, opts)
2095 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2095 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2096 displayer.show(repo[node])
2096 displayer.show(repo[node])
2097 return 0
2097 return 0
2098
2098
2099 return hg.merge(repo, node, force=opts.get('force'))
2099 return hg.merge(repo, node, force=opts.get('force'))
2100
2100
2101 def outgoing(ui, repo, dest=None, **opts):
2101 def outgoing(ui, repo, dest=None, **opts):
2102 """show changesets not found in destination
2102 """show changesets not found in destination
2103
2103
2104 Show changesets not found in the specified destination repository
2104 Show changesets not found in the specified destination repository
2105 or the default push location. These are the changesets that would
2105 or the default push location. These are the changesets that would
2106 be pushed if a push was requested.
2106 be pushed if a push was requested.
2107
2107
2108 See pull for valid destination format details.
2108 See pull for valid destination format details.
2109 """
2109 """
2110 limit = cmdutil.loglimit(opts)
2110 limit = cmdutil.loglimit(opts)
2111 dest, revs, checkout = hg.parseurl(
2111 dest, revs, checkout = hg.parseurl(
2112 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2112 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2113 if revs:
2113 if revs:
2114 revs = [repo.lookup(rev) for rev in revs]
2114 revs = [repo.lookup(rev) for rev in revs]
2115
2115
2116 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2116 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2117 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2117 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2118 o = repo.findoutgoing(other, force=opts.get('force'))
2118 o = repo.findoutgoing(other, force=opts.get('force'))
2119 if not o:
2119 if not o:
2120 ui.status(_("no changes found\n"))
2120 ui.status(_("no changes found\n"))
2121 return 1
2121 return 1
2122 o = repo.changelog.nodesbetween(o, revs)[0]
2122 o = repo.changelog.nodesbetween(o, revs)[0]
2123 if opts.get('newest_first'):
2123 if opts.get('newest_first'):
2124 o.reverse()
2124 o.reverse()
2125 displayer = cmdutil.show_changeset(ui, repo, opts)
2125 displayer = cmdutil.show_changeset(ui, repo, opts)
2126 count = 0
2126 count = 0
2127 for n in o:
2127 for n in o:
2128 if count >= limit:
2128 if count >= limit:
2129 break
2129 break
2130 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2130 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2131 if opts.get('no_merges') and len(parents) == 2:
2131 if opts.get('no_merges') and len(parents) == 2:
2132 continue
2132 continue
2133 count += 1
2133 count += 1
2134 displayer.show(repo[n])
2134 displayer.show(repo[n])
2135
2135
2136 def parents(ui, repo, file_=None, **opts):
2136 def parents(ui, repo, file_=None, **opts):
2137 """show the parents of the working directory or revision
2137 """show the parents of the working directory or revision
2138
2138
2139 Print the working directory's parent revisions. If a revision is
2139 Print the working directory's parent revisions. If a revision is
2140 given via -r/--rev, the parent of that revision will be printed.
2140 given via -r/--rev, the parent of that revision will be printed.
2141 If a file argument is given, revision in which the file was last
2141 If a file argument is given, revision in which the file was last
2142 changed (before the working directory revision or the argument to
2142 changed (before the working directory revision or the argument to
2143 --rev if given) is printed.
2143 --rev if given) is printed.
2144 """
2144 """
2145 rev = opts.get('rev')
2145 rev = opts.get('rev')
2146 if rev:
2146 if rev:
2147 ctx = repo[rev]
2147 ctx = repo[rev]
2148 else:
2148 else:
2149 ctx = repo[None]
2149 ctx = repo[None]
2150
2150
2151 if file_:
2151 if file_:
2152 m = cmdutil.match(repo, (file_,), opts)
2152 m = cmdutil.match(repo, (file_,), opts)
2153 if m.anypats() or len(m.files()) != 1:
2153 if m.anypats() or len(m.files()) != 1:
2154 raise util.Abort(_('can only specify an explicit file name'))
2154 raise util.Abort(_('can only specify an explicit file name'))
2155 file_ = m.files()[0]
2155 file_ = m.files()[0]
2156 filenodes = []
2156 filenodes = []
2157 for cp in ctx.parents():
2157 for cp in ctx.parents():
2158 if not cp:
2158 if not cp:
2159 continue
2159 continue
2160 try:
2160 try:
2161 filenodes.append(cp.filenode(file_))
2161 filenodes.append(cp.filenode(file_))
2162 except error.LookupError:
2162 except error.LookupError:
2163 pass
2163 pass
2164 if not filenodes:
2164 if not filenodes:
2165 raise util.Abort(_("'%s' not found in manifest!") % file_)
2165 raise util.Abort(_("'%s' not found in manifest!") % file_)
2166 fl = repo.file(file_)
2166 fl = repo.file(file_)
2167 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2167 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2168 else:
2168 else:
2169 p = [cp.node() for cp in ctx.parents()]
2169 p = [cp.node() for cp in ctx.parents()]
2170
2170
2171 displayer = cmdutil.show_changeset(ui, repo, opts)
2171 displayer = cmdutil.show_changeset(ui, repo, opts)
2172 for n in p:
2172 for n in p:
2173 if n != nullid:
2173 if n != nullid:
2174 displayer.show(repo[n])
2174 displayer.show(repo[n])
2175
2175
2176 def paths(ui, repo, search=None):
2176 def paths(ui, repo, search=None):
2177 """show aliases for remote repositories
2177 """show aliases for remote repositories
2178
2178
2179 Show definition of symbolic path name NAME. If no name is given,
2179 Show definition of symbolic path name NAME. If no name is given,
2180 show definition of available names.
2180 show definition of available names.
2181
2181
2182 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2182 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2183 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2183 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2184
2184
2185 See 'hg help urls' for more information.
2185 See 'hg help urls' for more information.
2186 """
2186 """
2187 if search:
2187 if search:
2188 for name, path in ui.configitems("paths"):
2188 for name, path in ui.configitems("paths"):
2189 if name == search:
2189 if name == search:
2190 ui.write("%s\n" % url.hidepassword(path))
2190 ui.write("%s\n" % url.hidepassword(path))
2191 return
2191 return
2192 ui.warn(_("not found!\n"))
2192 ui.warn(_("not found!\n"))
2193 return 1
2193 return 1
2194 else:
2194 else:
2195 for name, path in ui.configitems("paths"):
2195 for name, path in ui.configitems("paths"):
2196 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2196 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2197
2197
2198 def postincoming(ui, repo, modheads, optupdate, checkout):
2198 def postincoming(ui, repo, modheads, optupdate, checkout):
2199 if modheads == 0:
2199 if modheads == 0:
2200 return
2200 return
2201 if optupdate:
2201 if optupdate:
2202 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2202 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2203 return hg.update(repo, checkout)
2203 return hg.update(repo, checkout)
2204 else:
2204 else:
2205 ui.status(_("not updating, since new heads added\n"))
2205 ui.status(_("not updating, since new heads added\n"))
2206 if modheads > 1:
2206 if modheads > 1:
2207 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2207 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2208 else:
2208 else:
2209 ui.status(_("(run 'hg update' to get a working copy)\n"))
2209 ui.status(_("(run 'hg update' to get a working copy)\n"))
2210
2210
2211 def pull(ui, repo, source="default", **opts):
2211 def pull(ui, repo, source="default", **opts):
2212 """pull changes from the specified source
2212 """pull changes from the specified source
2213
2213
2214 Pull changes from a remote repository to the local one.
2214 Pull changes from a remote repository to the local one.
2215
2215
2216 This finds all changes from the repository at the specified path
2216 This finds all changes from the repository at the specified path
2217 or URL and adds them to the local repository. By default, this
2217 or URL and adds them to the local repository. By default, this
2218 does not update the copy of the project in the working directory.
2218 does not update the copy of the project in the working directory.
2219
2219
2220 Use hg incoming if you want to see what will be added by the next
2220 Use hg incoming if you want to see what will be added by the next
2221 pull without actually adding the changes to the repository.
2221 pull without actually adding the changes to the repository.
2222
2222
2223 If SOURCE is omitted, the 'default' path will be used.
2223 If SOURCE is omitted, the 'default' path will be used.
2224 See 'hg help urls' for more information.
2224 See 'hg help urls' for more information.
2225 """
2225 """
2226 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2226 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2227 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2227 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2228 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2228 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2229 if revs:
2229 if revs:
2230 try:
2230 try:
2231 revs = [other.lookup(rev) for rev in revs]
2231 revs = [other.lookup(rev) for rev in revs]
2232 except error.CapabilityError:
2232 except error.CapabilityError:
2233 err = _("Other repository doesn't support revision lookup, "
2233 err = _("Other repository doesn't support revision lookup, "
2234 "so a rev cannot be specified.")
2234 "so a rev cannot be specified.")
2235 raise util.Abort(err)
2235 raise util.Abort(err)
2236
2236
2237 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2237 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2238 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2238 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2239
2239
2240 def push(ui, repo, dest=None, **opts):
2240 def push(ui, repo, dest=None, **opts):
2241 """push changes to the specified destination
2241 """push changes to the specified destination
2242
2242
2243 Push changes from the local repository to the given destination.
2243 Push changes from the local repository to the given destination.
2244
2244
2245 This is the symmetrical operation for pull. It moves changes from
2245 This is the symmetrical operation for pull. It moves changes from
2246 the current repository to a different one. If the destination is
2246 the current repository to a different one. If the destination is
2247 local this is identical to a pull in that directory from the
2247 local this is identical to a pull in that directory from the
2248 current one.
2248 current one.
2249
2249
2250 By default, push will refuse to run if it detects the result would
2250 By default, push will refuse to run if it detects the result would
2251 increase the number of remote heads. This generally indicates the
2251 increase the number of remote heads. This generally indicates the
2252 the client has forgotten to pull and merge before pushing.
2252 the client has forgotten to pull and merge before pushing.
2253
2253
2254 If -r/--rev is used, the named revision and all its ancestors will
2254 If -r/--rev is used, the named revision and all its ancestors will
2255 be pushed to the remote repository.
2255 be pushed to the remote repository.
2256
2256
2257 Look at the help text for URLs for important details about ssh://
2257 Look at the help text for URLs for important details about ssh://
2258 URLs. If DESTINATION is omitted, a default path will be used.
2258 URLs. If DESTINATION is omitted, a default path will be used.
2259 See 'hg help urls' for more information.
2259 See 'hg help urls' for more information.
2260 """
2260 """
2261 dest, revs, checkout = hg.parseurl(
2261 dest, revs, checkout = hg.parseurl(
2262 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2262 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2263 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2263 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2264 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2264 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2265 if revs:
2265 if revs:
2266 revs = [repo.lookup(rev) for rev in revs]
2266 revs = [repo.lookup(rev) for rev in revs]
2267 r = repo.push(other, opts.get('force'), revs=revs)
2267 r = repo.push(other, opts.get('force'), revs=revs)
2268 return r == 0
2268 return r == 0
2269
2269
2270 def recover(ui, repo):
2270 def recover(ui, repo):
2271 """roll back an interrupted transaction
2271 """roll back an interrupted transaction
2272
2272
2273 Recover from an interrupted commit or pull.
2273 Recover from an interrupted commit or pull.
2274
2274
2275 This command tries to fix the repository status after an
2275 This command tries to fix the repository status after an
2276 interrupted operation. It should only be necessary when Mercurial
2276 interrupted operation. It should only be necessary when Mercurial
2277 suggests it.
2277 suggests it.
2278 """
2278 """
2279 if repo.recover():
2279 if repo.recover():
2280 return hg.verify(repo)
2280 return hg.verify(repo)
2281 return 1
2281 return 1
2282
2282
2283 def remove(ui, repo, *pats, **opts):
2283 def remove(ui, repo, *pats, **opts):
2284 """remove the specified files on the next commit
2284 """remove the specified files on the next commit
2285
2285
2286 Schedule the indicated files for removal from the repository.
2286 Schedule the indicated files for removal from the repository.
2287
2287
2288 This only removes files from the current branch, not from the
2288 This only removes files from the current branch, not from the
2289 entire project history. -A/--after can be used to remove only
2289 entire project history. -A/--after can be used to remove only
2290 files that have already been deleted, -f/--force can be used to
2290 files that have already been deleted, -f/--force can be used to
2291 force deletion, and -Af can be used to remove files from the next
2291 force deletion, and -Af can be used to remove files from the next
2292 revision without deleting them.
2292 revision without deleting them.
2293
2293
2294 The following table details the behavior of remove for different
2294 The following table details the behavior of remove for different
2295 file states (columns) and option combinations (rows). The file
2295 file states (columns) and option combinations (rows). The file
2296 states are Added, Clean, Modified and Missing (as reported by hg
2296 states are Added, Clean, Modified and Missing (as reported by hg
2297 status). The actions are Warn, Remove (from branch) and Delete
2297 status). The actions are Warn, Remove (from branch) and Delete
2298 (from disk).
2298 (from disk).
2299
2299
2300 A C M !
2300 A C M !
2301 none W RD W R
2301 none W RD W R
2302 -f R RD RD R
2302 -f R RD RD R
2303 -A W W W R
2303 -A W W W R
2304 -Af R R R R
2304 -Af R R R R
2305
2305
2306 This command schedules the files to be removed at the next commit.
2306 This command schedules the files to be removed at the next commit.
2307 To undo a remove before that, see hg revert.
2307 To undo a remove before that, see hg revert.
2308 """
2308 """
2309
2309
2310 after, force = opts.get('after'), opts.get('force')
2310 after, force = opts.get('after'), opts.get('force')
2311 if not pats and not after:
2311 if not pats and not after:
2312 raise util.Abort(_('no files specified'))
2312 raise util.Abort(_('no files specified'))
2313
2313
2314 m = cmdutil.match(repo, pats, opts)
2314 m = cmdutil.match(repo, pats, opts)
2315 s = repo.status(match=m, clean=True)
2315 s = repo.status(match=m, clean=True)
2316 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2316 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2317
2317
2318 for f in m.files():
2318 for f in m.files():
2319 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2319 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2320 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2320 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2321
2321
2322 def warn(files, reason):
2322 def warn(files, reason):
2323 for f in files:
2323 for f in files:
2324 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2324 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2325 % (m.rel(f), reason))
2325 % (m.rel(f), reason))
2326
2326
2327 if force:
2327 if force:
2328 remove, forget = modified + deleted + clean, added
2328 remove, forget = modified + deleted + clean, added
2329 elif after:
2329 elif after:
2330 remove, forget = deleted, []
2330 remove, forget = deleted, []
2331 warn(modified + added + clean, _('still exists'))
2331 warn(modified + added + clean, _('still exists'))
2332 else:
2332 else:
2333 remove, forget = deleted + clean, []
2333 remove, forget = deleted + clean, []
2334 warn(modified, _('is modified'))
2334 warn(modified, _('is modified'))
2335 warn(added, _('has been marked for add'))
2335 warn(added, _('has been marked for add'))
2336
2336
2337 for f in sorted(remove + forget):
2337 for f in sorted(remove + forget):
2338 if ui.verbose or not m.exact(f):
2338 if ui.verbose or not m.exact(f):
2339 ui.status(_('removing %s\n') % m.rel(f))
2339 ui.status(_('removing %s\n') % m.rel(f))
2340
2340
2341 repo.forget(forget)
2341 repo.forget(forget)
2342 repo.remove(remove, unlink=not after)
2342 repo.remove(remove, unlink=not after)
2343
2343
2344 def rename(ui, repo, *pats, **opts):
2344 def rename(ui, repo, *pats, **opts):
2345 """rename files; equivalent of copy + remove
2345 """rename files; equivalent of copy + remove
2346
2346
2347 Mark dest as copies of sources; mark sources for deletion. If dest
2347 Mark dest as copies of sources; mark sources for deletion. If dest
2348 is a directory, copies are put in that directory. If dest is a
2348 is a directory, copies are put in that directory. If dest is a
2349 file, there can only be one source.
2349 file, there can only be one source.
2350
2350
2351 By default, this command copies the contents of files as they
2351 By default, this command copies the contents of files as they
2352 exist in the working directory. If invoked with -A/--after, the
2352 exist in the working directory. If invoked with -A/--after, the
2353 operation is recorded, but no copying is performed.
2353 operation is recorded, but no copying is performed.
2354
2354
2355 This command takes effect at the next commit. To undo a rename
2355 This command takes effect at the next commit. To undo a rename
2356 before that, see hg revert.
2356 before that, see hg revert.
2357 """
2357 """
2358 wlock = repo.wlock(False)
2358 wlock = repo.wlock(False)
2359 try:
2359 try:
2360 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2360 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2361 finally:
2361 finally:
2362 wlock.release()
2362 wlock.release()
2363
2363
2364 def resolve(ui, repo, *pats, **opts):
2364 def resolve(ui, repo, *pats, **opts):
2365 """retry file merges from a merge or update
2365 """retry file merges from a merge or update
2366
2366
2367 This command will cleanly retry unresolved file merges using file
2367 This command will cleanly retry unresolved file merges using file
2368 revisions preserved from the last update or merge. To attempt to
2368 revisions preserved from the last update or merge. To attempt to
2369 resolve all unresolved files, use the -a/--all switch.
2369 resolve all unresolved files, use the -a/--all switch.
2370
2370
2371 If a conflict is resolved manually, please note that the changes
2371 If a conflict is resolved manually, please note that the changes
2372 will be overwritten if the merge is retried with resolve. The
2372 will be overwritten if the merge is retried with resolve. The
2373 -m/--mark switch should be used to mark the file as resolved.
2373 -m/--mark switch should be used to mark the file as resolved.
2374
2374
2375 This command will also allow listing resolved files and manually
2375 This command will also allow listing resolved files and manually
2376 marking and unmarking files as resolved. All files must be marked
2376 marking and unmarking files as resolved. All files must be marked
2377 as resolved before the new commits are permitted.
2377 as resolved before the new commits are permitted.
2378
2378
2379 The codes used to show the status of files are:
2379 The codes used to show the status of files are:
2380 U = unresolved
2380 U = unresolved
2381 R = resolved
2381 R = resolved
2382 """
2382 """
2383
2383
2384 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2384 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2385
2385
2386 if (show and (mark or unmark)) or (mark and unmark):
2386 if (show and (mark or unmark)) or (mark and unmark):
2387 raise util.Abort(_("too many options specified"))
2387 raise util.Abort(_("too many options specified"))
2388 if pats and all:
2388 if pats and all:
2389 raise util.Abort(_("can't specify --all and patterns"))
2389 raise util.Abort(_("can't specify --all and patterns"))
2390 if not (all or pats or show or mark or unmark):
2390 if not (all or pats or show or mark or unmark):
2391 raise util.Abort(_('no files or directories specified; '
2391 raise util.Abort(_('no files or directories specified; '
2392 'use --all to remerge all files'))
2392 'use --all to remerge all files'))
2393
2393
2394 ms = merge_.mergestate(repo)
2394 ms = merge_.mergestate(repo)
2395 m = cmdutil.match(repo, pats, opts)
2395 m = cmdutil.match(repo, pats, opts)
2396
2396
2397 for f in ms:
2397 for f in ms:
2398 if m(f):
2398 if m(f):
2399 if show:
2399 if show:
2400 ui.write("%s %s\n" % (ms[f].upper(), f))
2400 ui.write("%s %s\n" % (ms[f].upper(), f))
2401 elif mark:
2401 elif mark:
2402 ms.mark(f, "r")
2402 ms.mark(f, "r")
2403 elif unmark:
2403 elif unmark:
2404 ms.mark(f, "u")
2404 ms.mark(f, "u")
2405 else:
2405 else:
2406 wctx = repo[None]
2406 wctx = repo[None]
2407 mctx = wctx.parents()[-1]
2407 mctx = wctx.parents()[-1]
2408
2408
2409 # backup pre-resolve (merge uses .orig for its own purposes)
2409 # backup pre-resolve (merge uses .orig for its own purposes)
2410 a = repo.wjoin(f)
2410 a = repo.wjoin(f)
2411 util.copyfile(a, a + ".resolve")
2411 util.copyfile(a, a + ".resolve")
2412
2412
2413 # resolve file
2413 # resolve file
2414 ms.resolve(f, wctx, mctx)
2414 ms.resolve(f, wctx, mctx)
2415
2415
2416 # replace filemerge's .orig file with our resolve file
2416 # replace filemerge's .orig file with our resolve file
2417 util.rename(a + ".resolve", a + ".orig")
2417 util.rename(a + ".resolve", a + ".orig")
2418
2418
2419 def revert(ui, repo, *pats, **opts):
2419 def revert(ui, repo, *pats, **opts):
2420 """restore individual files or directories to an earlier state
2420 """restore individual files or directories to an earlier state
2421
2421
2422 (Use update -r to check out earlier revisions, revert does not
2422 (Use update -r to check out earlier revisions, revert does not
2423 change the working directory parents.)
2423 change the working directory parents.)
2424
2424
2425 With no revision specified, revert the named files or directories
2425 With no revision specified, revert the named files or directories
2426 to the contents they had in the parent of the working directory.
2426 to the contents they had in the parent of the working directory.
2427 This restores the contents of the affected files to an unmodified
2427 This restores the contents of the affected files to an unmodified
2428 state and unschedules adds, removes, copies, and renames. If the
2428 state and unschedules adds, removes, copies, and renames. If the
2429 working directory has two parents, you must explicitly specify the
2429 working directory has two parents, you must explicitly specify the
2430 revision to revert to.
2430 revision to revert to.
2431
2431
2432 Using the -r/--rev option, revert the given files or directories
2432 Using the -r/--rev option, revert the given files or directories
2433 to their contents as of a specific revision. This can be helpful
2433 to their contents as of a specific revision. This can be helpful
2434 to "roll back" some or all of an earlier change. See 'hg help
2434 to "roll back" some or all of an earlier change. See 'hg help
2435 dates' for a list of formats valid for -d/--date.
2435 dates' for a list of formats valid for -d/--date.
2436
2436
2437 Revert modifies the working directory. It does not commit any
2437 Revert modifies the working directory. It does not commit any
2438 changes, or change the parent of the working directory. If you
2438 changes, or change the parent of the working directory. If you
2439 revert to a revision other than the parent of the working
2439 revert to a revision other than the parent of the working
2440 directory, the reverted files will thus appear modified
2440 directory, the reverted files will thus appear modified
2441 afterwards.
2441 afterwards.
2442
2442
2443 If a file has been deleted, it is restored. If the executable mode
2443 If a file has been deleted, it is restored. If the executable mode
2444 of a file was changed, it is reset.
2444 of a file was changed, it is reset.
2445
2445
2446 If names are given, all files matching the names are reverted.
2446 If names are given, all files matching the names are reverted.
2447 If no arguments are given, no files are reverted.
2447 If no arguments are given, no files are reverted.
2448
2448
2449 Modified files are saved with a .orig suffix before reverting.
2449 Modified files are saved with a .orig suffix before reverting.
2450 To disable these backups, use --no-backup.
2450 To disable these backups, use --no-backup.
2451 """
2451 """
2452
2452
2453 if opts["date"]:
2453 if opts["date"]:
2454 if opts["rev"]:
2454 if opts["rev"]:
2455 raise util.Abort(_("you can't specify a revision and a date"))
2455 raise util.Abort(_("you can't specify a revision and a date"))
2456 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2456 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2457
2457
2458 if not pats and not opts.get('all'):
2458 if not pats and not opts.get('all'):
2459 raise util.Abort(_('no files or directories specified; '
2459 raise util.Abort(_('no files or directories specified; '
2460 'use --all to revert the whole repo'))
2460 'use --all to revert the whole repo'))
2461
2461
2462 parent, p2 = repo.dirstate.parents()
2462 parent, p2 = repo.dirstate.parents()
2463 if not opts.get('rev') and p2 != nullid:
2463 if not opts.get('rev') and p2 != nullid:
2464 raise util.Abort(_('uncommitted merge - please provide a '
2464 raise util.Abort(_('uncommitted merge - please provide a '
2465 'specific revision'))
2465 'specific revision'))
2466 ctx = repo[opts.get('rev')]
2466 ctx = repo[opts.get('rev')]
2467 node = ctx.node()
2467 node = ctx.node()
2468 mf = ctx.manifest()
2468 mf = ctx.manifest()
2469 if node == parent:
2469 if node == parent:
2470 pmf = mf
2470 pmf = mf
2471 else:
2471 else:
2472 pmf = None
2472 pmf = None
2473
2473
2474 # need all matching names in dirstate and manifest of target rev,
2474 # need all matching names in dirstate and manifest of target rev,
2475 # so have to walk both. do not print errors if files exist in one
2475 # so have to walk both. do not print errors if files exist in one
2476 # but not other.
2476 # but not other.
2477
2477
2478 names = {}
2478 names = {}
2479
2479
2480 wlock = repo.wlock()
2480 wlock = repo.wlock()
2481 try:
2481 try:
2482 # walk dirstate.
2482 # walk dirstate.
2483
2483
2484 m = cmdutil.match(repo, pats, opts)
2484 m = cmdutil.match(repo, pats, opts)
2485 m.bad = lambda x,y: False
2485 m.bad = lambda x,y: False
2486 for abs in repo.walk(m):
2486 for abs in repo.walk(m):
2487 names[abs] = m.rel(abs), m.exact(abs)
2487 names[abs] = m.rel(abs), m.exact(abs)
2488
2488
2489 # walk target manifest.
2489 # walk target manifest.
2490
2490
2491 def badfn(path, msg):
2491 def badfn(path, msg):
2492 if path in names:
2492 if path in names:
2493 return
2493 return
2494 path_ = path + '/'
2494 path_ = path + '/'
2495 for f in names:
2495 for f in names:
2496 if f.startswith(path_):
2496 if f.startswith(path_):
2497 return
2497 return
2498 ui.warn("%s: %s\n" % (m.rel(path), msg))
2498 ui.warn("%s: %s\n" % (m.rel(path), msg))
2499
2499
2500 m = cmdutil.match(repo, pats, opts)
2500 m = cmdutil.match(repo, pats, opts)
2501 m.bad = badfn
2501 m.bad = badfn
2502 for abs in repo[node].walk(m):
2502 for abs in repo[node].walk(m):
2503 if abs not in names:
2503 if abs not in names:
2504 names[abs] = m.rel(abs), m.exact(abs)
2504 names[abs] = m.rel(abs), m.exact(abs)
2505
2505
2506 m = cmdutil.matchfiles(repo, names)
2506 m = cmdutil.matchfiles(repo, names)
2507 changes = repo.status(match=m)[:4]
2507 changes = repo.status(match=m)[:4]
2508 modified, added, removed, deleted = map(set, changes)
2508 modified, added, removed, deleted = map(set, changes)
2509
2509
2510 # if f is a rename, also revert the source
2510 # if f is a rename, also revert the source
2511 cwd = repo.getcwd()
2511 cwd = repo.getcwd()
2512 for f in added:
2512 for f in added:
2513 src = repo.dirstate.copied(f)
2513 src = repo.dirstate.copied(f)
2514 if src and src not in names and repo.dirstate[src] == 'r':
2514 if src and src not in names and repo.dirstate[src] == 'r':
2515 removed.add(src)
2515 removed.add(src)
2516 names[src] = (repo.pathto(src, cwd), True)
2516 names[src] = (repo.pathto(src, cwd), True)
2517
2517
2518 def removeforget(abs):
2518 def removeforget(abs):
2519 if repo.dirstate[abs] == 'a':
2519 if repo.dirstate[abs] == 'a':
2520 return _('forgetting %s\n')
2520 return _('forgetting %s\n')
2521 return _('removing %s\n')
2521 return _('removing %s\n')
2522
2522
2523 revert = ([], _('reverting %s\n'))
2523 revert = ([], _('reverting %s\n'))
2524 add = ([], _('adding %s\n'))
2524 add = ([], _('adding %s\n'))
2525 remove = ([], removeforget)
2525 remove = ([], removeforget)
2526 undelete = ([], _('undeleting %s\n'))
2526 undelete = ([], _('undeleting %s\n'))
2527
2527
2528 disptable = (
2528 disptable = (
2529 # dispatch table:
2529 # dispatch table:
2530 # file state
2530 # file state
2531 # action if in target manifest
2531 # action if in target manifest
2532 # action if not in target manifest
2532 # action if not in target manifest
2533 # make backup if in target manifest
2533 # make backup if in target manifest
2534 # make backup if not in target manifest
2534 # make backup if not in target manifest
2535 (modified, revert, remove, True, True),
2535 (modified, revert, remove, True, True),
2536 (added, revert, remove, True, False),
2536 (added, revert, remove, True, False),
2537 (removed, undelete, None, False, False),
2537 (removed, undelete, None, False, False),
2538 (deleted, revert, remove, False, False),
2538 (deleted, revert, remove, False, False),
2539 )
2539 )
2540
2540
2541 for abs, (rel, exact) in sorted(names.items()):
2541 for abs, (rel, exact) in sorted(names.items()):
2542 mfentry = mf.get(abs)
2542 mfentry = mf.get(abs)
2543 target = repo.wjoin(abs)
2543 target = repo.wjoin(abs)
2544 def handle(xlist, dobackup):
2544 def handle(xlist, dobackup):
2545 xlist[0].append(abs)
2545 xlist[0].append(abs)
2546 if dobackup and not opts.get('no_backup') and util.lexists(target):
2546 if dobackup and not opts.get('no_backup') and util.lexists(target):
2547 bakname = "%s.orig" % rel
2547 bakname = "%s.orig" % rel
2548 ui.note(_('saving current version of %s as %s\n') %
2548 ui.note(_('saving current version of %s as %s\n') %
2549 (rel, bakname))
2549 (rel, bakname))
2550 if not opts.get('dry_run'):
2550 if not opts.get('dry_run'):
2551 util.copyfile(target, bakname)
2551 util.copyfile(target, bakname)
2552 if ui.verbose or not exact:
2552 if ui.verbose or not exact:
2553 msg = xlist[1]
2553 msg = xlist[1]
2554 if not isinstance(msg, basestring):
2554 if not isinstance(msg, basestring):
2555 msg = msg(abs)
2555 msg = msg(abs)
2556 ui.status(msg % rel)
2556 ui.status(msg % rel)
2557 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2557 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2558 if abs not in table: continue
2558 if abs not in table: continue
2559 # file has changed in dirstate
2559 # file has changed in dirstate
2560 if mfentry:
2560 if mfentry:
2561 handle(hitlist, backuphit)
2561 handle(hitlist, backuphit)
2562 elif misslist is not None:
2562 elif misslist is not None:
2563 handle(misslist, backupmiss)
2563 handle(misslist, backupmiss)
2564 break
2564 break
2565 else:
2565 else:
2566 if abs not in repo.dirstate:
2566 if abs not in repo.dirstate:
2567 if mfentry:
2567 if mfentry:
2568 handle(add, True)
2568 handle(add, True)
2569 elif exact:
2569 elif exact:
2570 ui.warn(_('file not managed: %s\n') % rel)
2570 ui.warn(_('file not managed: %s\n') % rel)
2571 continue
2571 continue
2572 # file has not changed in dirstate
2572 # file has not changed in dirstate
2573 if node == parent:
2573 if node == parent:
2574 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2574 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2575 continue
2575 continue
2576 if pmf is None:
2576 if pmf is None:
2577 # only need parent manifest in this unlikely case,
2577 # only need parent manifest in this unlikely case,
2578 # so do not read by default
2578 # so do not read by default
2579 pmf = repo[parent].manifest()
2579 pmf = repo[parent].manifest()
2580 if abs in pmf:
2580 if abs in pmf:
2581 if mfentry:
2581 if mfentry:
2582 # if version of file is same in parent and target
2582 # if version of file is same in parent and target
2583 # manifests, do nothing
2583 # manifests, do nothing
2584 if (pmf[abs] != mfentry or
2584 if (pmf[abs] != mfentry or
2585 pmf.flags(abs) != mf.flags(abs)):
2585 pmf.flags(abs) != mf.flags(abs)):
2586 handle(revert, False)
2586 handle(revert, False)
2587 else:
2587 else:
2588 handle(remove, False)
2588 handle(remove, False)
2589
2589
2590 if not opts.get('dry_run'):
2590 if not opts.get('dry_run'):
2591 def checkout(f):
2591 def checkout(f):
2592 fc = ctx[f]
2592 fc = ctx[f]
2593 repo.wwrite(f, fc.data(), fc.flags())
2593 repo.wwrite(f, fc.data(), fc.flags())
2594
2594
2595 audit_path = util.path_auditor(repo.root)
2595 audit_path = util.path_auditor(repo.root)
2596 for f in remove[0]:
2596 for f in remove[0]:
2597 if repo.dirstate[f] == 'a':
2597 if repo.dirstate[f] == 'a':
2598 repo.dirstate.forget(f)
2598 repo.dirstate.forget(f)
2599 continue
2599 continue
2600 audit_path(f)
2600 audit_path(f)
2601 try:
2601 try:
2602 util.unlink(repo.wjoin(f))
2602 util.unlink(repo.wjoin(f))
2603 except OSError:
2603 except OSError:
2604 pass
2604 pass
2605 repo.dirstate.remove(f)
2605 repo.dirstate.remove(f)
2606
2606
2607 normal = None
2607 normal = None
2608 if node == parent:
2608 if node == parent:
2609 # We're reverting to our parent. If possible, we'd like status
2609 # We're reverting to our parent. If possible, we'd like status
2610 # to report the file as clean. We have to use normallookup for
2610 # to report the file as clean. We have to use normallookup for
2611 # merges to avoid losing information about merged/dirty files.
2611 # merges to avoid losing information about merged/dirty files.
2612 if p2 != nullid:
2612 if p2 != nullid:
2613 normal = repo.dirstate.normallookup
2613 normal = repo.dirstate.normallookup
2614 else:
2614 else:
2615 normal = repo.dirstate.normal
2615 normal = repo.dirstate.normal
2616 for f in revert[0]:
2616 for f in revert[0]:
2617 checkout(f)
2617 checkout(f)
2618 if normal:
2618 if normal:
2619 normal(f)
2619 normal(f)
2620
2620
2621 for f in add[0]:
2621 for f in add[0]:
2622 checkout(f)
2622 checkout(f)
2623 repo.dirstate.add(f)
2623 repo.dirstate.add(f)
2624
2624
2625 normal = repo.dirstate.normallookup
2625 normal = repo.dirstate.normallookup
2626 if node == parent and p2 == nullid:
2626 if node == parent and p2 == nullid:
2627 normal = repo.dirstate.normal
2627 normal = repo.dirstate.normal
2628 for f in undelete[0]:
2628 for f in undelete[0]:
2629 checkout(f)
2629 checkout(f)
2630 normal(f)
2630 normal(f)
2631
2631
2632 finally:
2632 finally:
2633 wlock.release()
2633 wlock.release()
2634
2634
2635 def rollback(ui, repo):
2635 def rollback(ui, repo):
2636 """roll back the last transaction
2636 """roll back the last transaction
2637
2637
2638 This command should be used with care. There is only one level of
2638 This command should be used with care. There is only one level of
2639 rollback, and there is no way to undo a rollback. It will also
2639 rollback, and there is no way to undo a rollback. It will also
2640 restore the dirstate at the time of the last transaction, losing
2640 restore the dirstate at the time of the last transaction, losing
2641 any dirstate changes since that time.
2641 any dirstate changes since that time.
2642
2642
2643 Transactions are used to encapsulate the effects of all commands
2643 Transactions are used to encapsulate the effects of all commands
2644 that create new changesets or propagate existing changesets into a
2644 that create new changesets or propagate existing changesets into a
2645 repository. For example, the following commands are transactional,
2645 repository. For example, the following commands are transactional,
2646 and their effects can be rolled back:
2646 and their effects can be rolled back:
2647
2647
2648 commit
2648 commit
2649 import
2649 import
2650 pull
2650 pull
2651 push (with this repository as destination)
2651 push (with this repository as destination)
2652 unbundle
2652 unbundle
2653
2653
2654 This command is not intended for use on public repositories. Once
2654 This command is not intended for use on public repositories. Once
2655 changes are visible for pull by other users, rolling a transaction
2655 changes are visible for pull by other users, rolling a transaction
2656 back locally is ineffective (someone else may already have pulled
2656 back locally is ineffective (someone else may already have pulled
2657 the changes). Furthermore, a race is possible with readers of the
2657 the changes). Furthermore, a race is possible with readers of the
2658 repository; for example an in-progress pull from the repository
2658 repository; for example an in-progress pull from the repository
2659 may fail if a rollback is performed.
2659 may fail if a rollback is performed.
2660 """
2660 """
2661 repo.rollback()
2661 repo.rollback()
2662
2662
2663 def root(ui, repo):
2663 def root(ui, repo):
2664 """print the root (top) of the current working directory
2664 """print the root (top) of the current working directory
2665
2665
2666 Print the root directory of the current repository.
2666 Print the root directory of the current repository.
2667 """
2667 """
2668 ui.write(repo.root + "\n")
2668 ui.write(repo.root + "\n")
2669
2669
2670 def serve(ui, repo, **opts):
2670 def serve(ui, repo, **opts):
2671 """export the repository via HTTP
2671 """export the repository via HTTP
2672
2672
2673 Start a local HTTP repository browser and pull server.
2673 Start a local HTTP repository browser and pull server.
2674
2674
2675 By default, the server logs accesses to stdout and errors to
2675 By default, the server logs accesses to stdout and errors to
2676 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2676 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2677 files.
2677 files.
2678 """
2678 """
2679
2679
2680 if opts["stdio"]:
2680 if opts["stdio"]:
2681 if repo is None:
2681 if repo is None:
2682 raise error.RepoError(_("There is no Mercurial repository here"
2682 raise error.RepoError(_("There is no Mercurial repository here"
2683 " (.hg not found)"))
2683 " (.hg not found)"))
2684 s = sshserver.sshserver(ui, repo)
2684 s = sshserver.sshserver(ui, repo)
2685 s.serve_forever()
2685 s.serve_forever()
2686
2686
2687 baseui = repo and repo.baseui or ui
2687 baseui = repo and repo.baseui or ui
2688 optlist = ("name templates style address port prefix ipv6"
2688 optlist = ("name templates style address port prefix ipv6"
2689 " accesslog errorlog webdir_conf certificate")
2689 " accesslog errorlog webdir_conf certificate")
2690 for o in optlist.split():
2690 for o in optlist.split():
2691 if opts[o]:
2691 if opts[o]:
2692 baseui.setconfig("web", o, str(opts[o]))
2692 baseui.setconfig("web", o, str(opts[o]))
2693 if (repo is not None) and (repo.ui != baseui):
2693 if (repo is not None) and (repo.ui != baseui):
2694 repo.ui.setconfig("web", o, str(opts[o]))
2694 repo.ui.setconfig("web", o, str(opts[o]))
2695
2695
2696 if repo is None and not ui.config("web", "webdir_conf"):
2696 if repo is None and not ui.config("web", "webdir_conf"):
2697 raise error.RepoError(_("There is no Mercurial repository here"
2697 raise error.RepoError(_("There is no Mercurial repository here"
2698 " (.hg not found)"))
2698 " (.hg not found)"))
2699
2699
2700 class service:
2700 class service:
2701 def init(self):
2701 def init(self):
2702 util.set_signal_handler()
2702 util.set_signal_handler()
2703 self.httpd = server.create_server(baseui, repo)
2703 self.httpd = server.create_server(baseui, repo)
2704
2704
2705 if not ui.verbose: return
2705 if not ui.verbose: return
2706
2706
2707 if self.httpd.prefix:
2707 if self.httpd.prefix:
2708 prefix = self.httpd.prefix.strip('/') + '/'
2708 prefix = self.httpd.prefix.strip('/') + '/'
2709 else:
2709 else:
2710 prefix = ''
2710 prefix = ''
2711
2711
2712 port = ':%d' % self.httpd.port
2712 port = ':%d' % self.httpd.port
2713 if port == ':80':
2713 if port == ':80':
2714 port = ''
2714 port = ''
2715
2715
2716 bindaddr = self.httpd.addr
2716 bindaddr = self.httpd.addr
2717 if bindaddr == '0.0.0.0':
2717 if bindaddr == '0.0.0.0':
2718 bindaddr = '*'
2718 bindaddr = '*'
2719 elif ':' in bindaddr: # IPv6
2719 elif ':' in bindaddr: # IPv6
2720 bindaddr = '[%s]' % bindaddr
2720 bindaddr = '[%s]' % bindaddr
2721
2721
2722 fqaddr = self.httpd.fqaddr
2722 fqaddr = self.httpd.fqaddr
2723 if ':' in fqaddr:
2723 if ':' in fqaddr:
2724 fqaddr = '[%s]' % fqaddr
2724 fqaddr = '[%s]' % fqaddr
2725 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2725 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2726 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2726 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2727
2727
2728 def run(self):
2728 def run(self):
2729 self.httpd.serve_forever()
2729 self.httpd.serve_forever()
2730
2730
2731 service = service()
2731 service = service()
2732
2732
2733 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2733 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2734
2734
2735 def status(ui, repo, *pats, **opts):
2735 def status(ui, repo, *pats, **opts):
2736 """show changed files in the working directory
2736 """show changed files in the working directory
2737
2737
2738 Show status of files in the repository. If names are given, only
2738 Show status of files in the repository. If names are given, only
2739 files that match are shown. Files that are clean or ignored or
2739 files that match are shown. Files that are clean or ignored or
2740 source of a copy/move operation, are not listed unless -c/--clean,
2740 source of a copy/move operation, are not listed unless -c/--clean,
2741 -i/--ignored, -C/--copies or -A/--all is given. Unless options
2741 -i/--ignored, -C/--copies or -A/--all is given. Unless options
2742 described with "show only ..." are given, the options -mardu are
2742 described with "show only ..." are given, the options -mardu are
2743 used.
2743 used.
2744
2744
2745 Option -q/--quiet hides untracked (unknown and ignored) files
2745 Option -q/--quiet hides untracked (unknown and ignored) files
2746 unless explicitly requested with -u/--unknown or -i/--ignored.
2746 unless explicitly requested with -u/--unknown or -i/--ignored.
2747
2747
2748 NOTE: status may appear to disagree with diff if permissions have
2748 NOTE: status may appear to disagree with diff if permissions have
2749 changed or a merge has occurred. The standard diff format does not
2749 changed or a merge has occurred. The standard diff format does not
2750 report permission changes and diff only reports changes relative
2750 report permission changes and diff only reports changes relative
2751 to one merge parent.
2751 to one merge parent.
2752
2752
2753 If one revision is given, it is used as the base revision.
2753 If one revision is given, it is used as the base revision.
2754 If two revisions are given, the difference between them is shown.
2754 If two revisions are given, the difference between them is shown.
2755
2755
2756 The codes used to show the status of files are:
2756 The codes used to show the status of files are:
2757 M = modified
2757 M = modified
2758 A = added
2758 A = added
2759 R = removed
2759 R = removed
2760 C = clean
2760 C = clean
2761 ! = missing (deleted by non-hg command, but still tracked)
2761 ! = missing (deleted by non-hg command, but still tracked)
2762 ? = not tracked
2762 ? = not tracked
2763 I = ignored
2763 I = ignored
2764 = the previous added file was copied from here
2764 = the previous added file was copied from here
2765 """
2765 """
2766
2766
2767 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2767 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2768 cwd = (pats and repo.getcwd()) or ''
2768 cwd = (pats and repo.getcwd()) or ''
2769 end = opts.get('print0') and '\0' or '\n'
2769 end = opts.get('print0') and '\0' or '\n'
2770 copy = {}
2770 copy = {}
2771 states = 'modified added removed deleted unknown ignored clean'.split()
2771 states = 'modified added removed deleted unknown ignored clean'.split()
2772 show = [k for k in states if opts.get(k)]
2772 show = [k for k in states if opts.get(k)]
2773 if opts.get('all'):
2773 if opts.get('all'):
2774 show += ui.quiet and (states[:4] + ['clean']) or states
2774 show += ui.quiet and (states[:4] + ['clean']) or states
2775 if not show:
2775 if not show:
2776 show = ui.quiet and states[:4] or states[:5]
2776 show = ui.quiet and states[:4] or states[:5]
2777
2777
2778 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2778 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2779 'ignored' in show, 'clean' in show, 'unknown' in show)
2779 'ignored' in show, 'clean' in show, 'unknown' in show)
2780 changestates = zip(states, 'MAR!?IC', stat)
2780 changestates = zip(states, 'MAR!?IC', stat)
2781
2781
2782 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2782 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2783 ctxn = repo[nullid]
2783 ctxn = repo[nullid]
2784 ctx1 = repo[node1]
2784 ctx1 = repo[node1]
2785 ctx2 = repo[node2]
2785 ctx2 = repo[node2]
2786 added = stat[1]
2786 added = stat[1]
2787 if node2 is None:
2787 if node2 is None:
2788 added = stat[0] + stat[1] # merged?
2788 added = stat[0] + stat[1] # merged?
2789
2789
2790 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2790 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2791 if k in added:
2791 if k in added:
2792 copy[k] = v
2792 copy[k] = v
2793 elif v in added:
2793 elif v in added:
2794 copy[v] = k
2794 copy[v] = k
2795
2795
2796 for state, char, files in changestates:
2796 for state, char, files in changestates:
2797 if state in show:
2797 if state in show:
2798 format = "%s %%s%s" % (char, end)
2798 format = "%s %%s%s" % (char, end)
2799 if opts.get('no_status'):
2799 if opts.get('no_status'):
2800 format = "%%s%s" % end
2800 format = "%%s%s" % end
2801
2801
2802 for f in files:
2802 for f in files:
2803 ui.write(format % repo.pathto(f, cwd))
2803 ui.write(format % repo.pathto(f, cwd))
2804 if f in copy:
2804 if f in copy:
2805 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2805 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2806
2806
2807 def tag(ui, repo, name1, *names, **opts):
2807 def tag(ui, repo, name1, *names, **opts):
2808 """add one or more tags for the current or given revision
2808 """add one or more tags for the current or given revision
2809
2809
2810 Name a particular revision using <name>.
2810 Name a particular revision using <name>.
2811
2811
2812 Tags are used to name particular revisions of the repository and are
2812 Tags are used to name particular revisions of the repository and are
2813 very useful to compare different revisions, to go back to significant
2813 very useful to compare different revisions, to go back to significant
2814 earlier versions or to mark branch points as releases, etc.
2814 earlier versions or to mark branch points as releases, etc.
2815
2815
2816 If no revision is given, the parent of the working directory is
2816 If no revision is given, the parent of the working directory is
2817 used, or tip if no revision is checked out.
2817 used, or tip if no revision is checked out.
2818
2818
2819 To facilitate version control, distribution, and merging of tags,
2819 To facilitate version control, distribution, and merging of tags,
2820 they are stored as a file named ".hgtags" which is managed
2820 they are stored as a file named ".hgtags" which is managed
2821 similarly to other project files and can be hand-edited if
2821 similarly to other project files and can be hand-edited if
2822 necessary. The file '.hg/localtags' is used for local tags (not
2822 necessary. The file '.hg/localtags' is used for local tags (not
2823 shared among repositories).
2823 shared among repositories).
2824
2824
2825 See 'hg help dates' for a list of formats valid for -d/--date.
2825 See 'hg help dates' for a list of formats valid for -d/--date.
2826 """
2826 """
2827
2827
2828 rev_ = "."
2828 rev_ = "."
2829 names = (name1,) + names
2829 names = (name1,) + names
2830 if len(names) != len(set(names)):
2830 if len(names) != len(set(names)):
2831 raise util.Abort(_('tag names must be unique'))
2831 raise util.Abort(_('tag names must be unique'))
2832 for n in names:
2832 for n in names:
2833 if n in ['tip', '.', 'null']:
2833 if n in ['tip', '.', 'null']:
2834 raise util.Abort(_('the name \'%s\' is reserved') % n)
2834 raise util.Abort(_('the name \'%s\' is reserved') % n)
2835 if opts.get('rev') and opts.get('remove'):
2835 if opts.get('rev') and opts.get('remove'):
2836 raise util.Abort(_("--rev and --remove are incompatible"))
2836 raise util.Abort(_("--rev and --remove are incompatible"))
2837 if opts.get('rev'):
2837 if opts.get('rev'):
2838 rev_ = opts['rev']
2838 rev_ = opts['rev']
2839 message = opts.get('message')
2839 message = opts.get('message')
2840 if opts.get('remove'):
2840 if opts.get('remove'):
2841 expectedtype = opts.get('local') and 'local' or 'global'
2841 expectedtype = opts.get('local') and 'local' or 'global'
2842 for n in names:
2842 for n in names:
2843 if not repo.tagtype(n):
2843 if not repo.tagtype(n):
2844 raise util.Abort(_('tag \'%s\' does not exist') % n)
2844 raise util.Abort(_('tag \'%s\' does not exist') % n)
2845 if repo.tagtype(n) != expectedtype:
2845 if repo.tagtype(n) != expectedtype:
2846 if expectedtype == 'global':
2846 if expectedtype == 'global':
2847 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
2847 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
2848 else:
2848 else:
2849 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
2849 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
2850 rev_ = nullid
2850 rev_ = nullid
2851 if not message:
2851 if not message:
2852 message = _('Removed tag %s') % ', '.join(names)
2852 message = _('Removed tag %s') % ', '.join(names)
2853 elif not opts.get('force'):
2853 elif not opts.get('force'):
2854 for n in names:
2854 for n in names:
2855 if n in repo.tags():
2855 if n in repo.tags():
2856 raise util.Abort(_('tag \'%s\' already exists '
2856 raise util.Abort(_('tag \'%s\' already exists '
2857 '(use -f to force)') % n)
2857 '(use -f to force)') % n)
2858 if not rev_ and repo.dirstate.parents()[1] != nullid:
2858 if not rev_ and repo.dirstate.parents()[1] != nullid:
2859 raise util.Abort(_('uncommitted merge - please provide a '
2859 raise util.Abort(_('uncommitted merge - please provide a '
2860 'specific revision'))
2860 'specific revision'))
2861 r = repo[rev_].node()
2861 r = repo[rev_].node()
2862
2862
2863 if not message:
2863 if not message:
2864 message = (_('Added tag %s for changeset %s') %
2864 message = (_('Added tag %s for changeset %s') %
2865 (', '.join(names), short(r)))
2865 (', '.join(names), short(r)))
2866
2866
2867 date = opts.get('date')
2867 date = opts.get('date')
2868 if date:
2868 if date:
2869 date = util.parsedate(date)
2869 date = util.parsedate(date)
2870
2870
2871 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2871 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2872
2872
2873 def tags(ui, repo):
2873 def tags(ui, repo):
2874 """list repository tags
2874 """list repository tags
2875
2875
2876 This lists both regular and local tags. When the -v/--verbose
2876 This lists both regular and local tags. When the -v/--verbose
2877 switch is used, a third column "local" is printed for local tags.
2877 switch is used, a third column "local" is printed for local tags.
2878 """
2878 """
2879
2879
2880 hexfunc = ui.debugflag and hex or short
2880 hexfunc = ui.debugflag and hex or short
2881 tagtype = ""
2881 tagtype = ""
2882
2882
2883 for t, n in reversed(repo.tagslist()):
2883 for t, n in reversed(repo.tagslist()):
2884 if ui.quiet:
2884 if ui.quiet:
2885 ui.write("%s\n" % t)
2885 ui.write("%s\n" % t)
2886 continue
2886 continue
2887
2887
2888 try:
2888 try:
2889 hn = hexfunc(n)
2889 hn = hexfunc(n)
2890 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2890 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2891 except error.LookupError:
2891 except error.LookupError:
2892 r = " ?:%s" % hn
2892 r = " ?:%s" % hn
2893 else:
2893 else:
2894 spaces = " " * (30 - encoding.colwidth(t))
2894 spaces = " " * (30 - encoding.colwidth(t))
2895 if ui.verbose:
2895 if ui.verbose:
2896 if repo.tagtype(t) == 'local':
2896 if repo.tagtype(t) == 'local':
2897 tagtype = " local"
2897 tagtype = " local"
2898 else:
2898 else:
2899 tagtype = ""
2899 tagtype = ""
2900 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2900 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2901
2901
2902 def tip(ui, repo, **opts):
2902 def tip(ui, repo, **opts):
2903 """show the tip revision
2903 """show the tip revision
2904
2904
2905 The tip revision (usually just called the tip) is the most
2905 The tip revision (usually just called the tip) is the most
2906 recently added changeset in the repository, the most recently
2906 recently added changeset in the repository, the most recently
2907 changed head.
2907 changed head.
2908
2908
2909 If you have just made a commit, that commit will be the tip. If
2909 If you have just made a commit, that commit will be the tip. If
2910 you have just pulled changes from another repository, the tip of
2910 you have just pulled changes from another repository, the tip of
2911 that repository becomes the current tip. The "tip" tag is special
2911 that repository becomes the current tip. The "tip" tag is special
2912 and cannot be renamed or assigned to a different changeset.
2912 and cannot be renamed or assigned to a different changeset.
2913 """
2913 """
2914 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2914 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2915
2915
2916 def unbundle(ui, repo, fname1, *fnames, **opts):
2916 def unbundle(ui, repo, fname1, *fnames, **opts):
2917 """apply one or more changegroup files
2917 """apply one or more changegroup files
2918
2918
2919 Apply one or more compressed changegroup files generated by the
2919 Apply one or more compressed changegroup files generated by the
2920 bundle command.
2920 bundle command.
2921 """
2921 """
2922 fnames = (fname1,) + fnames
2922 fnames = (fname1,) + fnames
2923
2923
2924 lock = repo.lock()
2924 lock = repo.lock()
2925 try:
2925 try:
2926 for fname in fnames:
2926 for fname in fnames:
2927 f = url.open(ui, fname)
2927 f = url.open(ui, fname)
2928 gen = changegroup.readbundle(f, fname)
2928 gen = changegroup.readbundle(f, fname)
2929 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2929 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2930 finally:
2930 finally:
2931 lock.release()
2931 lock.release()
2932
2932
2933 return postincoming(ui, repo, modheads, opts.get('update'), None)
2933 return postincoming(ui, repo, modheads, opts.get('update'), None)
2934
2934
2935 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2935 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2936 """update working directory
2936 """update working directory
2937
2937
2938 Update the repository's working directory to the specified
2938 Update the repository's working directory to the specified
2939 revision, or the tip of the current branch if none is specified.
2939 revision, or the tip of the current branch if none is specified.
2940 Use null as the revision to remove the working copy (like 'hg
2940 Use null as the revision to remove the working copy (like 'hg
2941 clone -U').
2941 clone -U').
2942
2942
2943 When the working directory contains no uncommitted changes, it
2943 When the working directory contains no uncommitted changes, it
2944 will be replaced by the state of the requested revision from the
2944 will be replaced by the state of the requested revision from the
2945 repository. When the requested revision is on a different branch,
2945 repository. When the requested revision is on a different branch,
2946 the working directory will additionally be switched to that
2946 the working directory will additionally be switched to that
2947 branch.
2947 branch.
2948
2948
2949 When there are uncommitted changes, use option -C/--clean to
2949 When there are uncommitted changes, use option -C/--clean to
2950 discard them, forcibly replacing the state of the working
2950 discard them, forcibly replacing the state of the working
2951 directory with the requested revision.
2951 directory with the requested revision.
2952
2952
2953 When there are uncommitted changes and option -C/--clean is not
2953 When there are uncommitted changes and option -C/--clean is not
2954 used, and the parent revision and requested revision are on the
2954 used, and the parent revision and requested revision are on the
2955 same branch, and one of them is an ancestor of the other, then the
2955 same branch, and one of them is an ancestor of the other, then the
2956 new working directory will contain the requested revision merged
2956 new working directory will contain the requested revision merged
2957 with the uncommitted changes. Otherwise, the update will fail with
2957 with the uncommitted changes. Otherwise, the update will fail with
2958 a suggestion to use 'merge' or 'update -C' instead.
2958 a suggestion to use 'merge' or 'update -C' instead.
2959
2959
2960 If you want to update just one file to an older revision, use
2960 If you want to update just one file to an older revision, use
2961 revert.
2961 revert.
2962
2962
2963 See 'hg help dates' for a list of formats valid for -d/--date.
2963 See 'hg help dates' for a list of formats valid for -d/--date.
2964 """
2964 """
2965 if rev and node:
2965 if rev and node:
2966 raise util.Abort(_("please specify just one revision"))
2966 raise util.Abort(_("please specify just one revision"))
2967
2967
2968 if not rev:
2968 if not rev:
2969 rev = node
2969 rev = node
2970
2970
2971 if date:
2971 if date:
2972 if rev:
2972 if rev:
2973 raise util.Abort(_("you can't specify a revision and a date"))
2973 raise util.Abort(_("you can't specify a revision and a date"))
2974 rev = cmdutil.finddate(ui, repo, date)
2974 rev = cmdutil.finddate(ui, repo, date)
2975
2975
2976 if clean:
2976 if clean:
2977 return hg.clean(repo, rev)
2977 return hg.clean(repo, rev)
2978 else:
2978 else:
2979 return hg.update(repo, rev)
2979 return hg.update(repo, rev)
2980
2980
2981 def verify(ui, repo):
2981 def verify(ui, repo):
2982 """verify the integrity of the repository
2982 """verify the integrity of the repository
2983
2983
2984 Verify the integrity of the current repository.
2984 Verify the integrity of the current repository.
2985
2985
2986 This will perform an extensive check of the repository's
2986 This will perform an extensive check of the repository's
2987 integrity, validating the hashes and checksums of each entry in
2987 integrity, validating the hashes and checksums of each entry in
2988 the changelog, manifest, and tracked files, as well as the
2988 the changelog, manifest, and tracked files, as well as the
2989 integrity of their crosslinks and indices.
2989 integrity of their crosslinks and indices.
2990 """
2990 """
2991 return hg.verify(repo)
2991 return hg.verify(repo)
2992
2992
2993 def version_(ui):
2993 def version_(ui):
2994 """output version and copyright information"""
2994 """output version and copyright information"""
2995 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2995 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2996 % util.version())
2996 % util.version())
2997 ui.status(_(
2997 ui.status(_(
2998 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
2998 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
2999 "This is free software; see the source for copying conditions. "
2999 "This is free software; see the source for copying conditions. "
3000 "There is NO\nwarranty; "
3000 "There is NO\nwarranty; "
3001 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3001 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3002 ))
3002 ))
3003
3003
3004 # Command options and aliases are listed here, alphabetically
3004 # Command options and aliases are listed here, alphabetically
3005
3005
3006 globalopts = [
3006 globalopts = [
3007 ('R', 'repository', '',
3007 ('R', 'repository', '',
3008 _('repository root directory or symbolic path name')),
3008 _('repository root directory or symbolic path name')),
3009 ('', 'cwd', '', _('change working directory')),
3009 ('', 'cwd', '', _('change working directory')),
3010 ('y', 'noninteractive', None,
3010 ('y', 'noninteractive', None,
3011 _('do not prompt, assume \'yes\' for any required answers')),
3011 _('do not prompt, assume \'yes\' for any required answers')),
3012 ('q', 'quiet', None, _('suppress output')),
3012 ('q', 'quiet', None, _('suppress output')),
3013 ('v', 'verbose', None, _('enable additional output')),
3013 ('v', 'verbose', None, _('enable additional output')),
3014 ('', 'config', [], _('set/override config option')),
3014 ('', 'config', [], _('set/override config option')),
3015 ('', 'debug', None, _('enable debugging output')),
3015 ('', 'debug', None, _('enable debugging output')),
3016 ('', 'debugger', None, _('start debugger')),
3016 ('', 'debugger', None, _('start debugger')),
3017 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3017 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3018 ('', 'encodingmode', encoding.encodingmode,
3018 ('', 'encodingmode', encoding.encodingmode,
3019 _('set the charset encoding mode')),
3019 _('set the charset encoding mode')),
3020 ('', 'traceback', None, _('print traceback on exception')),
3020 ('', 'traceback', None, _('print traceback on exception')),
3021 ('', 'time', None, _('time how long the command takes')),
3021 ('', 'time', None, _('time how long the command takes')),
3022 ('', 'profile', None, _('print command execution profile')),
3022 ('', 'profile', None, _('print command execution profile')),
3023 ('', 'version', None, _('output version information and exit')),
3023 ('', 'version', None, _('output version information and exit')),
3024 ('h', 'help', None, _('display help and exit')),
3024 ('h', 'help', None, _('display help and exit')),
3025 ]
3025 ]
3026
3026
3027 dryrunopts = [('n', 'dry-run', None,
3027 dryrunopts = [('n', 'dry-run', None,
3028 _('do not perform actions, just print output'))]
3028 _('do not perform actions, just print output'))]
3029
3029
3030 remoteopts = [
3030 remoteopts = [
3031 ('e', 'ssh', '', _('specify ssh command to use')),
3031 ('e', 'ssh', '', _('specify ssh command to use')),
3032 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3032 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3033 ]
3033 ]
3034
3034
3035 walkopts = [
3035 walkopts = [
3036 ('I', 'include', [], _('include names matching the given patterns')),
3036 ('I', 'include', [], _('include names matching the given patterns')),
3037 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3037 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3038 ]
3038 ]
3039
3039
3040 commitopts = [
3040 commitopts = [
3041 ('m', 'message', '', _('use <text> as commit message')),
3041 ('m', 'message', '', _('use <text> as commit message')),
3042 ('l', 'logfile', '', _('read commit message from <file>')),
3042 ('l', 'logfile', '', _('read commit message from <file>')),
3043 ]
3043 ]
3044
3044
3045 commitopts2 = [
3045 commitopts2 = [
3046 ('d', 'date', '', _('record datecode as commit date')),
3046 ('d', 'date', '', _('record datecode as commit date')),
3047 ('u', 'user', '', _('record the specified user as committer')),
3047 ('u', 'user', '', _('record the specified user as committer')),
3048 ]
3048 ]
3049
3049
3050 templateopts = [
3050 templateopts = [
3051 ('', 'style', '', _('display using template map file')),
3051 ('', 'style', '', _('display using template map file')),
3052 ('', 'template', '', _('display with template')),
3052 ('', 'template', '', _('display with template')),
3053 ]
3053 ]
3054
3054
3055 logopts = [
3055 logopts = [
3056 ('p', 'patch', None, _('show patch')),
3056 ('p', 'patch', None, _('show patch')),
3057 ('g', 'git', None, _('use git extended diff format')),
3057 ('g', 'git', None, _('use git extended diff format')),
3058 ('l', 'limit', '', _('limit number of changes displayed')),
3058 ('l', 'limit', '', _('limit number of changes displayed')),
3059 ('M', 'no-merges', None, _('do not show merges')),
3059 ('M', 'no-merges', None, _('do not show merges')),
3060 ] + templateopts
3060 ] + templateopts
3061
3061
3062 diffopts = [
3062 diffopts = [
3063 ('a', 'text', None, _('treat all files as text')),
3063 ('a', 'text', None, _('treat all files as text')),
3064 ('g', 'git', None, _('use git extended diff format')),
3064 ('g', 'git', None, _('use git extended diff format')),
3065 ('', 'nodates', None, _("don't include dates in diff headers"))
3065 ('', 'nodates', None, _("don't include dates in diff headers"))
3066 ]
3066 ]
3067
3067
3068 diffopts2 = [
3068 diffopts2 = [
3069 ('p', 'show-function', None, _('show which function each change is in')),
3069 ('p', 'show-function', None, _('show which function each change is in')),
3070 ('w', 'ignore-all-space', None,
3070 ('w', 'ignore-all-space', None,
3071 _('ignore white space when comparing lines')),
3071 _('ignore white space when comparing lines')),
3072 ('b', 'ignore-space-change', None,
3072 ('b', 'ignore-space-change', None,
3073 _('ignore changes in the amount of white space')),
3073 _('ignore changes in the amount of white space')),
3074 ('B', 'ignore-blank-lines', None,
3074 ('B', 'ignore-blank-lines', None,
3075 _('ignore changes whose lines are all blank')),
3075 _('ignore changes whose lines are all blank')),
3076 ('U', 'unified', '', _('number of lines of context to show'))
3076 ('U', 'unified', '', _('number of lines of context to show'))
3077 ]
3077 ]
3078
3078
3079 similarityopts = [
3079 similarityopts = [
3080 ('s', 'similarity', '',
3080 ('s', 'similarity', '',
3081 _('guess renamed files by similarity (0<=s<=100)'))
3081 _('guess renamed files by similarity (0<=s<=100)'))
3082 ]
3082 ]
3083
3083
3084 table = {
3084 table = {
3085 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3085 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3086 "addremove":
3086 "addremove":
3087 (addremove, similarityopts + walkopts + dryrunopts,
3087 (addremove, similarityopts + walkopts + dryrunopts,
3088 _('[OPTION]... [FILE]...')),
3088 _('[OPTION]... [FILE]...')),
3089 "^annotate|blame":
3089 "^annotate|blame":
3090 (annotate,
3090 (annotate,
3091 [('r', 'rev', '', _('annotate the specified revision')),
3091 [('r', 'rev', '', _('annotate the specified revision')),
3092 ('f', 'follow', None, _('follow file copies and renames')),
3092 ('f', 'follow', None, _('follow file copies and renames')),
3093 ('a', 'text', None, _('treat all files as text')),
3093 ('a', 'text', None, _('treat all files as text')),
3094 ('u', 'user', None, _('list the author (long with -v)')),
3094 ('u', 'user', None, _('list the author (long with -v)')),
3095 ('d', 'date', None, _('list the date (short with -q)')),
3095 ('d', 'date', None, _('list the date (short with -q)')),
3096 ('n', 'number', None, _('list the revision number (default)')),
3096 ('n', 'number', None, _('list the revision number (default)')),
3097 ('c', 'changeset', None, _('list the changeset')),
3097 ('c', 'changeset', None, _('list the changeset')),
3098 ('l', 'line-number', None,
3098 ('l', 'line-number', None,
3099 _('show line number at the first appearance'))
3099 _('show line number at the first appearance'))
3100 ] + walkopts,
3100 ] + walkopts,
3101 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3101 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3102 "archive":
3102 "archive":
3103 (archive,
3103 (archive,
3104 [('', 'no-decode', None, _('do not pass files through decoders')),
3104 [('', 'no-decode', None, _('do not pass files through decoders')),
3105 ('p', 'prefix', '', _('directory prefix for files in archive')),
3105 ('p', 'prefix', '', _('directory prefix for files in archive')),
3106 ('r', 'rev', '', _('revision to distribute')),
3106 ('r', 'rev', '', _('revision to distribute')),
3107 ('t', 'type', '', _('type of distribution to create')),
3107 ('t', 'type', '', _('type of distribution to create')),
3108 ] + walkopts,
3108 ] + walkopts,
3109 _('[OPTION]... DEST')),
3109 _('[OPTION]... DEST')),
3110 "backout":
3110 "backout":
3111 (backout,
3111 (backout,
3112 [('', 'merge', None,
3112 [('', 'merge', None,
3113 _('merge with old dirstate parent after backout')),
3113 _('merge with old dirstate parent after backout')),
3114 ('', 'parent', '', _('parent to choose when backing out merge')),
3114 ('', 'parent', '', _('parent to choose when backing out merge')),
3115 ('r', 'rev', '', _('revision to backout')),
3115 ('r', 'rev', '', _('revision to backout')),
3116 ] + walkopts + commitopts + commitopts2,
3116 ] + walkopts + commitopts + commitopts2,
3117 _('[OPTION]... [-r] REV')),
3117 _('[OPTION]... [-r] REV')),
3118 "bisect":
3118 "bisect":
3119 (bisect,
3119 (bisect,
3120 [('r', 'reset', False, _('reset bisect state')),
3120 [('r', 'reset', False, _('reset bisect state')),
3121 ('g', 'good', False, _('mark changeset good')),
3121 ('g', 'good', False, _('mark changeset good')),
3122 ('b', 'bad', False, _('mark changeset bad')),
3122 ('b', 'bad', False, _('mark changeset bad')),
3123 ('s', 'skip', False, _('skip testing changeset')),
3123 ('s', 'skip', False, _('skip testing changeset')),
3124 ('c', 'command', '', _('use command to check changeset state')),
3124 ('c', 'command', '', _('use command to check changeset state')),
3125 ('U', 'noupdate', False, _('do not update to target'))],
3125 ('U', 'noupdate', False, _('do not update to target'))],
3126 _("[-gbsr] [-c CMD] [REV]")),
3126 _("[-gbsr] [-c CMD] [REV]")),
3127 "branch":
3127 "branch":
3128 (branch,
3128 (branch,
3129 [('f', 'force', None,
3129 [('f', 'force', None,
3130 _('set branch name even if it shadows an existing branch')),
3130 _('set branch name even if it shadows an existing branch')),
3131 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3131 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3132 _('[-fC] [NAME]')),
3132 _('[-fC] [NAME]')),
3133 "branches":
3133 "branches":
3134 (branches,
3134 (branches,
3135 [('a', 'active', False,
3135 [('a', 'active', False,
3136 _('show only branches that have unmerged heads'))],
3136 _('show only branches that have unmerged heads'))],
3137 _('[-a]')),
3137 _('[-a]')),
3138 "bundle":
3138 "bundle":
3139 (bundle,
3139 (bundle,
3140 [('f', 'force', None,
3140 [('f', 'force', None,
3141 _('run even when remote repository is unrelated')),
3141 _('run even when remote repository is unrelated')),
3142 ('r', 'rev', [],
3142 ('r', 'rev', [],
3143 _('a changeset up to which you would like to bundle')),
3143 _('a changeset up to which you would like to bundle')),
3144 ('', 'base', [],
3144 ('', 'base', [],
3145 _('a base changeset to specify instead of a destination')),
3145 _('a base changeset to specify instead of a destination')),
3146 ('a', 'all', None, _('bundle all changesets in the repository')),
3146 ('a', 'all', None, _('bundle all changesets in the repository')),
3147 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3147 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3148 ] + remoteopts,
3148 ] + remoteopts,
3149 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3149 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3150 "cat":
3150 "cat":
3151 (cat,
3151 (cat,
3152 [('o', 'output', '', _('print output to file with formatted name')),
3152 [('o', 'output', '', _('print output to file with formatted name')),
3153 ('r', 'rev', '', _('print the given revision')),
3153 ('r', 'rev', '', _('print the given revision')),
3154 ('', 'decode', None, _('apply any matching decode filter')),
3154 ('', 'decode', None, _('apply any matching decode filter')),
3155 ] + walkopts,
3155 ] + walkopts,
3156 _('[OPTION]... FILE...')),
3156 _('[OPTION]... FILE...')),
3157 "^clone":
3157 "^clone":
3158 (clone,
3158 (clone,
3159 [('U', 'noupdate', None,
3159 [('U', 'noupdate', None,
3160 _('the clone will only contain a repository (no working copy)')),
3160 _('the clone will only contain a repository (no working copy)')),
3161 ('r', 'rev', [],
3161 ('r', 'rev', [],
3162 _('a changeset you would like to have after cloning')),
3162 _('a changeset you would like to have after cloning')),
3163 ('', 'pull', None, _('use pull protocol to copy metadata')),
3163 ('', 'pull', None, _('use pull protocol to copy metadata')),
3164 ('', 'uncompressed', None,
3164 ('', 'uncompressed', None,
3165 _('use uncompressed transfer (fast over LAN)')),
3165 _('use uncompressed transfer (fast over LAN)')),
3166 ] + remoteopts,
3166 ] + remoteopts,
3167 _('[OPTION]... SOURCE [DEST]')),
3167 _('[OPTION]... SOURCE [DEST]')),
3168 "^commit|ci":
3168 "^commit|ci":
3169 (commit,
3169 (commit,
3170 [('A', 'addremove', None,
3170 [('A', 'addremove', None,
3171 _('mark new/missing files as added/removed before committing')),
3171 _('mark new/missing files as added/removed before committing')),
3172 ('', 'close-branch', None,
3172 ('', 'close-branch', None,
3173 _('mark a branch as closed, hiding it from the branch list')),
3173 _('mark a branch as closed, hiding it from the branch list')),
3174 ] + walkopts + commitopts + commitopts2,
3174 ] + walkopts + commitopts + commitopts2,
3175 _('[OPTION]... [FILE]...')),
3175 _('[OPTION]... [FILE]...')),
3176 "copy|cp":
3176 "copy|cp":
3177 (copy,
3177 (copy,
3178 [('A', 'after', None, _('record a copy that has already occurred')),
3178 [('A', 'after', None, _('record a copy that has already occurred')),
3179 ('f', 'force', None,
3179 ('f', 'force', None,
3180 _('forcibly copy over an existing managed file')),
3180 _('forcibly copy over an existing managed file')),
3181 ] + walkopts + dryrunopts,
3181 ] + walkopts + dryrunopts,
3182 _('[OPTION]... [SOURCE]... DEST')),
3182 _('[OPTION]... [SOURCE]... DEST')),
3183 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3183 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3184 "debugcheckstate": (debugcheckstate, []),
3184 "debugcheckstate": (debugcheckstate, []),
3185 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3185 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3186 "debugcomplete":
3186 "debugcomplete":
3187 (debugcomplete,
3187 (debugcomplete,
3188 [('o', 'options', None, _('show the command options'))],
3188 [('o', 'options', None, _('show the command options'))],
3189 _('[-o] CMD')),
3189 _('[-o] CMD')),
3190 "debugdate":
3190 "debugdate":
3191 (debugdate,
3191 (debugdate,
3192 [('e', 'extended', None, _('try extended date formats'))],
3192 [('e', 'extended', None, _('try extended date formats'))],
3193 _('[-e] DATE [RANGE]')),
3193 _('[-e] DATE [RANGE]')),
3194 "debugdata": (debugdata, [], _('FILE REV')),
3194 "debugdata": (debugdata, [], _('FILE REV')),
3195 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3195 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3196 "debugindex": (debugindex, [], _('FILE')),
3196 "debugindex": (debugindex, [], _('FILE')),
3197 "debugindexdot": (debugindexdot, [], _('FILE')),
3197 "debugindexdot": (debugindexdot, [], _('FILE')),
3198 "debuginstall": (debuginstall, []),
3198 "debuginstall": (debuginstall, []),
3199 "debugrebuildstate":
3199 "debugrebuildstate":
3200 (debugrebuildstate,
3200 (debugrebuildstate,
3201 [('r', 'rev', '', _('revision to rebuild to'))],
3201 [('r', 'rev', '', _('revision to rebuild to'))],
3202 _('[-r REV] [REV]')),
3202 _('[-r REV] [REV]')),
3203 "debugrename":
3203 "debugrename":
3204 (debugrename,
3204 (debugrename,
3205 [('r', 'rev', '', _('revision to debug'))],
3205 [('r', 'rev', '', _('revision to debug'))],
3206 _('[-r REV] FILE')),
3206 _('[-r REV] FILE')),
3207 "debugsetparents":
3207 "debugsetparents":
3208 (debugsetparents, [], _('REV1 [REV2]')),
3208 (debugsetparents, [], _('REV1 [REV2]')),
3209 "debugstate":
3209 "debugstate":
3210 (debugstate,
3210 (debugstate,
3211 [('', 'nodates', None, _('do not display the saved mtime'))],
3211 [('', 'nodates', None, _('do not display the saved mtime'))],
3212 _('[OPTION]...')),
3212 _('[OPTION]...')),
3213 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3213 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3214 "^diff":
3214 "^diff":
3215 (diff,
3215 (diff,
3216 [('r', 'rev', [], _('revision')),
3216 [('r', 'rev', [], _('revision')),
3217 ('c', 'change', '', _('change made by revision'))
3217 ('c', 'change', '', _('change made by revision'))
3218 ] + diffopts + diffopts2 + walkopts,
3218 ] + diffopts + diffopts2 + walkopts,
3219 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3219 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3220 "^export":
3220 "^export":
3221 (export,
3221 (export,
3222 [('o', 'output', '', _('print output to file with formatted name')),
3222 [('o', 'output', '', _('print output to file with formatted name')),
3223 ('', 'switch-parent', None, _('diff against the second parent'))
3223 ('', 'switch-parent', None, _('diff against the second parent'))
3224 ] + diffopts,
3224 ] + diffopts,
3225 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3225 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3226 "grep":
3226 "grep":
3227 (grep,
3227 (grep,
3228 [('0', 'print0', None, _('end fields with NUL')),
3228 [('0', 'print0', None, _('end fields with NUL')),
3229 ('', 'all', None, _('print all revisions that match')),
3229 ('', 'all', None, _('print all revisions that match')),
3230 ('f', 'follow', None,
3230 ('f', 'follow', None,
3231 _('follow changeset history, or file history across copies and renames')),
3231 _('follow changeset history, or file history across copies and renames')),
3232 ('i', 'ignore-case', None, _('ignore case when matching')),
3232 ('i', 'ignore-case', None, _('ignore case when matching')),
3233 ('l', 'files-with-matches', None,
3233 ('l', 'files-with-matches', None,
3234 _('print only filenames and revisions that match')),
3234 _('print only filenames and revisions that match')),
3235 ('n', 'line-number', None, _('print matching line numbers')),
3235 ('n', 'line-number', None, _('print matching line numbers')),
3236 ('r', 'rev', [], _('search in given revision range')),
3236 ('r', 'rev', [], _('search in given revision range')),
3237 ('u', 'user', None, _('list the author (long with -v)')),
3237 ('u', 'user', None, _('list the author (long with -v)')),
3238 ('d', 'date', None, _('list the date (short with -q)')),
3238 ('d', 'date', None, _('list the date (short with -q)')),
3239 ] + walkopts,
3239 ] + walkopts,
3240 _('[OPTION]... PATTERN [FILE]...')),
3240 _('[OPTION]... PATTERN [FILE]...')),
3241 "heads":
3241 "heads":
3242 (heads,
3242 (heads,
3243 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3243 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3244 ('a', 'active', False,
3244 ('a', 'active', False,
3245 _('show only the active heads from open branches')),
3245 _('show only the active heads from open branches')),
3246 ('c', 'closed', False,
3246 ('c', 'closed', False,
3247 _('show normal and closed heads')),
3247 _('show normal and closed heads')),
3248 ] + templateopts,
3248 ] + templateopts,
3249 _('[-r REV] [REV]...')),
3249 _('[-r REV] [REV]...')),
3250 "help": (help_, [], _('[TOPIC]')),
3250 "help": (help_, [], _('[TOPIC]')),
3251 "identify|id":
3251 "identify|id":
3252 (identify,
3252 (identify,
3253 [('r', 'rev', '', _('identify the specified revision')),
3253 [('r', 'rev', '', _('identify the specified revision')),
3254 ('n', 'num', None, _('show local revision number')),
3254 ('n', 'num', None, _('show local revision number')),
3255 ('i', 'id', None, _('show global revision id')),
3255 ('i', 'id', None, _('show global revision id')),
3256 ('b', 'branch', None, _('show branch')),
3256 ('b', 'branch', None, _('show branch')),
3257 ('t', 'tags', None, _('show tags'))],
3257 ('t', 'tags', None, _('show tags'))],
3258 _('[-nibt] [-r REV] [SOURCE]')),
3258 _('[-nibt] [-r REV] [SOURCE]')),
3259 "import|patch":
3259 "import|patch":
3260 (import_,
3260 (import_,
3261 [('p', 'strip', 1,
3261 [('p', 'strip', 1,
3262 _('directory strip option for patch. This has the same '
3262 _('directory strip option for patch. This has the same '
3263 'meaning as the corresponding patch option')),
3263 'meaning as the corresponding patch option')),
3264 ('b', 'base', '', _('base path')),
3264 ('b', 'base', '', _('base path')),
3265 ('f', 'force', None,
3265 ('f', 'force', None,
3266 _('skip check for outstanding uncommitted changes')),
3266 _('skip check for outstanding uncommitted changes')),
3267 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3267 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3268 ('', 'exact', None,
3268 ('', 'exact', None,
3269 _('apply patch to the nodes from which it was generated')),
3269 _('apply patch to the nodes from which it was generated')),
3270 ('', 'import-branch', None,
3270 ('', 'import-branch', None,
3271 _('use any branch information in patch (implied by --exact)'))] +
3271 _('use any branch information in patch (implied by --exact)'))] +
3272 commitopts + commitopts2 + similarityopts,
3272 commitopts + commitopts2 + similarityopts,
3273 _('[OPTION]... PATCH...')),
3273 _('[OPTION]... PATCH...')),
3274 "incoming|in":
3274 "incoming|in":
3275 (incoming,
3275 (incoming,
3276 [('f', 'force', None,
3276 [('f', 'force', None,
3277 _('run even when remote repository is unrelated')),
3277 _('run even when remote repository is unrelated')),
3278 ('n', 'newest-first', None, _('show newest record first')),
3278 ('n', 'newest-first', None, _('show newest record first')),
3279 ('', 'bundle', '', _('file to store the bundles into')),
3279 ('', 'bundle', '', _('file to store the bundles into')),
3280 ('r', 'rev', [],
3280 ('r', 'rev', [],
3281 _('a specific revision up to which you would like to pull')),
3281 _('a specific revision up to which you would like to pull')),
3282 ] + logopts + remoteopts,
3282 ] + logopts + remoteopts,
3283 _('[-p] [-n] [-M] [-f] [-r REV]...'
3283 _('[-p] [-n] [-M] [-f] [-r REV]...'
3284 ' [--bundle FILENAME] [SOURCE]')),
3284 ' [--bundle FILENAME] [SOURCE]')),
3285 "^init":
3285 "^init":
3286 (init,
3286 (init,
3287 remoteopts,
3287 remoteopts,
3288 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3288 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3289 "locate":
3289 "locate":
3290 (locate,
3290 (locate,
3291 [('r', 'rev', '', _('search the repository as it stood at REV')),
3291 [('r', 'rev', '', _('search the repository as it stood at REV')),
3292 ('0', 'print0', None,
3292 ('0', 'print0', None,
3293 _('end filenames with NUL, for use with xargs')),
3293 _('end filenames with NUL, for use with xargs')),
3294 ('f', 'fullpath', None,
3294 ('f', 'fullpath', None,
3295 _('print complete paths from the filesystem root')),
3295 _('print complete paths from the filesystem root')),
3296 ] + walkopts,
3296 ] + walkopts,
3297 _('[OPTION]... [PATTERN]...')),
3297 _('[OPTION]... [PATTERN]...')),
3298 "^log|history":
3298 "^log|history":
3299 (log,
3299 (log,
3300 [('f', 'follow', None,
3300 [('f', 'follow', None,
3301 _('follow changeset history, or file history across copies and renames')),
3301 _('follow changeset history, or file history across copies and renames')),
3302 ('', 'follow-first', None,
3302 ('', 'follow-first', None,
3303 _('only follow the first parent of merge changesets')),
3303 _('only follow the first parent of merge changesets')),
3304 ('d', 'date', '', _('show revisions matching date spec')),
3304 ('d', 'date', '', _('show revisions matching date spec')),
3305 ('C', 'copies', None, _('show copied files')),
3305 ('C', 'copies', None, _('show copied files')),
3306 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3306 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3307 ('r', 'rev', [], _('show the specified revision or range')),
3307 ('r', 'rev', [], _('show the specified revision or range')),
3308 ('', 'removed', None, _('include revisions where files were removed')),
3308 ('', 'removed', None, _('include revisions where files were removed')),
3309 ('m', 'only-merges', None, _('show only merges')),
3309 ('m', 'only-merges', None, _('show only merges')),
3310 ('u', 'user', [], _('revisions committed by user')),
3310 ('u', 'user', [], _('revisions committed by user')),
3311 ('b', 'only-branch', [],
3311 ('b', 'only-branch', [],
3312 _('show only changesets within the given named branch')),
3312 _('show only changesets within the given named branch')),
3313 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3313 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3314 ] + logopts + walkopts,
3314 ] + logopts + walkopts,
3315 _('[OPTION]... [FILE]')),
3315 _('[OPTION]... [FILE]')),
3316 "manifest":
3316 "manifest":
3317 (manifest,
3317 (manifest,
3318 [('r', 'rev', '', _('revision to display'))],
3318 [('r', 'rev', '', _('revision to display'))],
3319 _('[-r REV]')),
3319 _('[-r REV]')),
3320 "^merge":
3320 "^merge":
3321 (merge,
3321 (merge,
3322 [('f', 'force', None, _('force a merge with outstanding changes')),
3322 [('f', 'force', None, _('force a merge with outstanding changes')),
3323 ('r', 'rev', '', _('revision to merge')),
3323 ('r', 'rev', '', _('revision to merge')),
3324 ('S', 'show', None,
3324 ('S', 'show', None,
3325 _('review revisions to merge (no merge is performed)'))],
3325 _('review revisions to merge (no merge is performed)'))],
3326 _('[-f] [[-r] REV]')),
3326 _('[-f] [[-r] REV]')),
3327 "outgoing|out":
3327 "outgoing|out":
3328 (outgoing,
3328 (outgoing,
3329 [('f', 'force', None,
3329 [('f', 'force', None,
3330 _('run even when remote repository is unrelated')),
3330 _('run even when remote repository is unrelated')),
3331 ('r', 'rev', [],
3331 ('r', 'rev', [],
3332 _('a specific revision up to which you would like to push')),
3332 _('a specific revision up to which you would like to push')),
3333 ('n', 'newest-first', None, _('show newest record first')),
3333 ('n', 'newest-first', None, _('show newest record first')),
3334 ] + logopts + remoteopts,
3334 ] + logopts + remoteopts,
3335 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3335 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3336 "^parents":
3336 "^parents":
3337 (parents,
3337 (parents,
3338 [('r', 'rev', '', _('show parents from the specified revision')),
3338 [('r', 'rev', '', _('show parents from the specified revision')),
3339 ] + templateopts,
3339 ] + templateopts,
3340 _('[-r REV] [FILE]')),
3340 _('[-r REV] [FILE]')),
3341 "paths": (paths, [], _('[NAME]')),
3341 "paths": (paths, [], _('[NAME]')),
3342 "^pull":
3342 "^pull":
3343 (pull,
3343 (pull,
3344 [('u', 'update', None,
3344 [('u', 'update', None,
3345 _('update to new tip if changesets were pulled')),
3345 _('update to new tip if changesets were pulled')),
3346 ('f', 'force', None,
3346 ('f', 'force', None,
3347 _('run even when remote repository is unrelated')),
3347 _('run even when remote repository is unrelated')),
3348 ('r', 'rev', [],
3348 ('r', 'rev', [],
3349 _('a specific revision up to which you would like to pull')),
3349 _('a specific revision up to which you would like to pull')),
3350 ] + remoteopts,
3350 ] + remoteopts,
3351 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3351 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3352 "^push":
3352 "^push":
3353 (push,
3353 (push,
3354 [('f', 'force', None, _('force push')),
3354 [('f', 'force', None, _('force push')),
3355 ('r', 'rev', [],
3355 ('r', 'rev', [],
3356 _('a specific revision up to which you would like to push')),
3356 _('a specific revision up to which you would like to push')),
3357 ] + remoteopts,
3357 ] + remoteopts,
3358 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3358 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3359 "recover": (recover, []),
3359 "recover": (recover, []),
3360 "^remove|rm":
3360 "^remove|rm":
3361 (remove,
3361 (remove,
3362 [('A', 'after', None, _('record delete for missing files')),
3362 [('A', 'after', None, _('record delete for missing files')),
3363 ('f', 'force', None,
3363 ('f', 'force', None,
3364 _('remove (and delete) file even if added or modified')),
3364 _('remove (and delete) file even if added or modified')),
3365 ] + walkopts,
3365 ] + walkopts,
3366 _('[OPTION]... FILE...')),
3366 _('[OPTION]... FILE...')),
3367 "rename|mv":
3367 "rename|mv":
3368 (rename,
3368 (rename,
3369 [('A', 'after', None, _('record a rename that has already occurred')),
3369 [('A', 'after', None, _('record a rename that has already occurred')),
3370 ('f', 'force', None,
3370 ('f', 'force', None,
3371 _('forcibly copy over an existing managed file')),
3371 _('forcibly copy over an existing managed file')),
3372 ] + walkopts + dryrunopts,
3372 ] + walkopts + dryrunopts,
3373 _('[OPTION]... SOURCE... DEST')),
3373 _('[OPTION]... SOURCE... DEST')),
3374 "resolve":
3374 "resolve":
3375 (resolve,
3375 (resolve,
3376 [('a', 'all', None, _('remerge all unresolved files')),
3376 [('a', 'all', None, _('remerge all unresolved files')),
3377 ('l', 'list', None, _('list state of files needing merge')),
3377 ('l', 'list', None, _('list state of files needing merge')),
3378 ('m', 'mark', None, _('mark files as resolved')),
3378 ('m', 'mark', None, _('mark files as resolved')),
3379 ('u', 'unmark', None, _('unmark files as resolved'))]
3379 ('u', 'unmark', None, _('unmark files as resolved'))]
3380 + walkopts,
3380 + walkopts,
3381 _('[OPTION]... [FILE]...')),
3381 _('[OPTION]... [FILE]...')),
3382 "revert":
3382 "revert":
3383 (revert,
3383 (revert,
3384 [('a', 'all', None, _('revert all changes when no arguments given')),
3384 [('a', 'all', None, _('revert all changes when no arguments given')),
3385 ('d', 'date', '', _('tipmost revision matching date')),
3385 ('d', 'date', '', _('tipmost revision matching date')),
3386 ('r', 'rev', '', _('revision to revert to')),
3386 ('r', 'rev', '', _('revision to revert to')),
3387 ('', 'no-backup', None, _('do not save backup copies of files')),
3387 ('', 'no-backup', None, _('do not save backup copies of files')),
3388 ] + walkopts + dryrunopts,
3388 ] + walkopts + dryrunopts,
3389 _('[OPTION]... [-r REV] [NAME]...')),
3389 _('[OPTION]... [-r REV] [NAME]...')),
3390 "rollback": (rollback, []),
3390 "rollback": (rollback, []),
3391 "root": (root, []),
3391 "root": (root, []),
3392 "^serve":
3392 "^serve":
3393 (serve,
3393 (serve,
3394 [('A', 'accesslog', '', _('name of access log file to write to')),
3394 [('A', 'accesslog', '', _('name of access log file to write to')),
3395 ('d', 'daemon', None, _('run server in background')),
3395 ('d', 'daemon', None, _('run server in background')),
3396 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3396 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3397 ('E', 'errorlog', '', _('name of error log file to write to')),
3397 ('E', 'errorlog', '', _('name of error log file to write to')),
3398 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3398 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3399 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3399 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3400 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3400 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3401 ('n', 'name', '',
3401 ('n', 'name', '',
3402 _('name to show in web pages (default: working directory)')),
3402 _('name to show in web pages (default: working directory)')),
3403 ('', 'webdir-conf', '', _('name of the webdir config file'
3403 ('', 'webdir-conf', '', _('name of the webdir config file'
3404 ' (serve more than one repository)')),
3404 ' (serve more than one repository)')),
3405 ('', 'pid-file', '', _('name of file to write process ID to')),
3405 ('', 'pid-file', '', _('name of file to write process ID to')),
3406 ('', 'stdio', None, _('for remote clients')),
3406 ('', 'stdio', None, _('for remote clients')),
3407 ('t', 'templates', '', _('web templates to use')),
3407 ('t', 'templates', '', _('web templates to use')),
3408 ('', 'style', '', _('template style to use')),
3408 ('', 'style', '', _('template style to use')),
3409 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3409 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3410 ('', 'certificate', '', _('SSL certificate file'))],
3410 ('', 'certificate', '', _('SSL certificate file'))],
3411 _('[OPTION]...')),
3411 _('[OPTION]...')),
3412 "showconfig|debugconfig":
3412 "showconfig|debugconfig":
3413 (showconfig,
3413 (showconfig,
3414 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3414 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3415 _('[-u] [NAME]...')),
3415 _('[-u] [NAME]...')),
3416 "^status|st":
3416 "^status|st":
3417 (status,
3417 (status,
3418 [('A', 'all', None, _('show status of all files')),
3418 [('A', 'all', None, _('show status of all files')),
3419 ('m', 'modified', None, _('show only modified files')),
3419 ('m', 'modified', None, _('show only modified files')),
3420 ('a', 'added', None, _('show only added files')),
3420 ('a', 'added', None, _('show only added files')),
3421 ('r', 'removed', None, _('show only removed files')),
3421 ('r', 'removed', None, _('show only removed files')),
3422 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3422 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3423 ('c', 'clean', None, _('show only files without changes')),
3423 ('c', 'clean', None, _('show only files without changes')),
3424 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3424 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3425 ('i', 'ignored', None, _('show only ignored files')),
3425 ('i', 'ignored', None, _('show only ignored files')),
3426 ('n', 'no-status', None, _('hide status prefix')),
3426 ('n', 'no-status', None, _('hide status prefix')),
3427 ('C', 'copies', None, _('show source of copied files')),
3427 ('C', 'copies', None, _('show source of copied files')),
3428 ('0', 'print0', None,
3428 ('0', 'print0', None,
3429 _('end filenames with NUL, for use with xargs')),
3429 _('end filenames with NUL, for use with xargs')),
3430 ('', 'rev', [], _('show difference from revision')),
3430 ('', 'rev', [], _('show difference from revision')),
3431 ] + walkopts,
3431 ] + walkopts,
3432 _('[OPTION]... [FILE]...')),
3432 _('[OPTION]... [FILE]...')),
3433 "tag":
3433 "tag":
3434 (tag,
3434 (tag,
3435 [('f', 'force', None, _('replace existing tag')),
3435 [('f', 'force', None, _('replace existing tag')),
3436 ('l', 'local', None, _('make the tag local')),
3436 ('l', 'local', None, _('make the tag local')),
3437 ('r', 'rev', '', _('revision to tag')),
3437 ('r', 'rev', '', _('revision to tag')),
3438 ('', 'remove', None, _('remove a tag')),
3438 ('', 'remove', None, _('remove a tag')),
3439 # -l/--local is already there, commitopts cannot be used
3439 # -l/--local is already there, commitopts cannot be used
3440 ('m', 'message', '', _('use <text> as commit message')),
3440 ('m', 'message', '', _('use <text> as commit message')),
3441 ] + commitopts2,
3441 ] + commitopts2,
3442 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3442 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3443 "tags": (tags, []),
3443 "tags": (tags, []),
3444 "tip":
3444 "tip":
3445 (tip,
3445 (tip,
3446 [('p', 'patch', None, _('show patch')),
3446 [('p', 'patch', None, _('show patch')),
3447 ('g', 'git', None, _('use git extended diff format')),
3447 ('g', 'git', None, _('use git extended diff format')),
3448 ] + templateopts,
3448 ] + templateopts,
3449 _('[-p]')),
3449 _('[-p]')),
3450 "unbundle":
3450 "unbundle":
3451 (unbundle,
3451 (unbundle,
3452 [('u', 'update', None,
3452 [('u', 'update', None,
3453 _('update to new tip if changesets were unbundled'))],
3453 _('update to new tip if changesets were unbundled'))],
3454 _('[-u] FILE...')),
3454 _('[-u] FILE...')),
3455 "^update|up|checkout|co":
3455 "^update|up|checkout|co":
3456 (update,
3456 (update,
3457 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3457 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3458 ('d', 'date', '', _('tipmost revision matching date')),
3458 ('d', 'date', '', _('tipmost revision matching date')),
3459 ('r', 'rev', '', _('revision'))],
3459 ('r', 'rev', '', _('revision'))],
3460 _('[-C] [-d DATE] [[-r] REV]')),
3460 _('[-C] [-d DATE] [[-r] REV]')),
3461 "verify": (verify, []),
3461 "verify": (verify, []),
3462 "version": (version_, []),
3462 "version": (version_, []),
3463 }
3463 }
3464
3464
3465 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3465 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3466 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3466 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3467 optionalrepo = ("identify paths serve showconfig debugancestor")
3467 optionalrepo = ("identify paths serve showconfig debugancestor")
General Comments 0
You need to be logged in to leave comments. Login now