##// END OF EJS Templates
Make revrange return a list of ints so that callers don't have to convert
Matt Mackall -
r3526:68341c06 default
parent child Browse files
Show More
@@ -1,2157 +1,2157 b''
1 # queue.py - patch queues for mercurial
1 # queue.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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, 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.demandload import *
32 from mercurial.demandload import *
33 from mercurial.i18n import gettext as _
33 from mercurial.i18n import gettext as _
34 from mercurial import commands
34 from mercurial import commands
35 demandload(globals(), "os sys re struct traceback errno bz2")
35 demandload(globals(), "os sys re struct traceback errno bz2")
36 demandload(globals(), "mercurial:cmdutil,hg,patch,revlog,ui,util")
36 demandload(globals(), "mercurial:cmdutil,hg,patch,revlog,ui,util")
37
37
38 commands.norepo += " qclone qversion"
38 commands.norepo += " qclone qversion"
39
39
40 class statusentry:
40 class statusentry:
41 def __init__(self, rev, name=None):
41 def __init__(self, rev, name=None):
42 if not name:
42 if not name:
43 fields = rev.split(':', 1)
43 fields = rev.split(':', 1)
44 if len(fields) == 2:
44 if len(fields) == 2:
45 self.rev, self.name = fields
45 self.rev, self.name = fields
46 else:
46 else:
47 self.rev, self.name = None, None
47 self.rev, self.name = None, None
48 else:
48 else:
49 self.rev, self.name = rev, name
49 self.rev, self.name = rev, name
50
50
51 def __str__(self):
51 def __str__(self):
52 return self.rev + ':' + self.name
52 return self.rev + ':' + self.name
53
53
54 class queue:
54 class queue:
55 def __init__(self, ui, path, patchdir=None):
55 def __init__(self, ui, path, patchdir=None):
56 self.basepath = path
56 self.basepath = path
57 self.path = patchdir or os.path.join(path, "patches")
57 self.path = patchdir or os.path.join(path, "patches")
58 self.opener = util.opener(self.path)
58 self.opener = util.opener(self.path)
59 self.ui = ui
59 self.ui = ui
60 self.applied = []
60 self.applied = []
61 self.full_series = []
61 self.full_series = []
62 self.applied_dirty = 0
62 self.applied_dirty = 0
63 self.series_dirty = 0
63 self.series_dirty = 0
64 self.series_path = "series"
64 self.series_path = "series"
65 self.status_path = "status"
65 self.status_path = "status"
66 self.guards_path = "guards"
66 self.guards_path = "guards"
67 self.active_guards = None
67 self.active_guards = None
68 self.guards_dirty = False
68 self.guards_dirty = False
69 self._diffopts = None
69 self._diffopts = None
70
70
71 if os.path.exists(self.join(self.series_path)):
71 if os.path.exists(self.join(self.series_path)):
72 self.full_series = self.opener(self.series_path).read().splitlines()
72 self.full_series = self.opener(self.series_path).read().splitlines()
73 self.parse_series()
73 self.parse_series()
74
74
75 if os.path.exists(self.join(self.status_path)):
75 if os.path.exists(self.join(self.status_path)):
76 lines = self.opener(self.status_path).read().splitlines()
76 lines = self.opener(self.status_path).read().splitlines()
77 self.applied = [statusentry(l) for l in lines]
77 self.applied = [statusentry(l) for l in lines]
78
78
79 def diffopts(self):
79 def diffopts(self):
80 if self._diffopts is None:
80 if self._diffopts is None:
81 self._diffopts = patch.diffopts(self.ui)
81 self._diffopts = patch.diffopts(self.ui)
82 return self._diffopts
82 return self._diffopts
83
83
84 def join(self, *p):
84 def join(self, *p):
85 return os.path.join(self.path, *p)
85 return os.path.join(self.path, *p)
86
86
87 def find_series(self, patch):
87 def find_series(self, patch):
88 pre = re.compile("(\s*)([^#]+)")
88 pre = re.compile("(\s*)([^#]+)")
89 index = 0
89 index = 0
90 for l in self.full_series:
90 for l in self.full_series:
91 m = pre.match(l)
91 m = pre.match(l)
92 if m:
92 if m:
93 s = m.group(2)
93 s = m.group(2)
94 s = s.rstrip()
94 s = s.rstrip()
95 if s == patch:
95 if s == patch:
96 return index
96 return index
97 index += 1
97 index += 1
98 return None
98 return None
99
99
100 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
100 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
101
101
102 def parse_series(self):
102 def parse_series(self):
103 self.series = []
103 self.series = []
104 self.series_guards = []
104 self.series_guards = []
105 for l in self.full_series:
105 for l in self.full_series:
106 h = l.find('#')
106 h = l.find('#')
107 if h == -1:
107 if h == -1:
108 patch = l
108 patch = l
109 comment = ''
109 comment = ''
110 elif h == 0:
110 elif h == 0:
111 continue
111 continue
112 else:
112 else:
113 patch = l[:h]
113 patch = l[:h]
114 comment = l[h:]
114 comment = l[h:]
115 patch = patch.strip()
115 patch = patch.strip()
116 if patch:
116 if patch:
117 if patch in self.series:
117 if patch in self.series:
118 raise util.Abort(_('%s appears more than once in %s') %
118 raise util.Abort(_('%s appears more than once in %s') %
119 (patch, self.join(self.series_path)))
119 (patch, self.join(self.series_path)))
120 self.series.append(patch)
120 self.series.append(patch)
121 self.series_guards.append(self.guard_re.findall(comment))
121 self.series_guards.append(self.guard_re.findall(comment))
122
122
123 def check_guard(self, guard):
123 def check_guard(self, guard):
124 bad_chars = '# \t\r\n\f'
124 bad_chars = '# \t\r\n\f'
125 first = guard[0]
125 first = guard[0]
126 for c in '-+':
126 for c in '-+':
127 if first == c:
127 if first == c:
128 return (_('guard %r starts with invalid character: %r') %
128 return (_('guard %r starts with invalid character: %r') %
129 (guard, c))
129 (guard, c))
130 for c in bad_chars:
130 for c in bad_chars:
131 if c in guard:
131 if c in guard:
132 return _('invalid character in guard %r: %r') % (guard, c)
132 return _('invalid character in guard %r: %r') % (guard, c)
133
133
134 def set_active(self, guards):
134 def set_active(self, guards):
135 for guard in guards:
135 for guard in guards:
136 bad = self.check_guard(guard)
136 bad = self.check_guard(guard)
137 if bad:
137 if bad:
138 raise util.Abort(bad)
138 raise util.Abort(bad)
139 guards = dict.fromkeys(guards).keys()
139 guards = dict.fromkeys(guards).keys()
140 guards.sort()
140 guards.sort()
141 self.ui.debug('active guards: %s\n' % ' '.join(guards))
141 self.ui.debug('active guards: %s\n' % ' '.join(guards))
142 self.active_guards = guards
142 self.active_guards = guards
143 self.guards_dirty = True
143 self.guards_dirty = True
144
144
145 def active(self):
145 def active(self):
146 if self.active_guards is None:
146 if self.active_guards is None:
147 self.active_guards = []
147 self.active_guards = []
148 try:
148 try:
149 guards = self.opener(self.guards_path).read().split()
149 guards = self.opener(self.guards_path).read().split()
150 except IOError, err:
150 except IOError, err:
151 if err.errno != errno.ENOENT: raise
151 if err.errno != errno.ENOENT: raise
152 guards = []
152 guards = []
153 for i, guard in enumerate(guards):
153 for i, guard in enumerate(guards):
154 bad = self.check_guard(guard)
154 bad = self.check_guard(guard)
155 if bad:
155 if bad:
156 self.ui.warn('%s:%d: %s\n' %
156 self.ui.warn('%s:%d: %s\n' %
157 (self.join(self.guards_path), i + 1, bad))
157 (self.join(self.guards_path), i + 1, bad))
158 else:
158 else:
159 self.active_guards.append(guard)
159 self.active_guards.append(guard)
160 return self.active_guards
160 return self.active_guards
161
161
162 def set_guards(self, idx, guards):
162 def set_guards(self, idx, guards):
163 for g in guards:
163 for g in guards:
164 if len(g) < 2:
164 if len(g) < 2:
165 raise util.Abort(_('guard %r too short') % g)
165 raise util.Abort(_('guard %r too short') % g)
166 if g[0] not in '-+':
166 if g[0] not in '-+':
167 raise util.Abort(_('guard %r starts with invalid char') % g)
167 raise util.Abort(_('guard %r starts with invalid char') % g)
168 bad = self.check_guard(g[1:])
168 bad = self.check_guard(g[1:])
169 if bad:
169 if bad:
170 raise util.Abort(bad)
170 raise util.Abort(bad)
171 drop = self.guard_re.sub('', self.full_series[idx])
171 drop = self.guard_re.sub('', self.full_series[idx])
172 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
172 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
173 self.parse_series()
173 self.parse_series()
174 self.series_dirty = True
174 self.series_dirty = True
175
175
176 def pushable(self, idx):
176 def pushable(self, idx):
177 if isinstance(idx, str):
177 if isinstance(idx, str):
178 idx = self.series.index(idx)
178 idx = self.series.index(idx)
179 patchguards = self.series_guards[idx]
179 patchguards = self.series_guards[idx]
180 if not patchguards:
180 if not patchguards:
181 return True, None
181 return True, None
182 default = False
182 default = False
183 guards = self.active()
183 guards = self.active()
184 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
184 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
185 if exactneg:
185 if exactneg:
186 return False, exactneg[0]
186 return False, exactneg[0]
187 pos = [g for g in patchguards if g[0] == '+']
187 pos = [g for g in patchguards if g[0] == '+']
188 exactpos = [g for g in pos if g[1:] in guards]
188 exactpos = [g for g in pos if g[1:] in guards]
189 if pos:
189 if pos:
190 if exactpos:
190 if exactpos:
191 return True, exactpos[0]
191 return True, exactpos[0]
192 return False, pos
192 return False, pos
193 return True, ''
193 return True, ''
194
194
195 def explain_pushable(self, idx, all_patches=False):
195 def explain_pushable(self, idx, all_patches=False):
196 write = all_patches and self.ui.write or self.ui.warn
196 write = all_patches and self.ui.write or self.ui.warn
197 if all_patches or self.ui.verbose:
197 if all_patches or self.ui.verbose:
198 if isinstance(idx, str):
198 if isinstance(idx, str):
199 idx = self.series.index(idx)
199 idx = self.series.index(idx)
200 pushable, why = self.pushable(idx)
200 pushable, why = self.pushable(idx)
201 if all_patches and pushable:
201 if all_patches and pushable:
202 if why is None:
202 if why is None:
203 write(_('allowing %s - no guards in effect\n') %
203 write(_('allowing %s - no guards in effect\n') %
204 self.series[idx])
204 self.series[idx])
205 else:
205 else:
206 if not why:
206 if not why:
207 write(_('allowing %s - no matching negative guards\n') %
207 write(_('allowing %s - no matching negative guards\n') %
208 self.series[idx])
208 self.series[idx])
209 else:
209 else:
210 write(_('allowing %s - guarded by %r\n') %
210 write(_('allowing %s - guarded by %r\n') %
211 (self.series[idx], why))
211 (self.series[idx], why))
212 if not pushable:
212 if not pushable:
213 if why:
213 if why:
214 write(_('skipping %s - guarded by %r\n') %
214 write(_('skipping %s - guarded by %r\n') %
215 (self.series[idx], ' '.join(why)))
215 (self.series[idx], ' '.join(why)))
216 else:
216 else:
217 write(_('skipping %s - no matching guards\n') %
217 write(_('skipping %s - no matching guards\n') %
218 self.series[idx])
218 self.series[idx])
219
219
220 def save_dirty(self):
220 def save_dirty(self):
221 def write_list(items, path):
221 def write_list(items, path):
222 fp = self.opener(path, 'w')
222 fp = self.opener(path, 'w')
223 for i in items:
223 for i in items:
224 print >> fp, i
224 print >> fp, i
225 fp.close()
225 fp.close()
226 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
226 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
227 if self.series_dirty: write_list(self.full_series, self.series_path)
227 if self.series_dirty: write_list(self.full_series, self.series_path)
228 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
228 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
229
229
230 def readheaders(self, patch):
230 def readheaders(self, patch):
231 def eatdiff(lines):
231 def eatdiff(lines):
232 while lines:
232 while lines:
233 l = lines[-1]
233 l = lines[-1]
234 if (l.startswith("diff -") or
234 if (l.startswith("diff -") or
235 l.startswith("Index:") or
235 l.startswith("Index:") or
236 l.startswith("===========")):
236 l.startswith("===========")):
237 del lines[-1]
237 del lines[-1]
238 else:
238 else:
239 break
239 break
240 def eatempty(lines):
240 def eatempty(lines):
241 while lines:
241 while lines:
242 l = lines[-1]
242 l = lines[-1]
243 if re.match('\s*$', l):
243 if re.match('\s*$', l):
244 del lines[-1]
244 del lines[-1]
245 else:
245 else:
246 break
246 break
247
247
248 pf = self.join(patch)
248 pf = self.join(patch)
249 message = []
249 message = []
250 comments = []
250 comments = []
251 user = None
251 user = None
252 date = None
252 date = None
253 format = None
253 format = None
254 subject = None
254 subject = None
255 diffstart = 0
255 diffstart = 0
256
256
257 for line in file(pf):
257 for line in file(pf):
258 line = line.rstrip()
258 line = line.rstrip()
259 if line.startswith('diff --git'):
259 if line.startswith('diff --git'):
260 diffstart = 2
260 diffstart = 2
261 break
261 break
262 if diffstart:
262 if diffstart:
263 if line.startswith('+++ '):
263 if line.startswith('+++ '):
264 diffstart = 2
264 diffstart = 2
265 break
265 break
266 if line.startswith("--- "):
266 if line.startswith("--- "):
267 diffstart = 1
267 diffstart = 1
268 continue
268 continue
269 elif format == "hgpatch":
269 elif format == "hgpatch":
270 # parse values when importing the result of an hg export
270 # parse values when importing the result of an hg export
271 if line.startswith("# User "):
271 if line.startswith("# User "):
272 user = line[7:]
272 user = line[7:]
273 elif line.startswith("# Date "):
273 elif line.startswith("# Date "):
274 date = line[7:]
274 date = line[7:]
275 elif not line.startswith("# ") and line:
275 elif not line.startswith("# ") and line:
276 message.append(line)
276 message.append(line)
277 format = None
277 format = None
278 elif line == '# HG changeset patch':
278 elif line == '# HG changeset patch':
279 format = "hgpatch"
279 format = "hgpatch"
280 elif (format != "tagdone" and (line.startswith("Subject: ") or
280 elif (format != "tagdone" and (line.startswith("Subject: ") or
281 line.startswith("subject: "))):
281 line.startswith("subject: "))):
282 subject = line[9:]
282 subject = line[9:]
283 format = "tag"
283 format = "tag"
284 elif (format != "tagdone" and (line.startswith("From: ") or
284 elif (format != "tagdone" and (line.startswith("From: ") or
285 line.startswith("from: "))):
285 line.startswith("from: "))):
286 user = line[6:]
286 user = line[6:]
287 format = "tag"
287 format = "tag"
288 elif format == "tag" and line == "":
288 elif format == "tag" and line == "":
289 # when looking for tags (subject: from: etc) they
289 # when looking for tags (subject: from: etc) they
290 # end once you find a blank line in the source
290 # end once you find a blank line in the source
291 format = "tagdone"
291 format = "tagdone"
292 elif message or line:
292 elif message or line:
293 message.append(line)
293 message.append(line)
294 comments.append(line)
294 comments.append(line)
295
295
296 eatdiff(message)
296 eatdiff(message)
297 eatdiff(comments)
297 eatdiff(comments)
298 eatempty(message)
298 eatempty(message)
299 eatempty(comments)
299 eatempty(comments)
300
300
301 # make sure message isn't empty
301 # make sure message isn't empty
302 if format and format.startswith("tag") and subject:
302 if format and format.startswith("tag") and subject:
303 message.insert(0, "")
303 message.insert(0, "")
304 message.insert(0, subject)
304 message.insert(0, subject)
305 return (message, comments, user, date, diffstart > 1)
305 return (message, comments, user, date, diffstart > 1)
306
306
307 def printdiff(self, repo, node1, node2=None, files=None,
307 def printdiff(self, repo, node1, node2=None, files=None,
308 fp=None, changes=None, opts={}):
308 fp=None, changes=None, opts={}):
309 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
309 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
310
310
311 patch.diff(repo, node1, node2, fns, match=matchfn,
311 patch.diff(repo, node1, node2, fns, match=matchfn,
312 fp=fp, changes=changes, opts=self.diffopts())
312 fp=fp, changes=changes, opts=self.diffopts())
313
313
314 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
314 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
315 # first try just applying the patch
315 # first try just applying the patch
316 (err, n) = self.apply(repo, [ patch ], update_status=False,
316 (err, n) = self.apply(repo, [ patch ], update_status=False,
317 strict=True, merge=rev, wlock=wlock)
317 strict=True, merge=rev, wlock=wlock)
318
318
319 if err == 0:
319 if err == 0:
320 return (err, n)
320 return (err, n)
321
321
322 if n is None:
322 if n is None:
323 raise util.Abort(_("apply failed for patch %s") % patch)
323 raise util.Abort(_("apply failed for patch %s") % patch)
324
324
325 self.ui.warn("patch didn't work out, merging %s\n" % patch)
325 self.ui.warn("patch didn't work out, merging %s\n" % patch)
326
326
327 # apply failed, strip away that rev and merge.
327 # apply failed, strip away that rev and merge.
328 hg.clean(repo, head, wlock=wlock)
328 hg.clean(repo, head, wlock=wlock)
329 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
329 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
330
330
331 c = repo.changelog.read(rev)
331 c = repo.changelog.read(rev)
332 ret = hg.merge(repo, rev, wlock=wlock)
332 ret = hg.merge(repo, rev, wlock=wlock)
333 if ret:
333 if ret:
334 raise util.Abort(_("update returned %d") % ret)
334 raise util.Abort(_("update returned %d") % ret)
335 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
335 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
336 if n == None:
336 if n == None:
337 raise util.Abort(_("repo commit failed"))
337 raise util.Abort(_("repo commit failed"))
338 try:
338 try:
339 message, comments, user, date, patchfound = mergeq.readheaders(patch)
339 message, comments, user, date, patchfound = mergeq.readheaders(patch)
340 except:
340 except:
341 raise util.Abort(_("unable to read %s") % patch)
341 raise util.Abort(_("unable to read %s") % patch)
342
342
343 patchf = self.opener(patch, "w")
343 patchf = self.opener(patch, "w")
344 if comments:
344 if comments:
345 comments = "\n".join(comments) + '\n\n'
345 comments = "\n".join(comments) + '\n\n'
346 patchf.write(comments)
346 patchf.write(comments)
347 self.printdiff(repo, head, n, fp=patchf)
347 self.printdiff(repo, head, n, fp=patchf)
348 patchf.close()
348 patchf.close()
349 return (0, n)
349 return (0, n)
350
350
351 def qparents(self, repo, rev=None):
351 def qparents(self, repo, rev=None):
352 if rev is None:
352 if rev is None:
353 (p1, p2) = repo.dirstate.parents()
353 (p1, p2) = repo.dirstate.parents()
354 if p2 == revlog.nullid:
354 if p2 == revlog.nullid:
355 return p1
355 return p1
356 if len(self.applied) == 0:
356 if len(self.applied) == 0:
357 return None
357 return None
358 return revlog.bin(self.applied[-1].rev)
358 return revlog.bin(self.applied[-1].rev)
359 pp = repo.changelog.parents(rev)
359 pp = repo.changelog.parents(rev)
360 if pp[1] != revlog.nullid:
360 if pp[1] != revlog.nullid:
361 arevs = [ x.rev for x in self.applied ]
361 arevs = [ x.rev for x in self.applied ]
362 p0 = revlog.hex(pp[0])
362 p0 = revlog.hex(pp[0])
363 p1 = revlog.hex(pp[1])
363 p1 = revlog.hex(pp[1])
364 if p0 in arevs:
364 if p0 in arevs:
365 return pp[0]
365 return pp[0]
366 if p1 in arevs:
366 if p1 in arevs:
367 return pp[1]
367 return pp[1]
368 return pp[0]
368 return pp[0]
369
369
370 def mergepatch(self, repo, mergeq, series, wlock):
370 def mergepatch(self, repo, mergeq, series, wlock):
371 if len(self.applied) == 0:
371 if len(self.applied) == 0:
372 # each of the patches merged in will have two parents. This
372 # each of the patches merged in will have two parents. This
373 # can confuse the qrefresh, qdiff, and strip code because it
373 # can confuse the qrefresh, qdiff, and strip code because it
374 # needs to know which parent is actually in the patch queue.
374 # needs to know which parent is actually in the patch queue.
375 # so, we insert a merge marker with only one parent. This way
375 # so, we insert a merge marker with only one parent. This way
376 # the first patch in the queue is never a merge patch
376 # the first patch in the queue is never a merge patch
377 #
377 #
378 pname = ".hg.patches.merge.marker"
378 pname = ".hg.patches.merge.marker"
379 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
379 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
380 wlock=wlock)
380 wlock=wlock)
381 self.applied.append(statusentry(revlog.hex(n), pname))
381 self.applied.append(statusentry(revlog.hex(n), pname))
382 self.applied_dirty = 1
382 self.applied_dirty = 1
383
383
384 head = self.qparents(repo)
384 head = self.qparents(repo)
385
385
386 for patch in series:
386 for patch in series:
387 patch = mergeq.lookup(patch, strict=True)
387 patch = mergeq.lookup(patch, strict=True)
388 if not patch:
388 if not patch:
389 self.ui.warn("patch %s does not exist\n" % patch)
389 self.ui.warn("patch %s does not exist\n" % patch)
390 return (1, None)
390 return (1, None)
391 pushable, reason = self.pushable(patch)
391 pushable, reason = self.pushable(patch)
392 if not pushable:
392 if not pushable:
393 self.explain_pushable(patch, all_patches=True)
393 self.explain_pushable(patch, all_patches=True)
394 continue
394 continue
395 info = mergeq.isapplied(patch)
395 info = mergeq.isapplied(patch)
396 if not info:
396 if not info:
397 self.ui.warn("patch %s is not applied\n" % patch)
397 self.ui.warn("patch %s is not applied\n" % patch)
398 return (1, None)
398 return (1, None)
399 rev = revlog.bin(info[1])
399 rev = revlog.bin(info[1])
400 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
400 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
401 if head:
401 if head:
402 self.applied.append(statusentry(revlog.hex(head), patch))
402 self.applied.append(statusentry(revlog.hex(head), patch))
403 self.applied_dirty = 1
403 self.applied_dirty = 1
404 if err:
404 if err:
405 return (err, head)
405 return (err, head)
406 return (0, head)
406 return (0, head)
407
407
408 def patch(self, repo, patchfile):
408 def patch(self, repo, patchfile):
409 '''Apply patchfile to the working directory.
409 '''Apply patchfile to the working directory.
410 patchfile: file name of patch'''
410 patchfile: file name of patch'''
411 files = {}
411 files = {}
412 try:
412 try:
413 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
413 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
414 files=files)
414 files=files)
415 except Exception, inst:
415 except Exception, inst:
416 self.ui.note(str(inst) + '\n')
416 self.ui.note(str(inst) + '\n')
417 if not self.ui.verbose:
417 if not self.ui.verbose:
418 self.ui.warn("patch failed, unable to continue (try -v)\n")
418 self.ui.warn("patch failed, unable to continue (try -v)\n")
419 return (False, files, False)
419 return (False, files, False)
420
420
421 return (True, files, fuzz)
421 return (True, files, fuzz)
422
422
423 def apply(self, repo, series, list=False, update_status=True,
423 def apply(self, repo, series, list=False, update_status=True,
424 strict=False, patchdir=None, merge=None, wlock=None):
424 strict=False, patchdir=None, merge=None, wlock=None):
425 # TODO unify with commands.py
425 # TODO unify with commands.py
426 if not patchdir:
426 if not patchdir:
427 patchdir = self.path
427 patchdir = self.path
428 err = 0
428 err = 0
429 if not wlock:
429 if not wlock:
430 wlock = repo.wlock()
430 wlock = repo.wlock()
431 lock = repo.lock()
431 lock = repo.lock()
432 tr = repo.transaction()
432 tr = repo.transaction()
433 n = None
433 n = None
434 for patchname in series:
434 for patchname in series:
435 pushable, reason = self.pushable(patchname)
435 pushable, reason = self.pushable(patchname)
436 if not pushable:
436 if not pushable:
437 self.explain_pushable(patchname, all_patches=True)
437 self.explain_pushable(patchname, all_patches=True)
438 continue
438 continue
439 self.ui.warn("applying %s\n" % patchname)
439 self.ui.warn("applying %s\n" % patchname)
440 pf = os.path.join(patchdir, patchname)
440 pf = os.path.join(patchdir, patchname)
441
441
442 try:
442 try:
443 message, comments, user, date, patchfound = self.readheaders(patchname)
443 message, comments, user, date, patchfound = self.readheaders(patchname)
444 except:
444 except:
445 self.ui.warn("Unable to read %s\n" % patchname)
445 self.ui.warn("Unable to read %s\n" % patchname)
446 err = 1
446 err = 1
447 break
447 break
448
448
449 if not message:
449 if not message:
450 message = "imported patch %s\n" % patchname
450 message = "imported patch %s\n" % patchname
451 else:
451 else:
452 if list:
452 if list:
453 message.append("\nimported patch %s" % patchname)
453 message.append("\nimported patch %s" % patchname)
454 message = '\n'.join(message)
454 message = '\n'.join(message)
455
455
456 (patcherr, files, fuzz) = self.patch(repo, pf)
456 (patcherr, files, fuzz) = self.patch(repo, pf)
457 patcherr = not patcherr
457 patcherr = not patcherr
458
458
459 if merge and files:
459 if merge and files:
460 # Mark as merged and update dirstate parent info
460 # Mark as merged and update dirstate parent info
461 repo.dirstate.update(repo.dirstate.filterfiles(files.keys()), 'm')
461 repo.dirstate.update(repo.dirstate.filterfiles(files.keys()), 'm')
462 p1, p2 = repo.dirstate.parents()
462 p1, p2 = repo.dirstate.parents()
463 repo.dirstate.setparents(p1, merge)
463 repo.dirstate.setparents(p1, merge)
464 files = patch.updatedir(self.ui, repo, files, wlock=wlock)
464 files = patch.updatedir(self.ui, repo, files, wlock=wlock)
465 n = repo.commit(files, message, user, date, force=1, lock=lock,
465 n = repo.commit(files, message, user, date, force=1, lock=lock,
466 wlock=wlock)
466 wlock=wlock)
467
467
468 if n == None:
468 if n == None:
469 raise util.Abort(_("repo commit failed"))
469 raise util.Abort(_("repo commit failed"))
470
470
471 if update_status:
471 if update_status:
472 self.applied.append(statusentry(revlog.hex(n), patchname))
472 self.applied.append(statusentry(revlog.hex(n), patchname))
473
473
474 if patcherr:
474 if patcherr:
475 if not patchfound:
475 if not patchfound:
476 self.ui.warn("patch %s is empty\n" % patchname)
476 self.ui.warn("patch %s is empty\n" % patchname)
477 err = 0
477 err = 0
478 else:
478 else:
479 self.ui.warn("patch failed, rejects left in working dir\n")
479 self.ui.warn("patch failed, rejects left in working dir\n")
480 err = 1
480 err = 1
481 break
481 break
482
482
483 if fuzz and strict:
483 if fuzz and strict:
484 self.ui.warn("fuzz found when applying patch, stopping\n")
484 self.ui.warn("fuzz found when applying patch, stopping\n")
485 err = 1
485 err = 1
486 break
486 break
487 tr.close()
487 tr.close()
488 return (err, n)
488 return (err, n)
489
489
490 def delete(self, repo, patches, opts):
490 def delete(self, repo, patches, opts):
491 realpatches = []
491 realpatches = []
492 for patch in patches:
492 for patch in patches:
493 patch = self.lookup(patch, strict=True)
493 patch = self.lookup(patch, strict=True)
494 info = self.isapplied(patch)
494 info = self.isapplied(patch)
495 if info:
495 if info:
496 raise util.Abort(_("cannot delete applied patch %s") % patch)
496 raise util.Abort(_("cannot delete applied patch %s") % patch)
497 if patch not in self.series:
497 if patch not in self.series:
498 raise util.Abort(_("patch %s not in series file") % patch)
498 raise util.Abort(_("patch %s not in series file") % patch)
499 realpatches.append(patch)
499 realpatches.append(patch)
500
500
501 appliedbase = 0
501 appliedbase = 0
502 if opts.get('rev'):
502 if opts.get('rev'):
503 if not self.applied:
503 if not self.applied:
504 raise util.Abort(_('no patches applied'))
504 raise util.Abort(_('no patches applied'))
505 revs = [int(r) for r in cmdutil.revrange(ui, repo, opts['rev'])]
505 revs = cmdutil.revrange(ui, repo, opts['rev'])
506 if len(revs) > 1 and revs[0] > revs[1]:
506 if len(revs) > 1 and revs[0] > revs[1]:
507 revs.reverse()
507 revs.reverse()
508 for rev in revs:
508 for rev in revs:
509 if appliedbase >= len(self.applied):
509 if appliedbase >= len(self.applied):
510 raise util.Abort(_("revision %d is not managed") % rev)
510 raise util.Abort(_("revision %d is not managed") % rev)
511
511
512 base = revlog.bin(self.applied[appliedbase].rev)
512 base = revlog.bin(self.applied[appliedbase].rev)
513 node = repo.changelog.node(rev)
513 node = repo.changelog.node(rev)
514 if node != base:
514 if node != base:
515 raise util.Abort(_("cannot delete revision %d above "
515 raise util.Abort(_("cannot delete revision %d above "
516 "applied patches") % rev)
516 "applied patches") % rev)
517 realpatches.append(self.applied[appliedbase].name)
517 realpatches.append(self.applied[appliedbase].name)
518 appliedbase += 1
518 appliedbase += 1
519
519
520 if not opts.get('keep'):
520 if not opts.get('keep'):
521 r = self.qrepo()
521 r = self.qrepo()
522 if r:
522 if r:
523 r.remove(realpatches, True)
523 r.remove(realpatches, True)
524 else:
524 else:
525 for p in realpatches:
525 for p in realpatches:
526 os.unlink(self.join(p))
526 os.unlink(self.join(p))
527
527
528 if appliedbase:
528 if appliedbase:
529 del self.applied[:appliedbase]
529 del self.applied[:appliedbase]
530 self.applied_dirty = 1
530 self.applied_dirty = 1
531 indices = [self.find_series(p) for p in realpatches]
531 indices = [self.find_series(p) for p in realpatches]
532 indices.sort()
532 indices.sort()
533 for i in indices[-1::-1]:
533 for i in indices[-1::-1]:
534 del self.full_series[i]
534 del self.full_series[i]
535 self.parse_series()
535 self.parse_series()
536 self.series_dirty = 1
536 self.series_dirty = 1
537
537
538 def check_toppatch(self, repo):
538 def check_toppatch(self, repo):
539 if len(self.applied) > 0:
539 if len(self.applied) > 0:
540 top = revlog.bin(self.applied[-1].rev)
540 top = revlog.bin(self.applied[-1].rev)
541 pp = repo.dirstate.parents()
541 pp = repo.dirstate.parents()
542 if top not in pp:
542 if top not in pp:
543 raise util.Abort(_("queue top not at same revision as working directory"))
543 raise util.Abort(_("queue top not at same revision as working directory"))
544 return top
544 return top
545 return None
545 return None
546 def check_localchanges(self, repo, force=False, refresh=True):
546 def check_localchanges(self, repo, force=False, refresh=True):
547 m, a, r, d = repo.status()[:4]
547 m, a, r, d = repo.status()[:4]
548 if m or a or r or d:
548 if m or a or r or d:
549 if not force:
549 if not force:
550 if refresh:
550 if refresh:
551 raise util.Abort(_("local changes found, refresh first"))
551 raise util.Abort(_("local changes found, refresh first"))
552 else:
552 else:
553 raise util.Abort(_("local changes found"))
553 raise util.Abort(_("local changes found"))
554 return m, a, r, d
554 return m, a, r, d
555 def new(self, repo, patch, msg=None, force=None):
555 def new(self, repo, patch, msg=None, force=None):
556 if os.path.exists(self.join(patch)):
556 if os.path.exists(self.join(patch)):
557 raise util.Abort(_('patch "%s" already exists') % patch)
557 raise util.Abort(_('patch "%s" already exists') % patch)
558 m, a, r, d = self.check_localchanges(repo, force)
558 m, a, r, d = self.check_localchanges(repo, force)
559 commitfiles = m + a + r
559 commitfiles = m + a + r
560 self.check_toppatch(repo)
560 self.check_toppatch(repo)
561 wlock = repo.wlock()
561 wlock = repo.wlock()
562 insert = self.full_series_end()
562 insert = self.full_series_end()
563 if msg:
563 if msg:
564 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
564 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
565 wlock=wlock)
565 wlock=wlock)
566 else:
566 else:
567 n = repo.commit(commitfiles,
567 n = repo.commit(commitfiles,
568 "New patch: %s" % patch, force=True, wlock=wlock)
568 "New patch: %s" % patch, force=True, wlock=wlock)
569 if n == None:
569 if n == None:
570 raise util.Abort(_("repo commit failed"))
570 raise util.Abort(_("repo commit failed"))
571 self.full_series[insert:insert] = [patch]
571 self.full_series[insert:insert] = [patch]
572 self.applied.append(statusentry(revlog.hex(n), patch))
572 self.applied.append(statusentry(revlog.hex(n), patch))
573 self.parse_series()
573 self.parse_series()
574 self.series_dirty = 1
574 self.series_dirty = 1
575 self.applied_dirty = 1
575 self.applied_dirty = 1
576 p = self.opener(patch, "w")
576 p = self.opener(patch, "w")
577 if msg:
577 if msg:
578 msg = msg + "\n"
578 msg = msg + "\n"
579 p.write(msg)
579 p.write(msg)
580 p.close()
580 p.close()
581 wlock = None
581 wlock = None
582 r = self.qrepo()
582 r = self.qrepo()
583 if r: r.add([patch])
583 if r: r.add([patch])
584 if commitfiles:
584 if commitfiles:
585 self.refresh(repo, short=True)
585 self.refresh(repo, short=True)
586
586
587 def strip(self, repo, rev, update=True, backup="all", wlock=None):
587 def strip(self, repo, rev, update=True, backup="all", wlock=None):
588 def limitheads(chlog, stop):
588 def limitheads(chlog, stop):
589 """return the list of all nodes that have no children"""
589 """return the list of all nodes that have no children"""
590 p = {}
590 p = {}
591 h = []
591 h = []
592 stoprev = 0
592 stoprev = 0
593 if stop in chlog.nodemap:
593 if stop in chlog.nodemap:
594 stoprev = chlog.rev(stop)
594 stoprev = chlog.rev(stop)
595
595
596 for r in xrange(chlog.count() - 1, -1, -1):
596 for r in xrange(chlog.count() - 1, -1, -1):
597 n = chlog.node(r)
597 n = chlog.node(r)
598 if n not in p:
598 if n not in p:
599 h.append(n)
599 h.append(n)
600 if n == stop:
600 if n == stop:
601 break
601 break
602 if r < stoprev:
602 if r < stoprev:
603 break
603 break
604 for pn in chlog.parents(n):
604 for pn in chlog.parents(n):
605 p[pn] = 1
605 p[pn] = 1
606 return h
606 return h
607
607
608 def bundle(cg):
608 def bundle(cg):
609 backupdir = repo.join("strip-backup")
609 backupdir = repo.join("strip-backup")
610 if not os.path.isdir(backupdir):
610 if not os.path.isdir(backupdir):
611 os.mkdir(backupdir)
611 os.mkdir(backupdir)
612 name = os.path.join(backupdir, "%s" % revlog.short(rev))
612 name = os.path.join(backupdir, "%s" % revlog.short(rev))
613 name = savename(name)
613 name = savename(name)
614 self.ui.warn("saving bundle to %s\n" % name)
614 self.ui.warn("saving bundle to %s\n" % name)
615 # TODO, exclusive open
615 # TODO, exclusive open
616 f = open(name, "wb")
616 f = open(name, "wb")
617 try:
617 try:
618 f.write("HG10")
618 f.write("HG10")
619 z = bz2.BZ2Compressor(9)
619 z = bz2.BZ2Compressor(9)
620 while 1:
620 while 1:
621 chunk = cg.read(4096)
621 chunk = cg.read(4096)
622 if not chunk:
622 if not chunk:
623 break
623 break
624 f.write(z.compress(chunk))
624 f.write(z.compress(chunk))
625 f.write(z.flush())
625 f.write(z.flush())
626 except:
626 except:
627 os.unlink(name)
627 os.unlink(name)
628 raise
628 raise
629 f.close()
629 f.close()
630 return name
630 return name
631
631
632 def stripall(rev, revnum):
632 def stripall(rev, revnum):
633 cl = repo.changelog
633 cl = repo.changelog
634 c = cl.read(rev)
634 c = cl.read(rev)
635 mm = repo.manifest.read(c[0])
635 mm = repo.manifest.read(c[0])
636 seen = {}
636 seen = {}
637
637
638 for x in xrange(revnum, cl.count()):
638 for x in xrange(revnum, cl.count()):
639 c = cl.read(cl.node(x))
639 c = cl.read(cl.node(x))
640 for f in c[3]:
640 for f in c[3]:
641 if f in seen:
641 if f in seen:
642 continue
642 continue
643 seen[f] = 1
643 seen[f] = 1
644 if f in mm:
644 if f in mm:
645 filerev = mm[f]
645 filerev = mm[f]
646 else:
646 else:
647 filerev = 0
647 filerev = 0
648 seen[f] = filerev
648 seen[f] = filerev
649 # we go in two steps here so the strip loop happens in a
649 # we go in two steps here so the strip loop happens in a
650 # sensible order. When stripping many files, this helps keep
650 # sensible order. When stripping many files, this helps keep
651 # our disk access patterns under control.
651 # our disk access patterns under control.
652 seen_list = seen.keys()
652 seen_list = seen.keys()
653 seen_list.sort()
653 seen_list.sort()
654 for f in seen_list:
654 for f in seen_list:
655 ff = repo.file(f)
655 ff = repo.file(f)
656 filerev = seen[f]
656 filerev = seen[f]
657 if filerev != 0:
657 if filerev != 0:
658 if filerev in ff.nodemap:
658 if filerev in ff.nodemap:
659 filerev = ff.rev(filerev)
659 filerev = ff.rev(filerev)
660 else:
660 else:
661 filerev = 0
661 filerev = 0
662 ff.strip(filerev, revnum)
662 ff.strip(filerev, revnum)
663
663
664 if not wlock:
664 if not wlock:
665 wlock = repo.wlock()
665 wlock = repo.wlock()
666 lock = repo.lock()
666 lock = repo.lock()
667 chlog = repo.changelog
667 chlog = repo.changelog
668 # TODO delete the undo files, and handle undo of merge sets
668 # TODO delete the undo files, and handle undo of merge sets
669 pp = chlog.parents(rev)
669 pp = chlog.parents(rev)
670 revnum = chlog.rev(rev)
670 revnum = chlog.rev(rev)
671
671
672 if update:
672 if update:
673 self.check_localchanges(repo, refresh=False)
673 self.check_localchanges(repo, refresh=False)
674 urev = self.qparents(repo, rev)
674 urev = self.qparents(repo, rev)
675 hg.clean(repo, urev, wlock=wlock)
675 hg.clean(repo, urev, wlock=wlock)
676 repo.dirstate.write()
676 repo.dirstate.write()
677
677
678 # save is a list of all the branches we are truncating away
678 # save is a list of all the branches we are truncating away
679 # that we actually want to keep. changegroup will be used
679 # that we actually want to keep. changegroup will be used
680 # to preserve them and add them back after the truncate
680 # to preserve them and add them back after the truncate
681 saveheads = []
681 saveheads = []
682 savebases = {}
682 savebases = {}
683
683
684 heads = limitheads(chlog, rev)
684 heads = limitheads(chlog, rev)
685 seen = {}
685 seen = {}
686
686
687 # search through all the heads, finding those where the revision
687 # search through all the heads, finding those where the revision
688 # we want to strip away is an ancestor. Also look for merges
688 # we want to strip away is an ancestor. Also look for merges
689 # that might be turned into new heads by the strip.
689 # that might be turned into new heads by the strip.
690 while heads:
690 while heads:
691 h = heads.pop()
691 h = heads.pop()
692 n = h
692 n = h
693 while True:
693 while True:
694 seen[n] = 1
694 seen[n] = 1
695 pp = chlog.parents(n)
695 pp = chlog.parents(n)
696 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
696 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
697 if pp[1] not in seen:
697 if pp[1] not in seen:
698 heads.append(pp[1])
698 heads.append(pp[1])
699 if pp[0] == revlog.nullid:
699 if pp[0] == revlog.nullid:
700 break
700 break
701 if chlog.rev(pp[0]) < revnum:
701 if chlog.rev(pp[0]) < revnum:
702 break
702 break
703 n = pp[0]
703 n = pp[0]
704 if n == rev:
704 if n == rev:
705 break
705 break
706 r = chlog.reachable(h, rev)
706 r = chlog.reachable(h, rev)
707 if rev not in r:
707 if rev not in r:
708 saveheads.append(h)
708 saveheads.append(h)
709 for x in r:
709 for x in r:
710 if chlog.rev(x) > revnum:
710 if chlog.rev(x) > revnum:
711 savebases[x] = 1
711 savebases[x] = 1
712
712
713 # create a changegroup for all the branches we need to keep
713 # create a changegroup for all the branches we need to keep
714 if backup == "all":
714 if backup == "all":
715 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
715 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
716 bundle(backupch)
716 bundle(backupch)
717 if saveheads:
717 if saveheads:
718 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
718 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
719 chgrpfile = bundle(backupch)
719 chgrpfile = bundle(backupch)
720
720
721 stripall(rev, revnum)
721 stripall(rev, revnum)
722
722
723 change = chlog.read(rev)
723 change = chlog.read(rev)
724 chlog.strip(revnum, revnum)
724 chlog.strip(revnum, revnum)
725 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
725 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
726 if saveheads:
726 if saveheads:
727 self.ui.status("adding branch\n")
727 self.ui.status("adding branch\n")
728 commands.unbundle(self.ui, repo, chgrpfile, update=False)
728 commands.unbundle(self.ui, repo, chgrpfile, update=False)
729 if backup != "strip":
729 if backup != "strip":
730 os.unlink(chgrpfile)
730 os.unlink(chgrpfile)
731
731
732 def isapplied(self, patch):
732 def isapplied(self, patch):
733 """returns (index, rev, patch)"""
733 """returns (index, rev, patch)"""
734 for i in xrange(len(self.applied)):
734 for i in xrange(len(self.applied)):
735 a = self.applied[i]
735 a = self.applied[i]
736 if a.name == patch:
736 if a.name == patch:
737 return (i, a.rev, a.name)
737 return (i, a.rev, a.name)
738 return None
738 return None
739
739
740 # if the exact patch name does not exist, we try a few
740 # if the exact patch name does not exist, we try a few
741 # variations. If strict is passed, we try only #1
741 # variations. If strict is passed, we try only #1
742 #
742 #
743 # 1) a number to indicate an offset in the series file
743 # 1) a number to indicate an offset in the series file
744 # 2) a unique substring of the patch name was given
744 # 2) a unique substring of the patch name was given
745 # 3) patchname[-+]num to indicate an offset in the series file
745 # 3) patchname[-+]num to indicate an offset in the series file
746 def lookup(self, patch, strict=False):
746 def lookup(self, patch, strict=False):
747 patch = patch and str(patch)
747 patch = patch and str(patch)
748
748
749 def partial_name(s):
749 def partial_name(s):
750 if s in self.series:
750 if s in self.series:
751 return s
751 return s
752 matches = [x for x in self.series if s in x]
752 matches = [x for x in self.series if s in x]
753 if len(matches) > 1:
753 if len(matches) > 1:
754 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
754 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
755 for m in matches:
755 for m in matches:
756 self.ui.warn(' %s\n' % m)
756 self.ui.warn(' %s\n' % m)
757 return None
757 return None
758 if matches:
758 if matches:
759 return matches[0]
759 return matches[0]
760 if len(self.series) > 0 and len(self.applied) > 0:
760 if len(self.series) > 0 and len(self.applied) > 0:
761 if s == 'qtip':
761 if s == 'qtip':
762 return self.series[self.series_end()-1]
762 return self.series[self.series_end()-1]
763 if s == 'qbase':
763 if s == 'qbase':
764 return self.series[0]
764 return self.series[0]
765 return None
765 return None
766 if patch == None:
766 if patch == None:
767 return None
767 return None
768
768
769 # we don't want to return a partial match until we make
769 # we don't want to return a partial match until we make
770 # sure the file name passed in does not exist (checked below)
770 # sure the file name passed in does not exist (checked below)
771 res = partial_name(patch)
771 res = partial_name(patch)
772 if res and res == patch:
772 if res and res == patch:
773 return res
773 return res
774
774
775 if not os.path.isfile(self.join(patch)):
775 if not os.path.isfile(self.join(patch)):
776 try:
776 try:
777 sno = int(patch)
777 sno = int(patch)
778 except(ValueError, OverflowError):
778 except(ValueError, OverflowError):
779 pass
779 pass
780 else:
780 else:
781 if sno < len(self.series):
781 if sno < len(self.series):
782 return self.series[sno]
782 return self.series[sno]
783 if not strict:
783 if not strict:
784 # return any partial match made above
784 # return any partial match made above
785 if res:
785 if res:
786 return res
786 return res
787 minus = patch.rfind('-')
787 minus = patch.rfind('-')
788 if minus >= 0:
788 if minus >= 0:
789 res = partial_name(patch[:minus])
789 res = partial_name(patch[:minus])
790 if res:
790 if res:
791 i = self.series.index(res)
791 i = self.series.index(res)
792 try:
792 try:
793 off = int(patch[minus+1:] or 1)
793 off = int(patch[minus+1:] or 1)
794 except(ValueError, OverflowError):
794 except(ValueError, OverflowError):
795 pass
795 pass
796 else:
796 else:
797 if i - off >= 0:
797 if i - off >= 0:
798 return self.series[i - off]
798 return self.series[i - off]
799 plus = patch.rfind('+')
799 plus = patch.rfind('+')
800 if plus >= 0:
800 if plus >= 0:
801 res = partial_name(patch[:plus])
801 res = partial_name(patch[:plus])
802 if res:
802 if res:
803 i = self.series.index(res)
803 i = self.series.index(res)
804 try:
804 try:
805 off = int(patch[plus+1:] or 1)
805 off = int(patch[plus+1:] or 1)
806 except(ValueError, OverflowError):
806 except(ValueError, OverflowError):
807 pass
807 pass
808 else:
808 else:
809 if i + off < len(self.series):
809 if i + off < len(self.series):
810 return self.series[i + off]
810 return self.series[i + off]
811 raise util.Abort(_("patch %s not in series") % patch)
811 raise util.Abort(_("patch %s not in series") % patch)
812
812
813 def push(self, repo, patch=None, force=False, list=False,
813 def push(self, repo, patch=None, force=False, list=False,
814 mergeq=None, wlock=None):
814 mergeq=None, wlock=None):
815 if not wlock:
815 if not wlock:
816 wlock = repo.wlock()
816 wlock = repo.wlock()
817 patch = self.lookup(patch)
817 patch = self.lookup(patch)
818 if patch and self.isapplied(patch):
818 if patch and self.isapplied(patch):
819 raise util.Abort(_("patch %s is already applied") % patch)
819 raise util.Abort(_("patch %s is already applied") % patch)
820 if self.series_end() == len(self.series):
820 if self.series_end() == len(self.series):
821 raise util.Abort(_("patch series fully applied"))
821 raise util.Abort(_("patch series fully applied"))
822 if not force:
822 if not force:
823 self.check_localchanges(repo)
823 self.check_localchanges(repo)
824
824
825 self.applied_dirty = 1;
825 self.applied_dirty = 1;
826 start = self.series_end()
826 start = self.series_end()
827 if start > 0:
827 if start > 0:
828 self.check_toppatch(repo)
828 self.check_toppatch(repo)
829 if not patch:
829 if not patch:
830 patch = self.series[start]
830 patch = self.series[start]
831 end = start + 1
831 end = start + 1
832 else:
832 else:
833 end = self.series.index(patch, start) + 1
833 end = self.series.index(patch, start) + 1
834 s = self.series[start:end]
834 s = self.series[start:end]
835 if mergeq:
835 if mergeq:
836 ret = self.mergepatch(repo, mergeq, s, wlock)
836 ret = self.mergepatch(repo, mergeq, s, wlock)
837 else:
837 else:
838 ret = self.apply(repo, s, list, wlock=wlock)
838 ret = self.apply(repo, s, list, wlock=wlock)
839 top = self.applied[-1].name
839 top = self.applied[-1].name
840 if ret[0]:
840 if ret[0]:
841 self.ui.write("Errors during apply, please fix and refresh %s\n" %
841 self.ui.write("Errors during apply, please fix and refresh %s\n" %
842 top)
842 top)
843 else:
843 else:
844 self.ui.write("Now at: %s\n" % top)
844 self.ui.write("Now at: %s\n" % top)
845 return ret[0]
845 return ret[0]
846
846
847 def pop(self, repo, patch=None, force=False, update=True, all=False,
847 def pop(self, repo, patch=None, force=False, update=True, all=False,
848 wlock=None):
848 wlock=None):
849 def getfile(f, rev):
849 def getfile(f, rev):
850 t = repo.file(f).read(rev)
850 t = repo.file(f).read(rev)
851 try:
851 try:
852 repo.wfile(f, "w").write(t)
852 repo.wfile(f, "w").write(t)
853 except IOError:
853 except IOError:
854 try:
854 try:
855 os.makedirs(os.path.dirname(repo.wjoin(f)))
855 os.makedirs(os.path.dirname(repo.wjoin(f)))
856 except OSError, err:
856 except OSError, err:
857 if err.errno != errno.EEXIST: raise
857 if err.errno != errno.EEXIST: raise
858 repo.wfile(f, "w").write(t)
858 repo.wfile(f, "w").write(t)
859
859
860 if not wlock:
860 if not wlock:
861 wlock = repo.wlock()
861 wlock = repo.wlock()
862 if patch:
862 if patch:
863 # index, rev, patch
863 # index, rev, patch
864 info = self.isapplied(patch)
864 info = self.isapplied(patch)
865 if not info:
865 if not info:
866 patch = self.lookup(patch)
866 patch = self.lookup(patch)
867 info = self.isapplied(patch)
867 info = self.isapplied(patch)
868 if not info:
868 if not info:
869 raise util.Abort(_("patch %s is not applied") % patch)
869 raise util.Abort(_("patch %s is not applied") % patch)
870 if len(self.applied) == 0:
870 if len(self.applied) == 0:
871 raise util.Abort(_("no patches applied"))
871 raise util.Abort(_("no patches applied"))
872
872
873 if not update:
873 if not update:
874 parents = repo.dirstate.parents()
874 parents = repo.dirstate.parents()
875 rr = [ revlog.bin(x.rev) for x in self.applied ]
875 rr = [ revlog.bin(x.rev) for x in self.applied ]
876 for p in parents:
876 for p in parents:
877 if p in rr:
877 if p in rr:
878 self.ui.warn("qpop: forcing dirstate update\n")
878 self.ui.warn("qpop: forcing dirstate update\n")
879 update = True
879 update = True
880
880
881 if not force and update:
881 if not force and update:
882 self.check_localchanges(repo)
882 self.check_localchanges(repo)
883
883
884 self.applied_dirty = 1;
884 self.applied_dirty = 1;
885 end = len(self.applied)
885 end = len(self.applied)
886 if not patch:
886 if not patch:
887 if all:
887 if all:
888 popi = 0
888 popi = 0
889 else:
889 else:
890 popi = len(self.applied) - 1
890 popi = len(self.applied) - 1
891 else:
891 else:
892 popi = info[0] + 1
892 popi = info[0] + 1
893 if popi >= end:
893 if popi >= end:
894 self.ui.warn("qpop: %s is already at the top\n" % patch)
894 self.ui.warn("qpop: %s is already at the top\n" % patch)
895 return
895 return
896 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
896 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
897
897
898 start = info[0]
898 start = info[0]
899 rev = revlog.bin(info[1])
899 rev = revlog.bin(info[1])
900
900
901 # we know there are no local changes, so we can make a simplified
901 # we know there are no local changes, so we can make a simplified
902 # form of hg.update.
902 # form of hg.update.
903 if update:
903 if update:
904 top = self.check_toppatch(repo)
904 top = self.check_toppatch(repo)
905 qp = self.qparents(repo, rev)
905 qp = self.qparents(repo, rev)
906 changes = repo.changelog.read(qp)
906 changes = repo.changelog.read(qp)
907 mmap = repo.manifest.read(changes[0])
907 mmap = repo.manifest.read(changes[0])
908 m, a, r, d, u = repo.status(qp, top)[:5]
908 m, a, r, d, u = repo.status(qp, top)[:5]
909 if d:
909 if d:
910 raise util.Abort("deletions found between repo revs")
910 raise util.Abort("deletions found between repo revs")
911 for f in m:
911 for f in m:
912 getfile(f, mmap[f])
912 getfile(f, mmap[f])
913 for f in r:
913 for f in r:
914 getfile(f, mmap[f])
914 getfile(f, mmap[f])
915 util.set_exec(repo.wjoin(f), mmap.execf(f))
915 util.set_exec(repo.wjoin(f), mmap.execf(f))
916 repo.dirstate.update(m + r, 'n')
916 repo.dirstate.update(m + r, 'n')
917 for f in a:
917 for f in a:
918 try: os.unlink(repo.wjoin(f))
918 try: os.unlink(repo.wjoin(f))
919 except: raise
919 except: raise
920 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
920 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
921 except: pass
921 except: pass
922 if a:
922 if a:
923 repo.dirstate.forget(a)
923 repo.dirstate.forget(a)
924 repo.dirstate.setparents(qp, revlog.nullid)
924 repo.dirstate.setparents(qp, revlog.nullid)
925 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
925 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
926 del self.applied[start:end]
926 del self.applied[start:end]
927 if len(self.applied):
927 if len(self.applied):
928 self.ui.write("Now at: %s\n" % self.applied[-1].name)
928 self.ui.write("Now at: %s\n" % self.applied[-1].name)
929 else:
929 else:
930 self.ui.write("Patch queue now empty\n")
930 self.ui.write("Patch queue now empty\n")
931
931
932 def diff(self, repo, pats, opts):
932 def diff(self, repo, pats, opts):
933 top = self.check_toppatch(repo)
933 top = self.check_toppatch(repo)
934 if not top:
934 if not top:
935 self.ui.write("No patches applied\n")
935 self.ui.write("No patches applied\n")
936 return
936 return
937 qp = self.qparents(repo, top)
937 qp = self.qparents(repo, top)
938 self.printdiff(repo, qp, files=pats, opts=opts)
938 self.printdiff(repo, qp, files=pats, opts=opts)
939
939
940 def refresh(self, repo, pats=None, **opts):
940 def refresh(self, repo, pats=None, **opts):
941 if len(self.applied) == 0:
941 if len(self.applied) == 0:
942 self.ui.write("No patches applied\n")
942 self.ui.write("No patches applied\n")
943 return 1
943 return 1
944 wlock = repo.wlock()
944 wlock = repo.wlock()
945 self.check_toppatch(repo)
945 self.check_toppatch(repo)
946 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
946 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
947 top = revlog.bin(top)
947 top = revlog.bin(top)
948 cparents = repo.changelog.parents(top)
948 cparents = repo.changelog.parents(top)
949 patchparent = self.qparents(repo, top)
949 patchparent = self.qparents(repo, top)
950 message, comments, user, date, patchfound = self.readheaders(patchfn)
950 message, comments, user, date, patchfound = self.readheaders(patchfn)
951
951
952 patchf = self.opener(patchfn, "w")
952 patchf = self.opener(patchfn, "w")
953 msg = opts.get('msg', '').rstrip()
953 msg = opts.get('msg', '').rstrip()
954 if msg:
954 if msg:
955 if comments:
955 if comments:
956 # Remove existing message.
956 # Remove existing message.
957 ci = 0
957 ci = 0
958 for mi in xrange(len(message)):
958 for mi in xrange(len(message)):
959 while message[mi] != comments[ci]:
959 while message[mi] != comments[ci]:
960 ci += 1
960 ci += 1
961 del comments[ci]
961 del comments[ci]
962 comments.append(msg)
962 comments.append(msg)
963 if comments:
963 if comments:
964 comments = "\n".join(comments) + '\n\n'
964 comments = "\n".join(comments) + '\n\n'
965 patchf.write(comments)
965 patchf.write(comments)
966
966
967 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
967 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
968 tip = repo.changelog.tip()
968 tip = repo.changelog.tip()
969 if top == tip:
969 if top == tip:
970 # if the top of our patch queue is also the tip, there is an
970 # if the top of our patch queue is also the tip, there is an
971 # optimization here. We update the dirstate in place and strip
971 # optimization here. We update the dirstate in place and strip
972 # off the tip commit. Then just commit the current directory
972 # off the tip commit. Then just commit the current directory
973 # tree. We can also send repo.commit the list of files
973 # tree. We can also send repo.commit the list of files
974 # changed to speed up the diff
974 # changed to speed up the diff
975 #
975 #
976 # in short mode, we only diff the files included in the
976 # in short mode, we only diff the files included in the
977 # patch already
977 # patch already
978 #
978 #
979 # this should really read:
979 # this should really read:
980 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
980 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
981 # but we do it backwards to take advantage of manifest/chlog
981 # but we do it backwards to take advantage of manifest/chlog
982 # caching against the next repo.status call
982 # caching against the next repo.status call
983 #
983 #
984 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
984 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
985 if opts.get('short'):
985 if opts.get('short'):
986 filelist = mm + aa + dd
986 filelist = mm + aa + dd
987 else:
987 else:
988 filelist = None
988 filelist = None
989 m, a, r, d, u = repo.status(files=filelist)[:5]
989 m, a, r, d, u = repo.status(files=filelist)[:5]
990
990
991 # we might end up with files that were added between tip and
991 # we might end up with files that were added between tip and
992 # the dirstate parent, but then changed in the local dirstate.
992 # the dirstate parent, but then changed in the local dirstate.
993 # in this case, we want them to only show up in the added section
993 # in this case, we want them to only show up in the added section
994 for x in m:
994 for x in m:
995 if x not in aa:
995 if x not in aa:
996 mm.append(x)
996 mm.append(x)
997 # we might end up with files added by the local dirstate that
997 # we might end up with files added by the local dirstate that
998 # were deleted by the patch. In this case, they should only
998 # were deleted by the patch. In this case, they should only
999 # show up in the changed section.
999 # show up in the changed section.
1000 for x in a:
1000 for x in a:
1001 if x in dd:
1001 if x in dd:
1002 del dd[dd.index(x)]
1002 del dd[dd.index(x)]
1003 mm.append(x)
1003 mm.append(x)
1004 else:
1004 else:
1005 aa.append(x)
1005 aa.append(x)
1006 # make sure any files deleted in the local dirstate
1006 # make sure any files deleted in the local dirstate
1007 # are not in the add or change column of the patch
1007 # are not in the add or change column of the patch
1008 forget = []
1008 forget = []
1009 for x in d + r:
1009 for x in d + r:
1010 if x in aa:
1010 if x in aa:
1011 del aa[aa.index(x)]
1011 del aa[aa.index(x)]
1012 forget.append(x)
1012 forget.append(x)
1013 continue
1013 continue
1014 elif x in mm:
1014 elif x in mm:
1015 del mm[mm.index(x)]
1015 del mm[mm.index(x)]
1016 dd.append(x)
1016 dd.append(x)
1017
1017
1018 m = list(util.unique(mm))
1018 m = list(util.unique(mm))
1019 r = list(util.unique(dd))
1019 r = list(util.unique(dd))
1020 a = list(util.unique(aa))
1020 a = list(util.unique(aa))
1021 filelist = filter(matchfn, util.unique(m + r + a))
1021 filelist = filter(matchfn, util.unique(m + r + a))
1022 if opts.get('git'):
1022 if opts.get('git'):
1023 self.diffopts().git = True
1023 self.diffopts().git = True
1024 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1024 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1025 fp=patchf, changes=(m, a, r, [], u),
1025 fp=patchf, changes=(m, a, r, [], u),
1026 opts=self.diffopts())
1026 opts=self.diffopts())
1027 patchf.close()
1027 patchf.close()
1028
1028
1029 changes = repo.changelog.read(tip)
1029 changes = repo.changelog.read(tip)
1030 repo.dirstate.setparents(*cparents)
1030 repo.dirstate.setparents(*cparents)
1031 copies = [(f, repo.dirstate.copied(f)) for f in a]
1031 copies = [(f, repo.dirstate.copied(f)) for f in a]
1032 repo.dirstate.update(a, 'a')
1032 repo.dirstate.update(a, 'a')
1033 for dst, src in copies:
1033 for dst, src in copies:
1034 repo.dirstate.copy(src, dst)
1034 repo.dirstate.copy(src, dst)
1035 repo.dirstate.update(r, 'r')
1035 repo.dirstate.update(r, 'r')
1036 # if the patch excludes a modified file, mark that file with mtime=0
1036 # if the patch excludes a modified file, mark that file with mtime=0
1037 # so status can see it.
1037 # so status can see it.
1038 mm = []
1038 mm = []
1039 for i in xrange(len(m)-1, -1, -1):
1039 for i in xrange(len(m)-1, -1, -1):
1040 if not matchfn(m[i]):
1040 if not matchfn(m[i]):
1041 mm.append(m[i])
1041 mm.append(m[i])
1042 del m[i]
1042 del m[i]
1043 repo.dirstate.update(m, 'n')
1043 repo.dirstate.update(m, 'n')
1044 repo.dirstate.update(mm, 'n', st_mtime=0)
1044 repo.dirstate.update(mm, 'n', st_mtime=0)
1045 repo.dirstate.forget(forget)
1045 repo.dirstate.forget(forget)
1046
1046
1047 if not msg:
1047 if not msg:
1048 if not message:
1048 if not message:
1049 message = "patch queue: %s\n" % patchfn
1049 message = "patch queue: %s\n" % patchfn
1050 else:
1050 else:
1051 message = "\n".join(message)
1051 message = "\n".join(message)
1052 else:
1052 else:
1053 message = msg
1053 message = msg
1054
1054
1055 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1055 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1056 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
1056 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
1057 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1057 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1058 self.applied_dirty = 1
1058 self.applied_dirty = 1
1059 else:
1059 else:
1060 self.printdiff(repo, patchparent, fp=patchf)
1060 self.printdiff(repo, patchparent, fp=patchf)
1061 patchf.close()
1061 patchf.close()
1062 self.pop(repo, force=True, wlock=wlock)
1062 self.pop(repo, force=True, wlock=wlock)
1063 self.push(repo, force=True, wlock=wlock)
1063 self.push(repo, force=True, wlock=wlock)
1064
1064
1065 def init(self, repo, create=False):
1065 def init(self, repo, create=False):
1066 if os.path.isdir(self.path):
1066 if os.path.isdir(self.path):
1067 raise util.Abort(_("patch queue directory already exists"))
1067 raise util.Abort(_("patch queue directory already exists"))
1068 os.mkdir(self.path)
1068 os.mkdir(self.path)
1069 if create:
1069 if create:
1070 return self.qrepo(create=True)
1070 return self.qrepo(create=True)
1071
1071
1072 def unapplied(self, repo, patch=None):
1072 def unapplied(self, repo, patch=None):
1073 if patch and patch not in self.series:
1073 if patch and patch not in self.series:
1074 raise util.Abort(_("patch %s is not in series file") % patch)
1074 raise util.Abort(_("patch %s is not in series file") % patch)
1075 if not patch:
1075 if not patch:
1076 start = self.series_end()
1076 start = self.series_end()
1077 else:
1077 else:
1078 start = self.series.index(patch) + 1
1078 start = self.series.index(patch) + 1
1079 unapplied = []
1079 unapplied = []
1080 for i in xrange(start, len(self.series)):
1080 for i in xrange(start, len(self.series)):
1081 pushable, reason = self.pushable(i)
1081 pushable, reason = self.pushable(i)
1082 if pushable:
1082 if pushable:
1083 unapplied.append((i, self.series[i]))
1083 unapplied.append((i, self.series[i]))
1084 self.explain_pushable(i)
1084 self.explain_pushable(i)
1085 return unapplied
1085 return unapplied
1086
1086
1087 def qseries(self, repo, missing=None, start=0, length=0, status=None,
1087 def qseries(self, repo, missing=None, start=0, length=0, status=None,
1088 summary=False):
1088 summary=False):
1089 def displayname(patchname):
1089 def displayname(patchname):
1090 if summary:
1090 if summary:
1091 msg = self.readheaders(patchname)[0]
1091 msg = self.readheaders(patchname)[0]
1092 msg = msg and ': ' + msg[0] or ': '
1092 msg = msg and ': ' + msg[0] or ': '
1093 else:
1093 else:
1094 msg = ''
1094 msg = ''
1095 return '%s%s' % (patchname, msg)
1095 return '%s%s' % (patchname, msg)
1096
1096
1097 def pname(i):
1097 def pname(i):
1098 if status == 'A':
1098 if status == 'A':
1099 return self.applied[i].name
1099 return self.applied[i].name
1100 else:
1100 else:
1101 return self.series[i]
1101 return self.series[i]
1102
1102
1103 unapplied = self.series_end(all_patches=True)
1103 unapplied = self.series_end(all_patches=True)
1104 if not length:
1104 if not length:
1105 length = len(self.series) - start
1105 length = len(self.series) - start
1106 if not missing:
1106 if not missing:
1107 for i in xrange(start, start+length):
1107 for i in xrange(start, start+length):
1108 pfx = ''
1108 pfx = ''
1109 patch = pname(i)
1109 patch = pname(i)
1110 if self.ui.verbose:
1110 if self.ui.verbose:
1111 if i < unapplied:
1111 if i < unapplied:
1112 status = 'A'
1112 status = 'A'
1113 elif self.pushable(i)[0]:
1113 elif self.pushable(i)[0]:
1114 status = 'U'
1114 status = 'U'
1115 else:
1115 else:
1116 status = 'G'
1116 status = 'G'
1117 pfx = '%d %s ' % (i, status)
1117 pfx = '%d %s ' % (i, status)
1118 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1118 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1119 else:
1119 else:
1120 msng_list = []
1120 msng_list = []
1121 for root, dirs, files in os.walk(self.path):
1121 for root, dirs, files in os.walk(self.path):
1122 d = root[len(self.path) + 1:]
1122 d = root[len(self.path) + 1:]
1123 for f in files:
1123 for f in files:
1124 fl = os.path.join(d, f)
1124 fl = os.path.join(d, f)
1125 if (fl not in self.series and
1125 if (fl not in self.series and
1126 fl not in (self.status_path, self.series_path)
1126 fl not in (self.status_path, self.series_path)
1127 and not fl.startswith('.')):
1127 and not fl.startswith('.')):
1128 msng_list.append(fl)
1128 msng_list.append(fl)
1129 msng_list.sort()
1129 msng_list.sort()
1130 for x in msng_list:
1130 for x in msng_list:
1131 pfx = self.ui.verbose and ('D ') or ''
1131 pfx = self.ui.verbose and ('D ') or ''
1132 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1132 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1133
1133
1134 def issaveline(self, l):
1134 def issaveline(self, l):
1135 if l.name == '.hg.patches.save.line':
1135 if l.name == '.hg.patches.save.line':
1136 return True
1136 return True
1137
1137
1138 def qrepo(self, create=False):
1138 def qrepo(self, create=False):
1139 if create or os.path.isdir(self.join(".hg")):
1139 if create or os.path.isdir(self.join(".hg")):
1140 return hg.repository(self.ui, path=self.path, create=create)
1140 return hg.repository(self.ui, path=self.path, create=create)
1141
1141
1142 def restore(self, repo, rev, delete=None, qupdate=None):
1142 def restore(self, repo, rev, delete=None, qupdate=None):
1143 c = repo.changelog.read(rev)
1143 c = repo.changelog.read(rev)
1144 desc = c[4].strip()
1144 desc = c[4].strip()
1145 lines = desc.splitlines()
1145 lines = desc.splitlines()
1146 i = 0
1146 i = 0
1147 datastart = None
1147 datastart = None
1148 series = []
1148 series = []
1149 applied = []
1149 applied = []
1150 qpp = None
1150 qpp = None
1151 for i in xrange(0, len(lines)):
1151 for i in xrange(0, len(lines)):
1152 if lines[i] == 'Patch Data:':
1152 if lines[i] == 'Patch Data:':
1153 datastart = i + 1
1153 datastart = i + 1
1154 elif lines[i].startswith('Dirstate:'):
1154 elif lines[i].startswith('Dirstate:'):
1155 l = lines[i].rstrip()
1155 l = lines[i].rstrip()
1156 l = l[10:].split(' ')
1156 l = l[10:].split(' ')
1157 qpp = [ hg.bin(x) for x in l ]
1157 qpp = [ hg.bin(x) for x in l ]
1158 elif datastart != None:
1158 elif datastart != None:
1159 l = lines[i].rstrip()
1159 l = lines[i].rstrip()
1160 se = statusentry(l)
1160 se = statusentry(l)
1161 file_ = se.name
1161 file_ = se.name
1162 if se.rev:
1162 if se.rev:
1163 applied.append(se)
1163 applied.append(se)
1164 else:
1164 else:
1165 series.append(file_)
1165 series.append(file_)
1166 if datastart == None:
1166 if datastart == None:
1167 self.ui.warn("No saved patch data found\n")
1167 self.ui.warn("No saved patch data found\n")
1168 return 1
1168 return 1
1169 self.ui.warn("restoring status: %s\n" % lines[0])
1169 self.ui.warn("restoring status: %s\n" % lines[0])
1170 self.full_series = series
1170 self.full_series = series
1171 self.applied = applied
1171 self.applied = applied
1172 self.parse_series()
1172 self.parse_series()
1173 self.series_dirty = 1
1173 self.series_dirty = 1
1174 self.applied_dirty = 1
1174 self.applied_dirty = 1
1175 heads = repo.changelog.heads()
1175 heads = repo.changelog.heads()
1176 if delete:
1176 if delete:
1177 if rev not in heads:
1177 if rev not in heads:
1178 self.ui.warn("save entry has children, leaving it alone\n")
1178 self.ui.warn("save entry has children, leaving it alone\n")
1179 else:
1179 else:
1180 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1180 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1181 pp = repo.dirstate.parents()
1181 pp = repo.dirstate.parents()
1182 if rev in pp:
1182 if rev in pp:
1183 update = True
1183 update = True
1184 else:
1184 else:
1185 update = False
1185 update = False
1186 self.strip(repo, rev, update=update, backup='strip')
1186 self.strip(repo, rev, update=update, backup='strip')
1187 if qpp:
1187 if qpp:
1188 self.ui.warn("saved queue repository parents: %s %s\n" %
1188 self.ui.warn("saved queue repository parents: %s %s\n" %
1189 (hg.short(qpp[0]), hg.short(qpp[1])))
1189 (hg.short(qpp[0]), hg.short(qpp[1])))
1190 if qupdate:
1190 if qupdate:
1191 print "queue directory updating"
1191 print "queue directory updating"
1192 r = self.qrepo()
1192 r = self.qrepo()
1193 if not r:
1193 if not r:
1194 self.ui.warn("Unable to load queue repository\n")
1194 self.ui.warn("Unable to load queue repository\n")
1195 return 1
1195 return 1
1196 hg.clean(r, qpp[0])
1196 hg.clean(r, qpp[0])
1197
1197
1198 def save(self, repo, msg=None):
1198 def save(self, repo, msg=None):
1199 if len(self.applied) == 0:
1199 if len(self.applied) == 0:
1200 self.ui.warn("save: no patches applied, exiting\n")
1200 self.ui.warn("save: no patches applied, exiting\n")
1201 return 1
1201 return 1
1202 if self.issaveline(self.applied[-1]):
1202 if self.issaveline(self.applied[-1]):
1203 self.ui.warn("status is already saved\n")
1203 self.ui.warn("status is already saved\n")
1204 return 1
1204 return 1
1205
1205
1206 ar = [ ':' + x for x in self.full_series ]
1206 ar = [ ':' + x for x in self.full_series ]
1207 if not msg:
1207 if not msg:
1208 msg = "hg patches saved state"
1208 msg = "hg patches saved state"
1209 else:
1209 else:
1210 msg = "hg patches: " + msg.rstrip('\r\n')
1210 msg = "hg patches: " + msg.rstrip('\r\n')
1211 r = self.qrepo()
1211 r = self.qrepo()
1212 if r:
1212 if r:
1213 pp = r.dirstate.parents()
1213 pp = r.dirstate.parents()
1214 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1214 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1215 msg += "\n\nPatch Data:\n"
1215 msg += "\n\nPatch Data:\n"
1216 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1216 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1217 "\n".join(ar) + '\n' or "")
1217 "\n".join(ar) + '\n' or "")
1218 n = repo.commit(None, text, user=None, force=1)
1218 n = repo.commit(None, text, user=None, force=1)
1219 if not n:
1219 if not n:
1220 self.ui.warn("repo commit failed\n")
1220 self.ui.warn("repo commit failed\n")
1221 return 1
1221 return 1
1222 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1222 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1223 self.applied_dirty = 1
1223 self.applied_dirty = 1
1224
1224
1225 def full_series_end(self):
1225 def full_series_end(self):
1226 if len(self.applied) > 0:
1226 if len(self.applied) > 0:
1227 p = self.applied[-1].name
1227 p = self.applied[-1].name
1228 end = self.find_series(p)
1228 end = self.find_series(p)
1229 if end == None:
1229 if end == None:
1230 return len(self.full_series)
1230 return len(self.full_series)
1231 return end + 1
1231 return end + 1
1232 return 0
1232 return 0
1233
1233
1234 def series_end(self, all_patches=False):
1234 def series_end(self, all_patches=False):
1235 end = 0
1235 end = 0
1236 def next(start):
1236 def next(start):
1237 if all_patches:
1237 if all_patches:
1238 return start
1238 return start
1239 i = start
1239 i = start
1240 while i < len(self.series):
1240 while i < len(self.series):
1241 p, reason = self.pushable(i)
1241 p, reason = self.pushable(i)
1242 if p:
1242 if p:
1243 break
1243 break
1244 self.explain_pushable(i)
1244 self.explain_pushable(i)
1245 i += 1
1245 i += 1
1246 return i
1246 return i
1247 if len(self.applied) > 0:
1247 if len(self.applied) > 0:
1248 p = self.applied[-1].name
1248 p = self.applied[-1].name
1249 try:
1249 try:
1250 end = self.series.index(p)
1250 end = self.series.index(p)
1251 except ValueError:
1251 except ValueError:
1252 return 0
1252 return 0
1253 return next(end + 1)
1253 return next(end + 1)
1254 return next(end)
1254 return next(end)
1255
1255
1256 def appliedname(self, index):
1256 def appliedname(self, index):
1257 pname = self.applied[index].name
1257 pname = self.applied[index].name
1258 if not self.ui.verbose:
1258 if not self.ui.verbose:
1259 p = pname
1259 p = pname
1260 else:
1260 else:
1261 p = str(self.series.index(pname)) + " " + pname
1261 p = str(self.series.index(pname)) + " " + pname
1262 return p
1262 return p
1263
1263
1264 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1264 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1265 force=None):
1265 force=None):
1266 def checkseries(patchname):
1266 def checkseries(patchname):
1267 if patchname in self.series:
1267 if patchname in self.series:
1268 raise util.Abort(_('patch %s is already in the series file')
1268 raise util.Abort(_('patch %s is already in the series file')
1269 % patchname)
1269 % patchname)
1270 def checkfile(patchname):
1270 def checkfile(patchname):
1271 if not force and os.path.exists(self.join(patchname)):
1271 if not force and os.path.exists(self.join(patchname)):
1272 raise util.Abort(_('patch "%s" already exists')
1272 raise util.Abort(_('patch "%s" already exists')
1273 % patchname)
1273 % patchname)
1274
1274
1275 if rev:
1275 if rev:
1276 if files:
1276 if files:
1277 raise util.Abort(_('option "-r" not valid when importing '
1277 raise util.Abort(_('option "-r" not valid when importing '
1278 'files'))
1278 'files'))
1279 rev = [int(r) for r in cmdutil.revrange(self.ui, repo, rev)]
1279 rev = cmdutil.revrange(self.ui, repo, rev)
1280 rev.sort(lambda x, y: cmp(y, x))
1280 rev.sort(lambda x, y: cmp(y, x))
1281 if (len(files) > 1 or len(rev) > 1) and patchname:
1281 if (len(files) > 1 or len(rev) > 1) and patchname:
1282 raise util.Abort(_('option "-n" not valid when importing multiple '
1282 raise util.Abort(_('option "-n" not valid when importing multiple '
1283 'patches'))
1283 'patches'))
1284 i = 0
1284 i = 0
1285 added = []
1285 added = []
1286 if rev:
1286 if rev:
1287 # If mq patches are applied, we can only import revisions
1287 # If mq patches are applied, we can only import revisions
1288 # that form a linear path to qbase.
1288 # that form a linear path to qbase.
1289 # Otherwise, they should form a linear path to a head.
1289 # Otherwise, they should form a linear path to a head.
1290 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1290 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1291 if len(heads) > 1:
1291 if len(heads) > 1:
1292 raise util.Abort(_('revision %d is the root of more than one '
1292 raise util.Abort(_('revision %d is the root of more than one '
1293 'branch') % rev[-1])
1293 'branch') % rev[-1])
1294 if self.applied:
1294 if self.applied:
1295 base = revlog.hex(repo.changelog.node(rev[0]))
1295 base = revlog.hex(repo.changelog.node(rev[0]))
1296 if base in [n.rev for n in self.applied]:
1296 if base in [n.rev for n in self.applied]:
1297 raise util.Abort(_('revision %d is already managed')
1297 raise util.Abort(_('revision %d is already managed')
1298 % rev[0])
1298 % rev[0])
1299 if heads != [revlog.bin(self.applied[-1].rev)]:
1299 if heads != [revlog.bin(self.applied[-1].rev)]:
1300 raise util.Abort(_('revision %d is not the parent of '
1300 raise util.Abort(_('revision %d is not the parent of '
1301 'the queue') % rev[0])
1301 'the queue') % rev[0])
1302 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1302 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1303 lastparent = repo.changelog.parentrevs(base)[0]
1303 lastparent = repo.changelog.parentrevs(base)[0]
1304 else:
1304 else:
1305 if heads != [repo.changelog.node(rev[0])]:
1305 if heads != [repo.changelog.node(rev[0])]:
1306 raise util.Abort(_('revision %d has unmanaged children')
1306 raise util.Abort(_('revision %d has unmanaged children')
1307 % rev[0])
1307 % rev[0])
1308 lastparent = None
1308 lastparent = None
1309
1309
1310 for r in rev:
1310 for r in rev:
1311 p1, p2 = repo.changelog.parentrevs(r)
1311 p1, p2 = repo.changelog.parentrevs(r)
1312 n = repo.changelog.node(r)
1312 n = repo.changelog.node(r)
1313 if p2 != -1:
1313 if p2 != -1:
1314 raise util.Abort(_('cannot import merge revision %d') % r)
1314 raise util.Abort(_('cannot import merge revision %d') % r)
1315 if lastparent and lastparent != r:
1315 if lastparent and lastparent != r:
1316 raise util.Abort(_('revision %d is not the parent of %d')
1316 raise util.Abort(_('revision %d is not the parent of %d')
1317 % (r, lastparent))
1317 % (r, lastparent))
1318 lastparent = p1
1318 lastparent = p1
1319
1319
1320 if not patchname:
1320 if not patchname:
1321 patchname = '%d.diff' % r
1321 patchname = '%d.diff' % r
1322 checkseries(patchname)
1322 checkseries(patchname)
1323 checkfile(patchname)
1323 checkfile(patchname)
1324 self.full_series.insert(0, patchname)
1324 self.full_series.insert(0, patchname)
1325
1325
1326 patchf = self.opener(patchname, "w")
1326 patchf = self.opener(patchname, "w")
1327 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1327 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1328 patchf.close()
1328 patchf.close()
1329
1329
1330 se = statusentry(revlog.hex(n), patchname)
1330 se = statusentry(revlog.hex(n), patchname)
1331 self.applied.insert(0, se)
1331 self.applied.insert(0, se)
1332
1332
1333 added.append(patchname)
1333 added.append(patchname)
1334 patchname = None
1334 patchname = None
1335 self.parse_series()
1335 self.parse_series()
1336 self.applied_dirty = 1
1336 self.applied_dirty = 1
1337
1337
1338 for filename in files:
1338 for filename in files:
1339 if existing:
1339 if existing:
1340 if not patchname:
1340 if not patchname:
1341 patchname = filename
1341 patchname = filename
1342 if not os.path.isfile(self.join(patchname)):
1342 if not os.path.isfile(self.join(patchname)):
1343 raise util.Abort(_("patch %s does not exist") % patchname)
1343 raise util.Abort(_("patch %s does not exist") % patchname)
1344 else:
1344 else:
1345 try:
1345 try:
1346 text = file(filename).read()
1346 text = file(filename).read()
1347 except IOError:
1347 except IOError:
1348 raise util.Abort(_("unable to read %s") % patchname)
1348 raise util.Abort(_("unable to read %s") % patchname)
1349 if not patchname:
1349 if not patchname:
1350 patchname = os.path.basename(filename)
1350 patchname = os.path.basename(filename)
1351 checkfile(patchname)
1351 checkfile(patchname)
1352 patchf = self.opener(patchname, "w")
1352 patchf = self.opener(patchname, "w")
1353 patchf.write(text)
1353 patchf.write(text)
1354 checkseries(patchname)
1354 checkseries(patchname)
1355 index = self.full_series_end() + i
1355 index = self.full_series_end() + i
1356 self.full_series[index:index] = [patchname]
1356 self.full_series[index:index] = [patchname]
1357 self.parse_series()
1357 self.parse_series()
1358 self.ui.warn("adding %s to series file\n" % patchname)
1358 self.ui.warn("adding %s to series file\n" % patchname)
1359 i += 1
1359 i += 1
1360 added.append(patchname)
1360 added.append(patchname)
1361 patchname = None
1361 patchname = None
1362 self.series_dirty = 1
1362 self.series_dirty = 1
1363 qrepo = self.qrepo()
1363 qrepo = self.qrepo()
1364 if qrepo:
1364 if qrepo:
1365 qrepo.add(added)
1365 qrepo.add(added)
1366
1366
1367 def delete(ui, repo, *patches, **opts):
1367 def delete(ui, repo, *patches, **opts):
1368 """remove patches from queue
1368 """remove patches from queue
1369
1369
1370 With --rev, mq will stop managing the named revisions. The
1370 With --rev, mq will stop managing the named revisions. The
1371 patches must be applied and at the base of the stack. This option
1371 patches must be applied and at the base of the stack. This option
1372 is useful when the patches have been applied upstream.
1372 is useful when the patches have been applied upstream.
1373
1373
1374 Otherwise, the patches must not be applied.
1374 Otherwise, the patches must not be applied.
1375
1375
1376 With --keep, the patch files are preserved in the patch directory."""
1376 With --keep, the patch files are preserved in the patch directory."""
1377 q = repo.mq
1377 q = repo.mq
1378 q.delete(repo, patches, opts)
1378 q.delete(repo, patches, opts)
1379 q.save_dirty()
1379 q.save_dirty()
1380 return 0
1380 return 0
1381
1381
1382 def applied(ui, repo, patch=None, **opts):
1382 def applied(ui, repo, patch=None, **opts):
1383 """print the patches already applied"""
1383 """print the patches already applied"""
1384 q = repo.mq
1384 q = repo.mq
1385 if patch:
1385 if patch:
1386 if patch not in q.series:
1386 if patch not in q.series:
1387 raise util.Abort(_("patch %s is not in series file") % patch)
1387 raise util.Abort(_("patch %s is not in series file") % patch)
1388 end = q.series.index(patch) + 1
1388 end = q.series.index(patch) + 1
1389 else:
1389 else:
1390 end = len(q.applied)
1390 end = len(q.applied)
1391 if not end:
1391 if not end:
1392 return
1392 return
1393
1393
1394 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1394 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1395
1395
1396 def unapplied(ui, repo, patch=None, **opts):
1396 def unapplied(ui, repo, patch=None, **opts):
1397 """print the patches not yet applied"""
1397 """print the patches not yet applied"""
1398 q = repo.mq
1398 q = repo.mq
1399 if patch:
1399 if patch:
1400 if patch not in q.series:
1400 if patch not in q.series:
1401 raise util.Abort(_("patch %s is not in series file") % patch)
1401 raise util.Abort(_("patch %s is not in series file") % patch)
1402 start = q.series.index(patch) + 1
1402 start = q.series.index(patch) + 1
1403 else:
1403 else:
1404 start = q.series_end()
1404 start = q.series_end()
1405 q.qseries(repo, start=start, summary=opts.get('summary'))
1405 q.qseries(repo, start=start, summary=opts.get('summary'))
1406
1406
1407 def qimport(ui, repo, *filename, **opts):
1407 def qimport(ui, repo, *filename, **opts):
1408 """import a patch
1408 """import a patch
1409
1409
1410 The patch will have the same name as its source file unless you
1410 The patch will have the same name as its source file unless you
1411 give it a new one with --name.
1411 give it a new one with --name.
1412
1412
1413 You can register an existing patch inside the patch directory
1413 You can register an existing patch inside the patch directory
1414 with the --existing flag.
1414 with the --existing flag.
1415
1415
1416 With --force, an existing patch of the same name will be overwritten.
1416 With --force, an existing patch of the same name will be overwritten.
1417
1417
1418 An existing changeset may be placed under mq control with --rev
1418 An existing changeset may be placed under mq control with --rev
1419 (e.g. qimport --rev tip -n patch will place tip under mq control).
1419 (e.g. qimport --rev tip -n patch will place tip under mq control).
1420 """
1420 """
1421 q = repo.mq
1421 q = repo.mq
1422 q.qimport(repo, filename, patchname=opts['name'],
1422 q.qimport(repo, filename, patchname=opts['name'],
1423 existing=opts['existing'], force=opts['force'], rev=opts['rev'])
1423 existing=opts['existing'], force=opts['force'], rev=opts['rev'])
1424 q.save_dirty()
1424 q.save_dirty()
1425 return 0
1425 return 0
1426
1426
1427 def init(ui, repo, **opts):
1427 def init(ui, repo, **opts):
1428 """init a new queue repository
1428 """init a new queue repository
1429
1429
1430 The queue repository is unversioned by default. If -c is
1430 The queue repository is unversioned by default. If -c is
1431 specified, qinit will create a separate nested repository
1431 specified, qinit will create a separate nested repository
1432 for patches. Use qcommit to commit changes to this queue
1432 for patches. Use qcommit to commit changes to this queue
1433 repository."""
1433 repository."""
1434 q = repo.mq
1434 q = repo.mq
1435 r = q.init(repo, create=opts['create_repo'])
1435 r = q.init(repo, create=opts['create_repo'])
1436 q.save_dirty()
1436 q.save_dirty()
1437 if r:
1437 if r:
1438 fp = r.wopener('.hgignore', 'w')
1438 fp = r.wopener('.hgignore', 'w')
1439 print >> fp, 'syntax: glob'
1439 print >> fp, 'syntax: glob'
1440 print >> fp, 'status'
1440 print >> fp, 'status'
1441 fp.close()
1441 fp.close()
1442 r.wopener('series', 'w').close()
1442 r.wopener('series', 'w').close()
1443 r.add(['.hgignore', 'series'])
1443 r.add(['.hgignore', 'series'])
1444 return 0
1444 return 0
1445
1445
1446 def clone(ui, source, dest=None, **opts):
1446 def clone(ui, source, dest=None, **opts):
1447 '''clone main and patch repository at same time
1447 '''clone main and patch repository at same time
1448
1448
1449 If source is local, destination will have no patches applied. If
1449 If source is local, destination will have no patches applied. If
1450 source is remote, this command can not check if patches are
1450 source is remote, this command can not check if patches are
1451 applied in source, so cannot guarantee that patches are not
1451 applied in source, so cannot guarantee that patches are not
1452 applied in destination. If you clone remote repository, be sure
1452 applied in destination. If you clone remote repository, be sure
1453 before that it has no patches applied.
1453 before that it has no patches applied.
1454
1454
1455 Source patch repository is looked for in <src>/.hg/patches by
1455 Source patch repository is looked for in <src>/.hg/patches by
1456 default. Use -p <url> to change.
1456 default. Use -p <url> to change.
1457 '''
1457 '''
1458 commands.setremoteconfig(ui, opts)
1458 commands.setremoteconfig(ui, opts)
1459 if dest is None:
1459 if dest is None:
1460 dest = hg.defaultdest(source)
1460 dest = hg.defaultdest(source)
1461 sr = hg.repository(ui, ui.expandpath(source))
1461 sr = hg.repository(ui, ui.expandpath(source))
1462 qbase, destrev = None, None
1462 qbase, destrev = None, None
1463 if sr.local():
1463 if sr.local():
1464 reposetup(ui, sr)
1464 reposetup(ui, sr)
1465 if sr.mq.applied:
1465 if sr.mq.applied:
1466 qbase = revlog.bin(sr.mq.applied[0].rev)
1466 qbase = revlog.bin(sr.mq.applied[0].rev)
1467 if not hg.islocal(dest):
1467 if not hg.islocal(dest):
1468 destrev = sr.parents(qbase)[0]
1468 destrev = sr.parents(qbase)[0]
1469 ui.note(_('cloning main repo\n'))
1469 ui.note(_('cloning main repo\n'))
1470 sr, dr = hg.clone(ui, sr, dest,
1470 sr, dr = hg.clone(ui, sr, dest,
1471 pull=opts['pull'],
1471 pull=opts['pull'],
1472 rev=destrev,
1472 rev=destrev,
1473 update=False,
1473 update=False,
1474 stream=opts['uncompressed'])
1474 stream=opts['uncompressed'])
1475 ui.note(_('cloning patch repo\n'))
1475 ui.note(_('cloning patch repo\n'))
1476 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1476 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1477 dr.url() + '/.hg/patches',
1477 dr.url() + '/.hg/patches',
1478 pull=opts['pull'],
1478 pull=opts['pull'],
1479 update=not opts['noupdate'],
1479 update=not opts['noupdate'],
1480 stream=opts['uncompressed'])
1480 stream=opts['uncompressed'])
1481 if dr.local():
1481 if dr.local():
1482 if qbase:
1482 if qbase:
1483 ui.note(_('stripping applied patches from destination repo\n'))
1483 ui.note(_('stripping applied patches from destination repo\n'))
1484 reposetup(ui, dr)
1484 reposetup(ui, dr)
1485 dr.mq.strip(dr, qbase, update=False, backup=None)
1485 dr.mq.strip(dr, qbase, update=False, backup=None)
1486 if not opts['noupdate']:
1486 if not opts['noupdate']:
1487 ui.note(_('updating destination repo\n'))
1487 ui.note(_('updating destination repo\n'))
1488 hg.update(dr, dr.changelog.tip())
1488 hg.update(dr, dr.changelog.tip())
1489
1489
1490 def commit(ui, repo, *pats, **opts):
1490 def commit(ui, repo, *pats, **opts):
1491 """commit changes in the queue repository"""
1491 """commit changes in the queue repository"""
1492 q = repo.mq
1492 q = repo.mq
1493 r = q.qrepo()
1493 r = q.qrepo()
1494 if not r: raise util.Abort('no queue repository')
1494 if not r: raise util.Abort('no queue repository')
1495 commands.commit(r.ui, r, *pats, **opts)
1495 commands.commit(r.ui, r, *pats, **opts)
1496
1496
1497 def series(ui, repo, **opts):
1497 def series(ui, repo, **opts):
1498 """print the entire series file"""
1498 """print the entire series file"""
1499 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1499 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1500 return 0
1500 return 0
1501
1501
1502 def top(ui, repo, **opts):
1502 def top(ui, repo, **opts):
1503 """print the name of the current patch"""
1503 """print the name of the current patch"""
1504 q = repo.mq
1504 q = repo.mq
1505 t = len(q.applied)
1505 t = len(q.applied)
1506 if t:
1506 if t:
1507 return q.qseries(repo, start=t-1, length=1, status='A',
1507 return q.qseries(repo, start=t-1, length=1, status='A',
1508 summary=opts.get('summary'))
1508 summary=opts.get('summary'))
1509 else:
1509 else:
1510 ui.write("No patches applied\n")
1510 ui.write("No patches applied\n")
1511 return 1
1511 return 1
1512
1512
1513 def next(ui, repo, **opts):
1513 def next(ui, repo, **opts):
1514 """print the name of the next patch"""
1514 """print the name of the next patch"""
1515 q = repo.mq
1515 q = repo.mq
1516 end = q.series_end()
1516 end = q.series_end()
1517 if end == len(q.series):
1517 if end == len(q.series):
1518 ui.write("All patches applied\n")
1518 ui.write("All patches applied\n")
1519 return 1
1519 return 1
1520 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1520 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1521
1521
1522 def prev(ui, repo, **opts):
1522 def prev(ui, repo, **opts):
1523 """print the name of the previous patch"""
1523 """print the name of the previous patch"""
1524 q = repo.mq
1524 q = repo.mq
1525 l = len(q.applied)
1525 l = len(q.applied)
1526 if l == 1:
1526 if l == 1:
1527 ui.write("Only one patch applied\n")
1527 ui.write("Only one patch applied\n")
1528 return 1
1528 return 1
1529 if not l:
1529 if not l:
1530 ui.write("No patches applied\n")
1530 ui.write("No patches applied\n")
1531 return 1
1531 return 1
1532 return q.qseries(repo, start=l-2, length=1, status='A',
1532 return q.qseries(repo, start=l-2, length=1, status='A',
1533 summary=opts.get('summary'))
1533 summary=opts.get('summary'))
1534
1534
1535 def new(ui, repo, patch, **opts):
1535 def new(ui, repo, patch, **opts):
1536 """create a new patch
1536 """create a new patch
1537
1537
1538 qnew creates a new patch on top of the currently-applied patch
1538 qnew creates a new patch on top of the currently-applied patch
1539 (if any). It will refuse to run if there are any outstanding
1539 (if any). It will refuse to run if there are any outstanding
1540 changes unless -f is specified, in which case the patch will
1540 changes unless -f is specified, in which case the patch will
1541 be initialised with them.
1541 be initialised with them.
1542
1542
1543 -e, -m or -l set the patch header as well as the commit message.
1543 -e, -m or -l set the patch header as well as the commit message.
1544 If none is specified, the patch header is empty and the
1544 If none is specified, the patch header is empty and the
1545 commit message is 'New patch: PATCH'"""
1545 commit message is 'New patch: PATCH'"""
1546 q = repo.mq
1546 q = repo.mq
1547 message = commands.logmessage(opts)
1547 message = commands.logmessage(opts)
1548 if opts['edit']:
1548 if opts['edit']:
1549 message = ui.edit(message, ui.username())
1549 message = ui.edit(message, ui.username())
1550 q.new(repo, patch, msg=message, force=opts['force'])
1550 q.new(repo, patch, msg=message, force=opts['force'])
1551 q.save_dirty()
1551 q.save_dirty()
1552 return 0
1552 return 0
1553
1553
1554 def refresh(ui, repo, *pats, **opts):
1554 def refresh(ui, repo, *pats, **opts):
1555 """update the current patch
1555 """update the current patch
1556
1556
1557 If any file patterns are provided, the refreshed patch will contain only
1557 If any file patterns are provided, the refreshed patch will contain only
1558 the modifications that match those patterns; the remaining modifications
1558 the modifications that match those patterns; the remaining modifications
1559 will remain in the working directory.
1559 will remain in the working directory.
1560 """
1560 """
1561 q = repo.mq
1561 q = repo.mq
1562 message = commands.logmessage(opts)
1562 message = commands.logmessage(opts)
1563 if opts['edit']:
1563 if opts['edit']:
1564 if message:
1564 if message:
1565 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1565 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1566 patch = q.applied[-1].name
1566 patch = q.applied[-1].name
1567 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1567 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1568 message = ui.edit('\n'.join(message), user or ui.username())
1568 message = ui.edit('\n'.join(message), user or ui.username())
1569 ret = q.refresh(repo, pats, msg=message, **opts)
1569 ret = q.refresh(repo, pats, msg=message, **opts)
1570 q.save_dirty()
1570 q.save_dirty()
1571 return ret
1571 return ret
1572
1572
1573 def diff(ui, repo, *pats, **opts):
1573 def diff(ui, repo, *pats, **opts):
1574 """diff of the current patch"""
1574 """diff of the current patch"""
1575 repo.mq.diff(repo, pats, opts)
1575 repo.mq.diff(repo, pats, opts)
1576 return 0
1576 return 0
1577
1577
1578 def fold(ui, repo, *files, **opts):
1578 def fold(ui, repo, *files, **opts):
1579 """fold the named patches into the current patch
1579 """fold the named patches into the current patch
1580
1580
1581 Patches must not yet be applied. Each patch will be successively
1581 Patches must not yet be applied. Each patch will be successively
1582 applied to the current patch in the order given. If all the
1582 applied to the current patch in the order given. If all the
1583 patches apply successfully, the current patch will be refreshed
1583 patches apply successfully, the current patch will be refreshed
1584 with the new cumulative patch, and the folded patches will
1584 with the new cumulative patch, and the folded patches will
1585 be deleted. With -k/--keep, the folded patch files will not
1585 be deleted. With -k/--keep, the folded patch files will not
1586 be removed afterwards.
1586 be removed afterwards.
1587
1587
1588 The header for each folded patch will be concatenated with
1588 The header for each folded patch will be concatenated with
1589 the current patch header, separated by a line of '* * *'."""
1589 the current patch header, separated by a line of '* * *'."""
1590
1590
1591 q = repo.mq
1591 q = repo.mq
1592
1592
1593 if not files:
1593 if not files:
1594 raise util.Abort(_('qfold requires at least one patch name'))
1594 raise util.Abort(_('qfold requires at least one patch name'))
1595 if not q.check_toppatch(repo):
1595 if not q.check_toppatch(repo):
1596 raise util.Abort(_('No patches applied'))
1596 raise util.Abort(_('No patches applied'))
1597
1597
1598 message = commands.logmessage(opts)
1598 message = commands.logmessage(opts)
1599 if opts['edit']:
1599 if opts['edit']:
1600 if message:
1600 if message:
1601 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1601 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1602
1602
1603 parent = q.lookup('qtip')
1603 parent = q.lookup('qtip')
1604 patches = []
1604 patches = []
1605 messages = []
1605 messages = []
1606 for f in files:
1606 for f in files:
1607 p = q.lookup(f)
1607 p = q.lookup(f)
1608 if p in patches or p == parent:
1608 if p in patches or p == parent:
1609 ui.warn(_('Skipping already folded patch %s') % p)
1609 ui.warn(_('Skipping already folded patch %s') % p)
1610 if q.isapplied(p):
1610 if q.isapplied(p):
1611 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1611 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1612 patches.append(p)
1612 patches.append(p)
1613
1613
1614 for p in patches:
1614 for p in patches:
1615 if not message:
1615 if not message:
1616 messages.append(q.readheaders(p)[0])
1616 messages.append(q.readheaders(p)[0])
1617 pf = q.join(p)
1617 pf = q.join(p)
1618 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1618 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1619 if not patchsuccess:
1619 if not patchsuccess:
1620 raise util.Abort(_('Error folding patch %s') % p)
1620 raise util.Abort(_('Error folding patch %s') % p)
1621 patch.updatedir(ui, repo, files)
1621 patch.updatedir(ui, repo, files)
1622
1622
1623 if not message:
1623 if not message:
1624 message, comments, user = q.readheaders(parent)[0:3]
1624 message, comments, user = q.readheaders(parent)[0:3]
1625 for msg in messages:
1625 for msg in messages:
1626 message.append('* * *')
1626 message.append('* * *')
1627 message.extend(msg)
1627 message.extend(msg)
1628 message = '\n'.join(message)
1628 message = '\n'.join(message)
1629
1629
1630 if opts['edit']:
1630 if opts['edit']:
1631 message = ui.edit(message, user or ui.username())
1631 message = ui.edit(message, user or ui.username())
1632
1632
1633 q.refresh(repo, msg=message)
1633 q.refresh(repo, msg=message)
1634 q.delete(repo, patches, opts)
1634 q.delete(repo, patches, opts)
1635 q.save_dirty()
1635 q.save_dirty()
1636
1636
1637 def guard(ui, repo, *args, **opts):
1637 def guard(ui, repo, *args, **opts):
1638 '''set or print guards for a patch
1638 '''set or print guards for a patch
1639
1639
1640 Guards control whether a patch can be pushed. A patch with no
1640 Guards control whether a patch can be pushed. A patch with no
1641 guards is always pushed. A patch with a positive guard ("+foo") is
1641 guards is always pushed. A patch with a positive guard ("+foo") is
1642 pushed only if the qselect command has activated it. A patch with
1642 pushed only if the qselect command has activated it. A patch with
1643 a negative guard ("-foo") is never pushed if the qselect command
1643 a negative guard ("-foo") is never pushed if the qselect command
1644 has activated it.
1644 has activated it.
1645
1645
1646 With no arguments, print the currently active guards.
1646 With no arguments, print the currently active guards.
1647 With arguments, set guards for the named patch.
1647 With arguments, set guards for the named patch.
1648
1648
1649 To set a negative guard "-foo" on topmost patch ("--" is needed so
1649 To set a negative guard "-foo" on topmost patch ("--" is needed so
1650 hg will not interpret "-foo" as an option):
1650 hg will not interpret "-foo" as an option):
1651 hg qguard -- -foo
1651 hg qguard -- -foo
1652
1652
1653 To set guards on another patch:
1653 To set guards on another patch:
1654 hg qguard other.patch +2.6.17 -stable
1654 hg qguard other.patch +2.6.17 -stable
1655 '''
1655 '''
1656 def status(idx):
1656 def status(idx):
1657 guards = q.series_guards[idx] or ['unguarded']
1657 guards = q.series_guards[idx] or ['unguarded']
1658 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1658 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1659 q = repo.mq
1659 q = repo.mq
1660 patch = None
1660 patch = None
1661 args = list(args)
1661 args = list(args)
1662 if opts['list']:
1662 if opts['list']:
1663 if args or opts['none']:
1663 if args or opts['none']:
1664 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1664 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1665 for i in xrange(len(q.series)):
1665 for i in xrange(len(q.series)):
1666 status(i)
1666 status(i)
1667 return
1667 return
1668 if not args or args[0][0:1] in '-+':
1668 if not args or args[0][0:1] in '-+':
1669 if not q.applied:
1669 if not q.applied:
1670 raise util.Abort(_('no patches applied'))
1670 raise util.Abort(_('no patches applied'))
1671 patch = q.applied[-1].name
1671 patch = q.applied[-1].name
1672 if patch is None and args[0][0:1] not in '-+':
1672 if patch is None and args[0][0:1] not in '-+':
1673 patch = args.pop(0)
1673 patch = args.pop(0)
1674 if patch is None:
1674 if patch is None:
1675 raise util.Abort(_('no patch to work with'))
1675 raise util.Abort(_('no patch to work with'))
1676 if args or opts['none']:
1676 if args or opts['none']:
1677 q.set_guards(q.find_series(patch), args)
1677 q.set_guards(q.find_series(patch), args)
1678 q.save_dirty()
1678 q.save_dirty()
1679 else:
1679 else:
1680 status(q.series.index(q.lookup(patch)))
1680 status(q.series.index(q.lookup(patch)))
1681
1681
1682 def header(ui, repo, patch=None):
1682 def header(ui, repo, patch=None):
1683 """Print the header of the topmost or specified patch"""
1683 """Print the header of the topmost or specified patch"""
1684 q = repo.mq
1684 q = repo.mq
1685
1685
1686 if patch:
1686 if patch:
1687 patch = q.lookup(patch)
1687 patch = q.lookup(patch)
1688 else:
1688 else:
1689 if not q.applied:
1689 if not q.applied:
1690 ui.write('No patches applied\n')
1690 ui.write('No patches applied\n')
1691 return 1
1691 return 1
1692 patch = q.lookup('qtip')
1692 patch = q.lookup('qtip')
1693 message = repo.mq.readheaders(patch)[0]
1693 message = repo.mq.readheaders(patch)[0]
1694
1694
1695 ui.write('\n'.join(message) + '\n')
1695 ui.write('\n'.join(message) + '\n')
1696
1696
1697 def lastsavename(path):
1697 def lastsavename(path):
1698 (directory, base) = os.path.split(path)
1698 (directory, base) = os.path.split(path)
1699 names = os.listdir(directory)
1699 names = os.listdir(directory)
1700 namere = re.compile("%s.([0-9]+)" % base)
1700 namere = re.compile("%s.([0-9]+)" % base)
1701 maxindex = None
1701 maxindex = None
1702 maxname = None
1702 maxname = None
1703 for f in names:
1703 for f in names:
1704 m = namere.match(f)
1704 m = namere.match(f)
1705 if m:
1705 if m:
1706 index = int(m.group(1))
1706 index = int(m.group(1))
1707 if maxindex == None or index > maxindex:
1707 if maxindex == None or index > maxindex:
1708 maxindex = index
1708 maxindex = index
1709 maxname = f
1709 maxname = f
1710 if maxname:
1710 if maxname:
1711 return (os.path.join(directory, maxname), maxindex)
1711 return (os.path.join(directory, maxname), maxindex)
1712 return (None, None)
1712 return (None, None)
1713
1713
1714 def savename(path):
1714 def savename(path):
1715 (last, index) = lastsavename(path)
1715 (last, index) = lastsavename(path)
1716 if last is None:
1716 if last is None:
1717 index = 0
1717 index = 0
1718 newpath = path + ".%d" % (index + 1)
1718 newpath = path + ".%d" % (index + 1)
1719 return newpath
1719 return newpath
1720
1720
1721 def push(ui, repo, patch=None, **opts):
1721 def push(ui, repo, patch=None, **opts):
1722 """push the next patch onto the stack"""
1722 """push the next patch onto the stack"""
1723 q = repo.mq
1723 q = repo.mq
1724 mergeq = None
1724 mergeq = None
1725
1725
1726 if opts['all']:
1726 if opts['all']:
1727 patch = q.series[-1]
1727 patch = q.series[-1]
1728 if opts['merge']:
1728 if opts['merge']:
1729 if opts['name']:
1729 if opts['name']:
1730 newpath = opts['name']
1730 newpath = opts['name']
1731 else:
1731 else:
1732 newpath, i = lastsavename(q.path)
1732 newpath, i = lastsavename(q.path)
1733 if not newpath:
1733 if not newpath:
1734 ui.warn("no saved queues found, please use -n\n")
1734 ui.warn("no saved queues found, please use -n\n")
1735 return 1
1735 return 1
1736 mergeq = queue(ui, repo.join(""), newpath)
1736 mergeq = queue(ui, repo.join(""), newpath)
1737 ui.warn("merging with queue at: %s\n" % mergeq.path)
1737 ui.warn("merging with queue at: %s\n" % mergeq.path)
1738 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1738 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1739 mergeq=mergeq)
1739 mergeq=mergeq)
1740 q.save_dirty()
1740 q.save_dirty()
1741 return ret
1741 return ret
1742
1742
1743 def pop(ui, repo, patch=None, **opts):
1743 def pop(ui, repo, patch=None, **opts):
1744 """pop the current patch off the stack"""
1744 """pop the current patch off the stack"""
1745 localupdate = True
1745 localupdate = True
1746 if opts['name']:
1746 if opts['name']:
1747 q = queue(ui, repo.join(""), repo.join(opts['name']))
1747 q = queue(ui, repo.join(""), repo.join(opts['name']))
1748 ui.warn('using patch queue: %s\n' % q.path)
1748 ui.warn('using patch queue: %s\n' % q.path)
1749 localupdate = False
1749 localupdate = False
1750 else:
1750 else:
1751 q = repo.mq
1751 q = repo.mq
1752 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1752 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1753 q.save_dirty()
1753 q.save_dirty()
1754 return 0
1754 return 0
1755
1755
1756 def rename(ui, repo, patch, name=None, **opts):
1756 def rename(ui, repo, patch, name=None, **opts):
1757 """rename a patch
1757 """rename a patch
1758
1758
1759 With one argument, renames the current patch to PATCH1.
1759 With one argument, renames the current patch to PATCH1.
1760 With two arguments, renames PATCH1 to PATCH2."""
1760 With two arguments, renames PATCH1 to PATCH2."""
1761
1761
1762 q = repo.mq
1762 q = repo.mq
1763
1763
1764 if not name:
1764 if not name:
1765 name = patch
1765 name = patch
1766 patch = None
1766 patch = None
1767
1767
1768 if patch:
1768 if patch:
1769 patch = q.lookup(patch)
1769 patch = q.lookup(patch)
1770 else:
1770 else:
1771 if not q.applied:
1771 if not q.applied:
1772 ui.write(_('No patches applied\n'))
1772 ui.write(_('No patches applied\n'))
1773 return
1773 return
1774 patch = q.lookup('qtip')
1774 patch = q.lookup('qtip')
1775 absdest = q.join(name)
1775 absdest = q.join(name)
1776 if os.path.isdir(absdest):
1776 if os.path.isdir(absdest):
1777 name = os.path.join(name, os.path.basename(patch))
1777 name = os.path.join(name, os.path.basename(patch))
1778 absdest = q.join(name)
1778 absdest = q.join(name)
1779 if os.path.exists(absdest):
1779 if os.path.exists(absdest):
1780 raise util.Abort(_('%s already exists') % absdest)
1780 raise util.Abort(_('%s already exists') % absdest)
1781
1781
1782 if name in q.series:
1782 if name in q.series:
1783 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1783 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1784
1784
1785 if ui.verbose:
1785 if ui.verbose:
1786 ui.write('Renaming %s to %s\n' % (patch, name))
1786 ui.write('Renaming %s to %s\n' % (patch, name))
1787 i = q.find_series(patch)
1787 i = q.find_series(patch)
1788 q.full_series[i] = name
1788 q.full_series[i] = name
1789 q.parse_series()
1789 q.parse_series()
1790 q.series_dirty = 1
1790 q.series_dirty = 1
1791
1791
1792 info = q.isapplied(patch)
1792 info = q.isapplied(patch)
1793 if info:
1793 if info:
1794 q.applied[info[0]] = statusentry(info[1], name)
1794 q.applied[info[0]] = statusentry(info[1], name)
1795 q.applied_dirty = 1
1795 q.applied_dirty = 1
1796
1796
1797 util.rename(q.join(patch), absdest)
1797 util.rename(q.join(patch), absdest)
1798 r = q.qrepo()
1798 r = q.qrepo()
1799 if r:
1799 if r:
1800 wlock = r.wlock()
1800 wlock = r.wlock()
1801 if r.dirstate.state(name) == 'r':
1801 if r.dirstate.state(name) == 'r':
1802 r.undelete([name], wlock)
1802 r.undelete([name], wlock)
1803 r.copy(patch, name, wlock)
1803 r.copy(patch, name, wlock)
1804 r.remove([patch], False, wlock)
1804 r.remove([patch], False, wlock)
1805
1805
1806 q.save_dirty()
1806 q.save_dirty()
1807
1807
1808 def restore(ui, repo, rev, **opts):
1808 def restore(ui, repo, rev, **opts):
1809 """restore the queue state saved by a rev"""
1809 """restore the queue state saved by a rev"""
1810 rev = repo.lookup(rev)
1810 rev = repo.lookup(rev)
1811 q = repo.mq
1811 q = repo.mq
1812 q.restore(repo, rev, delete=opts['delete'],
1812 q.restore(repo, rev, delete=opts['delete'],
1813 qupdate=opts['update'])
1813 qupdate=opts['update'])
1814 q.save_dirty()
1814 q.save_dirty()
1815 return 0
1815 return 0
1816
1816
1817 def save(ui, repo, **opts):
1817 def save(ui, repo, **opts):
1818 """save current queue state"""
1818 """save current queue state"""
1819 q = repo.mq
1819 q = repo.mq
1820 message = commands.logmessage(opts)
1820 message = commands.logmessage(opts)
1821 ret = q.save(repo, msg=message)
1821 ret = q.save(repo, msg=message)
1822 if ret:
1822 if ret:
1823 return ret
1823 return ret
1824 q.save_dirty()
1824 q.save_dirty()
1825 if opts['copy']:
1825 if opts['copy']:
1826 path = q.path
1826 path = q.path
1827 if opts['name']:
1827 if opts['name']:
1828 newpath = os.path.join(q.basepath, opts['name'])
1828 newpath = os.path.join(q.basepath, opts['name'])
1829 if os.path.exists(newpath):
1829 if os.path.exists(newpath):
1830 if not os.path.isdir(newpath):
1830 if not os.path.isdir(newpath):
1831 raise util.Abort(_('destination %s exists and is not '
1831 raise util.Abort(_('destination %s exists and is not '
1832 'a directory') % newpath)
1832 'a directory') % newpath)
1833 if not opts['force']:
1833 if not opts['force']:
1834 raise util.Abort(_('destination %s exists, '
1834 raise util.Abort(_('destination %s exists, '
1835 'use -f to force') % newpath)
1835 'use -f to force') % newpath)
1836 else:
1836 else:
1837 newpath = savename(path)
1837 newpath = savename(path)
1838 ui.warn("copy %s to %s\n" % (path, newpath))
1838 ui.warn("copy %s to %s\n" % (path, newpath))
1839 util.copyfiles(path, newpath)
1839 util.copyfiles(path, newpath)
1840 if opts['empty']:
1840 if opts['empty']:
1841 try:
1841 try:
1842 os.unlink(q.join(q.status_path))
1842 os.unlink(q.join(q.status_path))
1843 except:
1843 except:
1844 pass
1844 pass
1845 return 0
1845 return 0
1846
1846
1847 def strip(ui, repo, rev, **opts):
1847 def strip(ui, repo, rev, **opts):
1848 """strip a revision and all later revs on the same branch"""
1848 """strip a revision and all later revs on the same branch"""
1849 rev = repo.lookup(rev)
1849 rev = repo.lookup(rev)
1850 backup = 'all'
1850 backup = 'all'
1851 if opts['backup']:
1851 if opts['backup']:
1852 backup = 'strip'
1852 backup = 'strip'
1853 elif opts['nobackup']:
1853 elif opts['nobackup']:
1854 backup = 'none'
1854 backup = 'none'
1855 update = repo.dirstate.parents()[0] != revlog.nullid
1855 update = repo.dirstate.parents()[0] != revlog.nullid
1856 repo.mq.strip(repo, rev, backup=backup, update=update)
1856 repo.mq.strip(repo, rev, backup=backup, update=update)
1857 return 0
1857 return 0
1858
1858
1859 def select(ui, repo, *args, **opts):
1859 def select(ui, repo, *args, **opts):
1860 '''set or print guarded patches to push
1860 '''set or print guarded patches to push
1861
1861
1862 Use the qguard command to set or print guards on patch, then use
1862 Use the qguard command to set or print guards on patch, then use
1863 qselect to tell mq which guards to use. A patch will be pushed if it
1863 qselect to tell mq which guards to use. A patch will be pushed if it
1864 has no guards or any positive guards match the currently selected guard,
1864 has no guards or any positive guards match the currently selected guard,
1865 but will not be pushed if any negative guards match the current guard.
1865 but will not be pushed if any negative guards match the current guard.
1866 For example:
1866 For example:
1867
1867
1868 qguard foo.patch -stable (negative guard)
1868 qguard foo.patch -stable (negative guard)
1869 qguard bar.patch +stable (positive guard)
1869 qguard bar.patch +stable (positive guard)
1870 qselect stable
1870 qselect stable
1871
1871
1872 This activates the "stable" guard. mq will skip foo.patch (because
1872 This activates the "stable" guard. mq will skip foo.patch (because
1873 it has a negative match) but push bar.patch (because it
1873 it has a negative match) but push bar.patch (because it
1874 has a positive match).
1874 has a positive match).
1875
1875
1876 With no arguments, prints the currently active guards.
1876 With no arguments, prints the currently active guards.
1877 With one argument, sets the active guard.
1877 With one argument, sets the active guard.
1878
1878
1879 Use -n/--none to deactivate guards (no other arguments needed).
1879 Use -n/--none to deactivate guards (no other arguments needed).
1880 When no guards are active, patches with positive guards are skipped
1880 When no guards are active, patches with positive guards are skipped
1881 and patches with negative guards are pushed.
1881 and patches with negative guards are pushed.
1882
1882
1883 qselect can change the guards on applied patches. It does not pop
1883 qselect can change the guards on applied patches. It does not pop
1884 guarded patches by default. Use --pop to pop back to the last applied
1884 guarded patches by default. Use --pop to pop back to the last applied
1885 patch that is not guarded. Use --reapply (which implies --pop) to push
1885 patch that is not guarded. Use --reapply (which implies --pop) to push
1886 back to the current patch afterwards, but skip guarded patches.
1886 back to the current patch afterwards, but skip guarded patches.
1887
1887
1888 Use -s/--series to print a list of all guards in the series file (no
1888 Use -s/--series to print a list of all guards in the series file (no
1889 other arguments needed). Use -v for more information.'''
1889 other arguments needed). Use -v for more information.'''
1890
1890
1891 q = repo.mq
1891 q = repo.mq
1892 guards = q.active()
1892 guards = q.active()
1893 if args or opts['none']:
1893 if args or opts['none']:
1894 old_unapplied = q.unapplied(repo)
1894 old_unapplied = q.unapplied(repo)
1895 old_guarded = [i for i in xrange(len(q.applied)) if
1895 old_guarded = [i for i in xrange(len(q.applied)) if
1896 not q.pushable(i)[0]]
1896 not q.pushable(i)[0]]
1897 q.set_active(args)
1897 q.set_active(args)
1898 q.save_dirty()
1898 q.save_dirty()
1899 if not args:
1899 if not args:
1900 ui.status(_('guards deactivated\n'))
1900 ui.status(_('guards deactivated\n'))
1901 if not opts['pop'] and not opts['reapply']:
1901 if not opts['pop'] and not opts['reapply']:
1902 unapplied = q.unapplied(repo)
1902 unapplied = q.unapplied(repo)
1903 guarded = [i for i in xrange(len(q.applied))
1903 guarded = [i for i in xrange(len(q.applied))
1904 if not q.pushable(i)[0]]
1904 if not q.pushable(i)[0]]
1905 if len(unapplied) != len(old_unapplied):
1905 if len(unapplied) != len(old_unapplied):
1906 ui.status(_('number of unguarded, unapplied patches has '
1906 ui.status(_('number of unguarded, unapplied patches has '
1907 'changed from %d to %d\n') %
1907 'changed from %d to %d\n') %
1908 (len(old_unapplied), len(unapplied)))
1908 (len(old_unapplied), len(unapplied)))
1909 if len(guarded) != len(old_guarded):
1909 if len(guarded) != len(old_guarded):
1910 ui.status(_('number of guarded, applied patches has changed '
1910 ui.status(_('number of guarded, applied patches has changed '
1911 'from %d to %d\n') %
1911 'from %d to %d\n') %
1912 (len(old_guarded), len(guarded)))
1912 (len(old_guarded), len(guarded)))
1913 elif opts['series']:
1913 elif opts['series']:
1914 guards = {}
1914 guards = {}
1915 noguards = 0
1915 noguards = 0
1916 for gs in q.series_guards:
1916 for gs in q.series_guards:
1917 if not gs:
1917 if not gs:
1918 noguards += 1
1918 noguards += 1
1919 for g in gs:
1919 for g in gs:
1920 guards.setdefault(g, 0)
1920 guards.setdefault(g, 0)
1921 guards[g] += 1
1921 guards[g] += 1
1922 if ui.verbose:
1922 if ui.verbose:
1923 guards['NONE'] = noguards
1923 guards['NONE'] = noguards
1924 guards = guards.items()
1924 guards = guards.items()
1925 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
1925 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
1926 if guards:
1926 if guards:
1927 ui.note(_('guards in series file:\n'))
1927 ui.note(_('guards in series file:\n'))
1928 for guard, count in guards:
1928 for guard, count in guards:
1929 ui.note('%2d ' % count)
1929 ui.note('%2d ' % count)
1930 ui.write(guard, '\n')
1930 ui.write(guard, '\n')
1931 else:
1931 else:
1932 ui.note(_('no guards in series file\n'))
1932 ui.note(_('no guards in series file\n'))
1933 else:
1933 else:
1934 if guards:
1934 if guards:
1935 ui.note(_('active guards:\n'))
1935 ui.note(_('active guards:\n'))
1936 for g in guards:
1936 for g in guards:
1937 ui.write(g, '\n')
1937 ui.write(g, '\n')
1938 else:
1938 else:
1939 ui.write(_('no active guards\n'))
1939 ui.write(_('no active guards\n'))
1940 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
1940 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
1941 popped = False
1941 popped = False
1942 if opts['pop'] or opts['reapply']:
1942 if opts['pop'] or opts['reapply']:
1943 for i in xrange(len(q.applied)):
1943 for i in xrange(len(q.applied)):
1944 pushable, reason = q.pushable(i)
1944 pushable, reason = q.pushable(i)
1945 if not pushable:
1945 if not pushable:
1946 ui.status(_('popping guarded patches\n'))
1946 ui.status(_('popping guarded patches\n'))
1947 popped = True
1947 popped = True
1948 if i == 0:
1948 if i == 0:
1949 q.pop(repo, all=True)
1949 q.pop(repo, all=True)
1950 else:
1950 else:
1951 q.pop(repo, i-1)
1951 q.pop(repo, i-1)
1952 break
1952 break
1953 if popped:
1953 if popped:
1954 try:
1954 try:
1955 if reapply:
1955 if reapply:
1956 ui.status(_('reapplying unguarded patches\n'))
1956 ui.status(_('reapplying unguarded patches\n'))
1957 q.push(repo, reapply)
1957 q.push(repo, reapply)
1958 finally:
1958 finally:
1959 q.save_dirty()
1959 q.save_dirty()
1960
1960
1961 def reposetup(ui, repo):
1961 def reposetup(ui, repo):
1962 class mqrepo(repo.__class__):
1962 class mqrepo(repo.__class__):
1963 def abort_if_wdir_patched(self, errmsg, force=False):
1963 def abort_if_wdir_patched(self, errmsg, force=False):
1964 if self.mq.applied and not force:
1964 if self.mq.applied and not force:
1965 parent = revlog.hex(self.dirstate.parents()[0])
1965 parent = revlog.hex(self.dirstate.parents()[0])
1966 if parent in [s.rev for s in self.mq.applied]:
1966 if parent in [s.rev for s in self.mq.applied]:
1967 raise util.Abort(errmsg)
1967 raise util.Abort(errmsg)
1968
1968
1969 def commit(self, *args, **opts):
1969 def commit(self, *args, **opts):
1970 if len(args) >= 6:
1970 if len(args) >= 6:
1971 force = args[5]
1971 force = args[5]
1972 else:
1972 else:
1973 force = opts.get('force')
1973 force = opts.get('force')
1974 self.abort_if_wdir_patched(
1974 self.abort_if_wdir_patched(
1975 _('cannot commit over an applied mq patch'),
1975 _('cannot commit over an applied mq patch'),
1976 force)
1976 force)
1977
1977
1978 return super(mqrepo, self).commit(*args, **opts)
1978 return super(mqrepo, self).commit(*args, **opts)
1979
1979
1980 def push(self, remote, force=False, revs=None):
1980 def push(self, remote, force=False, revs=None):
1981 if self.mq.applied and not force:
1981 if self.mq.applied and not force:
1982 raise util.Abort(_('source has mq patches applied'))
1982 raise util.Abort(_('source has mq patches applied'))
1983 return super(mqrepo, self).push(remote, force, revs)
1983 return super(mqrepo, self).push(remote, force, revs)
1984
1984
1985 def tags(self):
1985 def tags(self):
1986 if self.tagscache:
1986 if self.tagscache:
1987 return self.tagscache
1987 return self.tagscache
1988
1988
1989 tagscache = super(mqrepo, self).tags()
1989 tagscache = super(mqrepo, self).tags()
1990
1990
1991 q = self.mq
1991 q = self.mq
1992 if not q.applied:
1992 if not q.applied:
1993 return tagscache
1993 return tagscache
1994
1994
1995 mqtags = [(patch.rev, patch.name) for patch in q.applied]
1995 mqtags = [(patch.rev, patch.name) for patch in q.applied]
1996 mqtags.append((mqtags[-1][0], 'qtip'))
1996 mqtags.append((mqtags[-1][0], 'qtip'))
1997 mqtags.append((mqtags[0][0], 'qbase'))
1997 mqtags.append((mqtags[0][0], 'qbase'))
1998 for patch in mqtags:
1998 for patch in mqtags:
1999 if patch[1] in tagscache:
1999 if patch[1] in tagscache:
2000 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2000 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2001 else:
2001 else:
2002 tagscache[patch[1]] = revlog.bin(patch[0])
2002 tagscache[patch[1]] = revlog.bin(patch[0])
2003
2003
2004 return tagscache
2004 return tagscache
2005
2005
2006 def branchtags(self):
2006 def branchtags(self):
2007 if self.branchcache != None:
2007 if self.branchcache != None:
2008 return self.branchcache
2008 return self.branchcache
2009
2009
2010 q = self.mq
2010 q = self.mq
2011 if not q.applied:
2011 if not q.applied:
2012 return super(mqrepo, self).branchtags()
2012 return super(mqrepo, self).branchtags()
2013
2013
2014 self.branchcache = {} # avoid recursion in changectx
2014 self.branchcache = {} # avoid recursion in changectx
2015 cl = self.changelog
2015 cl = self.changelog
2016 partial, last, lrev = self._readbranchcache()
2016 partial, last, lrev = self._readbranchcache()
2017
2017
2018 qbase = cl.rev(revlog.bin(q.applied[0].rev))
2018 qbase = cl.rev(revlog.bin(q.applied[0].rev))
2019 start = lrev + 1
2019 start = lrev + 1
2020 if start < qbase:
2020 if start < qbase:
2021 # update the cache (excluding the patches) and save it
2021 # update the cache (excluding the patches) and save it
2022 self._updatebranchcache(partial, lrev+1, qbase)
2022 self._updatebranchcache(partial, lrev+1, qbase)
2023 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2023 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2024 start = qbase
2024 start = qbase
2025 # if start = qbase, the cache is as updated as it should be.
2025 # if start = qbase, the cache is as updated as it should be.
2026 # if start > qbase, the cache includes (part of) the patches.
2026 # if start > qbase, the cache includes (part of) the patches.
2027 # we might as well use it, but we won't save it.
2027 # we might as well use it, but we won't save it.
2028
2028
2029 # update the cache up to the tip
2029 # update the cache up to the tip
2030 self._updatebranchcache(partial, start, cl.count())
2030 self._updatebranchcache(partial, start, cl.count())
2031
2031
2032 self.branchcache = partial
2032 self.branchcache = partial
2033 return self.branchcache
2033 return self.branchcache
2034
2034
2035 if repo.local():
2035 if repo.local():
2036 repo.__class__ = mqrepo
2036 repo.__class__ = mqrepo
2037 repo.mq = queue(ui, repo.join(""))
2037 repo.mq = queue(ui, repo.join(""))
2038
2038
2039 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2039 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2040
2040
2041 cmdtable = {
2041 cmdtable = {
2042 "qapplied": (applied, [] + seriesopts, 'hg qapplied [-s] [PATCH]'),
2042 "qapplied": (applied, [] + seriesopts, 'hg qapplied [-s] [PATCH]'),
2043 "qclone": (clone,
2043 "qclone": (clone,
2044 [('', 'pull', None, _('use pull protocol to copy metadata')),
2044 [('', 'pull', None, _('use pull protocol to copy metadata')),
2045 ('U', 'noupdate', None, _('do not update the new working directories')),
2045 ('U', 'noupdate', None, _('do not update the new working directories')),
2046 ('', 'uncompressed', None,
2046 ('', 'uncompressed', None,
2047 _('use uncompressed transfer (fast over LAN)')),
2047 _('use uncompressed transfer (fast over LAN)')),
2048 ('e', 'ssh', '', _('specify ssh command to use')),
2048 ('e', 'ssh', '', _('specify ssh command to use')),
2049 ('p', 'patches', '', _('location of source patch repo')),
2049 ('p', 'patches', '', _('location of source patch repo')),
2050 ('', 'remotecmd', '',
2050 ('', 'remotecmd', '',
2051 _('specify hg command to run on the remote side'))],
2051 _('specify hg command to run on the remote side'))],
2052 'hg qclone [OPTION]... SOURCE [DEST]'),
2052 'hg qclone [OPTION]... SOURCE [DEST]'),
2053 "qcommit|qci":
2053 "qcommit|qci":
2054 (commit,
2054 (commit,
2055 commands.table["^commit|ci"][1],
2055 commands.table["^commit|ci"][1],
2056 'hg qcommit [OPTION]... [FILE]...'),
2056 'hg qcommit [OPTION]... [FILE]...'),
2057 "^qdiff": (diff,
2057 "^qdiff": (diff,
2058 [('I', 'include', [], _('include names matching the given patterns')),
2058 [('I', 'include', [], _('include names matching the given patterns')),
2059 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2059 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2060 'hg qdiff [-I] [-X] [FILE]...'),
2060 'hg qdiff [-I] [-X] [FILE]...'),
2061 "qdelete|qremove|qrm":
2061 "qdelete|qremove|qrm":
2062 (delete,
2062 (delete,
2063 [('k', 'keep', None, _('keep patch file')),
2063 [('k', 'keep', None, _('keep patch file')),
2064 ('r', 'rev', [], _('stop managing a revision'))],
2064 ('r', 'rev', [], _('stop managing a revision'))],
2065 'hg qdelete [-k] [-r REV]... PATCH...'),
2065 'hg qdelete [-k] [-r REV]... PATCH...'),
2066 'qfold':
2066 'qfold':
2067 (fold,
2067 (fold,
2068 [('e', 'edit', None, _('edit patch header')),
2068 [('e', 'edit', None, _('edit patch header')),
2069 ('k', 'keep', None, _('keep folded patch files')),
2069 ('k', 'keep', None, _('keep folded patch files')),
2070 ('m', 'message', '', _('set patch header to <text>')),
2070 ('m', 'message', '', _('set patch header to <text>')),
2071 ('l', 'logfile', '', _('set patch header to contents of <file>'))],
2071 ('l', 'logfile', '', _('set patch header to contents of <file>'))],
2072 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
2072 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
2073 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
2073 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
2074 ('n', 'none', None, _('drop all guards'))],
2074 ('n', 'none', None, _('drop all guards'))],
2075 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
2075 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
2076 'qheader': (header, [],
2076 'qheader': (header, [],
2077 _('hg qheader [PATCH]')),
2077 _('hg qheader [PATCH]')),
2078 "^qimport":
2078 "^qimport":
2079 (qimport,
2079 (qimport,
2080 [('e', 'existing', None, 'import file in patch dir'),
2080 [('e', 'existing', None, 'import file in patch dir'),
2081 ('n', 'name', '', 'patch file name'),
2081 ('n', 'name', '', 'patch file name'),
2082 ('f', 'force', None, 'overwrite existing files'),
2082 ('f', 'force', None, 'overwrite existing files'),
2083 ('r', 'rev', [], 'place existing revisions under mq control')],
2083 ('r', 'rev', [], 'place existing revisions under mq control')],
2084 'hg qimport [-e] [-n NAME] [-f] [-r REV]... FILE...'),
2084 'hg qimport [-e] [-n NAME] [-f] [-r REV]... FILE...'),
2085 "^qinit":
2085 "^qinit":
2086 (init,
2086 (init,
2087 [('c', 'create-repo', None, 'create queue repository')],
2087 [('c', 'create-repo', None, 'create queue repository')],
2088 'hg qinit [-c]'),
2088 'hg qinit [-c]'),
2089 "qnew":
2089 "qnew":
2090 (new,
2090 (new,
2091 [('e', 'edit', None, _('edit commit message')),
2091 [('e', 'edit', None, _('edit commit message')),
2092 ('m', 'message', '', _('use <text> as commit message')),
2092 ('m', 'message', '', _('use <text> as commit message')),
2093 ('l', 'logfile', '', _('read the commit message from <file>')),
2093 ('l', 'logfile', '', _('read the commit message from <file>')),
2094 ('f', 'force', None, _('import uncommitted changes into patch'))],
2094 ('f', 'force', None, _('import uncommitted changes into patch'))],
2095 'hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH'),
2095 'hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH'),
2096 "qnext": (next, [] + seriesopts, 'hg qnext [-s]'),
2096 "qnext": (next, [] + seriesopts, 'hg qnext [-s]'),
2097 "qprev": (prev, [] + seriesopts, 'hg qprev [-s]'),
2097 "qprev": (prev, [] + seriesopts, 'hg qprev [-s]'),
2098 "^qpop":
2098 "^qpop":
2099 (pop,
2099 (pop,
2100 [('a', 'all', None, 'pop all patches'),
2100 [('a', 'all', None, 'pop all patches'),
2101 ('n', 'name', '', 'queue name to pop'),
2101 ('n', 'name', '', 'queue name to pop'),
2102 ('f', 'force', None, 'forget any local changes')],
2102 ('f', 'force', None, 'forget any local changes')],
2103 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
2103 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
2104 "^qpush":
2104 "^qpush":
2105 (push,
2105 (push,
2106 [('f', 'force', None, 'apply if the patch has rejects'),
2106 [('f', 'force', None, 'apply if the patch has rejects'),
2107 ('l', 'list', None, 'list patch name in commit text'),
2107 ('l', 'list', None, 'list patch name in commit text'),
2108 ('a', 'all', None, 'apply all patches'),
2108 ('a', 'all', None, 'apply all patches'),
2109 ('m', 'merge', None, 'merge from another queue'),
2109 ('m', 'merge', None, 'merge from another queue'),
2110 ('n', 'name', '', 'merge queue name')],
2110 ('n', 'name', '', 'merge queue name')],
2111 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
2111 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
2112 "^qrefresh":
2112 "^qrefresh":
2113 (refresh,
2113 (refresh,
2114 [('e', 'edit', None, _('edit commit message')),
2114 [('e', 'edit', None, _('edit commit message')),
2115 ('m', 'message', '', _('change commit message with <text>')),
2115 ('m', 'message', '', _('change commit message with <text>')),
2116 ('l', 'logfile', '', _('change commit message with <file> content')),
2116 ('l', 'logfile', '', _('change commit message with <file> content')),
2117 ('g', 'git', None, _('use git extended diff format')),
2117 ('g', 'git', None, _('use git extended diff format')),
2118 ('s', 'short', None, 'short refresh'),
2118 ('s', 'short', None, 'short refresh'),
2119 ('I', 'include', [], _('include names matching the given patterns')),
2119 ('I', 'include', [], _('include names matching the given patterns')),
2120 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2120 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2121 'hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] FILES...'),
2121 'hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] FILES...'),
2122 'qrename|qmv':
2122 'qrename|qmv':
2123 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
2123 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
2124 "qrestore":
2124 "qrestore":
2125 (restore,
2125 (restore,
2126 [('d', 'delete', None, 'delete save entry'),
2126 [('d', 'delete', None, 'delete save entry'),
2127 ('u', 'update', None, 'update queue working dir')],
2127 ('u', 'update', None, 'update queue working dir')],
2128 'hg qrestore [-d] [-u] REV'),
2128 'hg qrestore [-d] [-u] REV'),
2129 "qsave":
2129 "qsave":
2130 (save,
2130 (save,
2131 [('m', 'message', '', _('use <text> as commit message')),
2131 [('m', 'message', '', _('use <text> as commit message')),
2132 ('l', 'logfile', '', _('read the commit message from <file>')),
2132 ('l', 'logfile', '', _('read the commit message from <file>')),
2133 ('c', 'copy', None, 'copy patch directory'),
2133 ('c', 'copy', None, 'copy patch directory'),
2134 ('n', 'name', '', 'copy directory name'),
2134 ('n', 'name', '', 'copy directory name'),
2135 ('e', 'empty', None, 'clear queue status file'),
2135 ('e', 'empty', None, 'clear queue status file'),
2136 ('f', 'force', None, 'force copy')],
2136 ('f', 'force', None, 'force copy')],
2137 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
2137 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
2138 "qselect": (select,
2138 "qselect": (select,
2139 [('n', 'none', None, _('disable all guards')),
2139 [('n', 'none', None, _('disable all guards')),
2140 ('s', 'series', None, _('list all guards in series file')),
2140 ('s', 'series', None, _('list all guards in series file')),
2141 ('', 'pop', None,
2141 ('', 'pop', None,
2142 _('pop to before first guarded applied patch')),
2142 _('pop to before first guarded applied patch')),
2143 ('', 'reapply', None, _('pop, then reapply patches'))],
2143 ('', 'reapply', None, _('pop, then reapply patches'))],
2144 'hg qselect [OPTION...] [GUARD...]'),
2144 'hg qselect [OPTION...] [GUARD...]'),
2145 "qseries":
2145 "qseries":
2146 (series,
2146 (series,
2147 [('m', 'missing', None, 'print patches not in series')] + seriesopts,
2147 [('m', 'missing', None, 'print patches not in series')] + seriesopts,
2148 'hg qseries [-ms]'),
2148 'hg qseries [-ms]'),
2149 "^strip":
2149 "^strip":
2150 (strip,
2150 (strip,
2151 [('f', 'force', None, 'force multi-head removal'),
2151 [('f', 'force', None, 'force multi-head removal'),
2152 ('b', 'backup', None, 'bundle unrelated changesets'),
2152 ('b', 'backup', None, 'bundle unrelated changesets'),
2153 ('n', 'nobackup', None, 'no backups')],
2153 ('n', 'nobackup', None, 'no backups')],
2154 'hg strip [-f] [-b] [-n] REV'),
2154 'hg strip [-f] [-b] [-n] REV'),
2155 "qtop": (top, [] + seriesopts, 'hg qtop [-s]'),
2155 "qtop": (top, [] + seriesopts, 'hg qtop [-s]'),
2156 "qunapplied": (unapplied, [] + seriesopts, 'hg qunapplied [-s] [PATCH]'),
2156 "qunapplied": (unapplied, [] + seriesopts, 'hg qunapplied [-s] [PATCH]'),
2157 }
2157 }
@@ -1,202 +1,204 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import demandload
8 from demandload import demandload
9 from node import *
9 from node import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), 'mdiff util')
11 demandload(globals(), 'mdiff util')
12 demandload(globals(), 'os sys')
12 demandload(globals(), 'os sys')
13
13
14 revrangesep = ':'
14 revrangesep = ':'
15
15
16 def revpair(ui, repo, revs):
16 def revpair(ui, repo, revs):
17 '''return pair of nodes, given list of revisions. second item can
17 '''return pair of nodes, given list of revisions. second item can
18 be None, meaning use working dir.'''
18 be None, meaning use working dir.'''
19
19
20 def revfix(repo, val, defval):
20 def revfix(repo, val, defval):
21 if not val and val != 0:
21 if not val and val != 0:
22 val = defval
22 val = defval
23 return repo.lookup(val)
23 return repo.lookup(val)
24
24
25 if not revs:
25 if not revs:
26 return repo.dirstate.parents()[0], None
26 return repo.dirstate.parents()[0], None
27 end = None
27 end = None
28 if len(revs) == 1:
28 if len(revs) == 1:
29 if revrangesep in revs[0]:
29 if revrangesep in revs[0]:
30 start, end = revs[0].split(revrangesep, 1)
30 start, end = revs[0].split(revrangesep, 1)
31 start = revfix(repo, start, 0)
31 start = revfix(repo, start, 0)
32 end = revfix(repo, end, repo.changelog.count() - 1)
32 end = revfix(repo, end, repo.changelog.count() - 1)
33 else:
33 else:
34 start = revfix(repo, revs[0], None)
34 start = revfix(repo, revs[0], None)
35 elif len(revs) == 2:
35 elif len(revs) == 2:
36 if revrangesep in revs[0] or revrangesep in revs[1]:
36 if revrangesep in revs[0] or revrangesep in revs[1]:
37 raise util.Abort(_('too many revisions specified'))
37 raise util.Abort(_('too many revisions specified'))
38 start = revfix(repo, revs[0], None)
38 start = revfix(repo, revs[0], None)
39 end = revfix(repo, revs[1], None)
39 end = revfix(repo, revs[1], None)
40 else:
40 else:
41 raise util.Abort(_('too many revisions specified'))
41 raise util.Abort(_('too many revisions specified'))
42 return start, end
42 return start, end
43
43
44 def revrange(ui, repo, revs):
44 def revrange(ui, repo, revs):
45 """Yield revision as strings from a list of revision specifications."""
45 """Yield revision as strings from a list of revision specifications."""
46
46
47 def revfix(repo, val, defval):
47 def revfix(repo, val, defval):
48 if not val and val != 0:
48 if not val and val != 0:
49 return defval
49 return defval
50 return repo.changelog.rev(repo.lookup(val))
50 return repo.changelog.rev(repo.lookup(val))
51
51
52 seen = {}
52 seen, l = {}, []
53 for spec in revs:
53 for spec in revs:
54 if revrangesep in spec:
54 if revrangesep in spec:
55 start, end = spec.split(revrangesep, 1)
55 start, end = spec.split(revrangesep, 1)
56 start = revfix(repo, start, 0)
56 start = revfix(repo, start, 0)
57 end = revfix(repo, end, repo.changelog.count() - 1)
57 end = revfix(repo, end, repo.changelog.count() - 1)
58 step = start > end and -1 or 1
58 step = start > end and -1 or 1
59 for rev in xrange(start, end+step, step):
59 for rev in xrange(start, end+step, step):
60 if rev in seen:
60 if rev in seen:
61 continue
61 continue
62 seen[rev] = 1
62 seen[rev] = 1
63 yield rev
63 l.append(rev)
64 else:
64 else:
65 rev = revfix(repo, spec, None)
65 rev = revfix(repo, spec, None)
66 if rev in seen:
66 if rev in seen:
67 continue
67 continue
68 seen[rev] = 1
68 seen[rev] = 1
69 yield rev
69 l.append(rev)
70
71 return l
70
72
71 def make_filename(repo, pat, node,
73 def make_filename(repo, pat, node,
72 total=None, seqno=None, revwidth=None, pathname=None):
74 total=None, seqno=None, revwidth=None, pathname=None):
73 node_expander = {
75 node_expander = {
74 'H': lambda: hex(node),
76 'H': lambda: hex(node),
75 'R': lambda: str(repo.changelog.rev(node)),
77 'R': lambda: str(repo.changelog.rev(node)),
76 'h': lambda: short(node),
78 'h': lambda: short(node),
77 }
79 }
78 expander = {
80 expander = {
79 '%': lambda: '%',
81 '%': lambda: '%',
80 'b': lambda: os.path.basename(repo.root),
82 'b': lambda: os.path.basename(repo.root),
81 }
83 }
82
84
83 try:
85 try:
84 if node:
86 if node:
85 expander.update(node_expander)
87 expander.update(node_expander)
86 if node and revwidth is not None:
88 if node and revwidth is not None:
87 expander['r'] = (lambda:
89 expander['r'] = (lambda:
88 str(repo.changelog.rev(node)).zfill(revwidth))
90 str(repo.changelog.rev(node)).zfill(revwidth))
89 if total is not None:
91 if total is not None:
90 expander['N'] = lambda: str(total)
92 expander['N'] = lambda: str(total)
91 if seqno is not None:
93 if seqno is not None:
92 expander['n'] = lambda: str(seqno)
94 expander['n'] = lambda: str(seqno)
93 if total is not None and seqno is not None:
95 if total is not None and seqno is not None:
94 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
96 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
95 if pathname is not None:
97 if pathname is not None:
96 expander['s'] = lambda: os.path.basename(pathname)
98 expander['s'] = lambda: os.path.basename(pathname)
97 expander['d'] = lambda: os.path.dirname(pathname) or '.'
99 expander['d'] = lambda: os.path.dirname(pathname) or '.'
98 expander['p'] = lambda: pathname
100 expander['p'] = lambda: pathname
99
101
100 newname = []
102 newname = []
101 patlen = len(pat)
103 patlen = len(pat)
102 i = 0
104 i = 0
103 while i < patlen:
105 while i < patlen:
104 c = pat[i]
106 c = pat[i]
105 if c == '%':
107 if c == '%':
106 i += 1
108 i += 1
107 c = pat[i]
109 c = pat[i]
108 c = expander[c]()
110 c = expander[c]()
109 newname.append(c)
111 newname.append(c)
110 i += 1
112 i += 1
111 return ''.join(newname)
113 return ''.join(newname)
112 except KeyError, inst:
114 except KeyError, inst:
113 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
115 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
114 inst.args[0])
116 inst.args[0])
115
117
116 def make_file(repo, pat, node=None,
118 def make_file(repo, pat, node=None,
117 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
119 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
118 if not pat or pat == '-':
120 if not pat or pat == '-':
119 return 'w' in mode and sys.stdout or sys.stdin
121 return 'w' in mode and sys.stdout or sys.stdin
120 if hasattr(pat, 'write') and 'w' in mode:
122 if hasattr(pat, 'write') and 'w' in mode:
121 return pat
123 return pat
122 if hasattr(pat, 'read') and 'r' in mode:
124 if hasattr(pat, 'read') and 'r' in mode:
123 return pat
125 return pat
124 return open(make_filename(repo, pat, node, total, seqno, revwidth,
126 return open(make_filename(repo, pat, node, total, seqno, revwidth,
125 pathname),
127 pathname),
126 mode)
128 mode)
127
129
128 def matchpats(repo, pats=[], opts={}, head=''):
130 def matchpats(repo, pats=[], opts={}, head=''):
129 cwd = repo.getcwd()
131 cwd = repo.getcwd()
130 if not pats and cwd:
132 if not pats and cwd:
131 opts['include'] = [os.path.join(cwd, i)
133 opts['include'] = [os.path.join(cwd, i)
132 for i in opts.get('include', [])]
134 for i in opts.get('include', [])]
133 opts['exclude'] = [os.path.join(cwd, x)
135 opts['exclude'] = [os.path.join(cwd, x)
134 for x in opts.get('exclude', [])]
136 for x in opts.get('exclude', [])]
135 cwd = ''
137 cwd = ''
136 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
138 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
137 opts.get('exclude'), head)
139 opts.get('exclude'), head)
138
140
139 def makewalk(repo, pats=[], opts={}, node=None, head='', badmatch=None):
141 def makewalk(repo, pats=[], opts={}, node=None, head='', badmatch=None):
140 files, matchfn, anypats = matchpats(repo, pats, opts, head)
142 files, matchfn, anypats = matchpats(repo, pats, opts, head)
141 exact = dict(zip(files, files))
143 exact = dict(zip(files, files))
142 def walk():
144 def walk():
143 for src, fn in repo.walk(node=node, files=files, match=matchfn,
145 for src, fn in repo.walk(node=node, files=files, match=matchfn,
144 badmatch=badmatch):
146 badmatch=badmatch):
145 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
147 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
146 return files, matchfn, walk()
148 return files, matchfn, walk()
147
149
148 def walk(repo, pats=[], opts={}, node=None, head='', badmatch=None):
150 def walk(repo, pats=[], opts={}, node=None, head='', badmatch=None):
149 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
151 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
150 for r in results:
152 for r in results:
151 yield r
153 yield r
152
154
153 def findrenames(repo, added=None, removed=None, threshold=0.5):
155 def findrenames(repo, added=None, removed=None, threshold=0.5):
154 if added is None or removed is None:
156 if added is None or removed is None:
155 added, removed = repo.status()[1:3]
157 added, removed = repo.status()[1:3]
156 changes = repo.changelog.read(repo.dirstate.parents()[0])
158 changes = repo.changelog.read(repo.dirstate.parents()[0])
157 mf = repo.manifest.read(changes[0])
159 mf = repo.manifest.read(changes[0])
158 for a in added:
160 for a in added:
159 aa = repo.wread(a)
161 aa = repo.wread(a)
160 bestscore, bestname = None, None
162 bestscore, bestname = None, None
161 for r in removed:
163 for r in removed:
162 rr = repo.file(r).read(mf[r])
164 rr = repo.file(r).read(mf[r])
163 delta = mdiff.textdiff(aa, rr)
165 delta = mdiff.textdiff(aa, rr)
164 if len(delta) < len(aa):
166 if len(delta) < len(aa):
165 myscore = 1.0 - (float(len(delta)) / len(aa))
167 myscore = 1.0 - (float(len(delta)) / len(aa))
166 if bestscore is None or myscore > bestscore:
168 if bestscore is None or myscore > bestscore:
167 bestscore, bestname = myscore, r
169 bestscore, bestname = myscore, r
168 if bestname and bestscore >= threshold:
170 if bestname and bestscore >= threshold:
169 yield bestname, a, bestscore
171 yield bestname, a, bestscore
170
172
171 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None,
173 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None,
172 similarity=None):
174 similarity=None):
173 if dry_run is None:
175 if dry_run is None:
174 dry_run = opts.get('dry_run')
176 dry_run = opts.get('dry_run')
175 if similarity is None:
177 if similarity is None:
176 similarity = float(opts.get('similarity') or 0)
178 similarity = float(opts.get('similarity') or 0)
177 add, remove = [], []
179 add, remove = [], []
178 mapping = {}
180 mapping = {}
179 for src, abs, rel, exact in walk(repo, pats, opts):
181 for src, abs, rel, exact in walk(repo, pats, opts):
180 if src == 'f' and repo.dirstate.state(abs) == '?':
182 if src == 'f' and repo.dirstate.state(abs) == '?':
181 add.append(abs)
183 add.append(abs)
182 mapping[abs] = rel, exact
184 mapping[abs] = rel, exact
183 if repo.ui.verbose or not exact:
185 if repo.ui.verbose or not exact:
184 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
186 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
185 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
187 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
186 remove.append(abs)
188 remove.append(abs)
187 mapping[abs] = rel, exact
189 mapping[abs] = rel, exact
188 if repo.ui.verbose or not exact:
190 if repo.ui.verbose or not exact:
189 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
191 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
190 if not dry_run:
192 if not dry_run:
191 repo.add(add, wlock=wlock)
193 repo.add(add, wlock=wlock)
192 repo.remove(remove, wlock=wlock)
194 repo.remove(remove, wlock=wlock)
193 if similarity > 0:
195 if similarity > 0:
194 for old, new, score in findrenames(repo, add, remove, similarity):
196 for old, new, score in findrenames(repo, add, remove, similarity):
195 oldrel, oldexact = mapping[old]
197 oldrel, oldexact = mapping[old]
196 newrel, newexact = mapping[new]
198 newrel, newexact = mapping[new]
197 if repo.ui.verbose or not oldexact or not newexact:
199 if repo.ui.verbose or not oldexact or not newexact:
198 repo.ui.status(_('recording removal of %s as rename to %s '
200 repo.ui.status(_('recording removal of %s as rename to %s '
199 '(%d%% similar)\n') %
201 '(%d%% similar)\n') %
200 (oldrel, newrel, score * 100))
202 (oldrel, newrel, score * 100))
201 if not dry_run:
203 if not dry_run:
202 repo.copy(old, new, wlock=wlock)
204 repo.copy(old, new, wlock=wlock)
@@ -1,3522 +1,3521 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import demandload
8 from demandload import demandload
9 from node import *
9 from node import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), "os re sys signal shutil imp urllib pdb shlex")
11 demandload(globals(), "os re sys signal shutil imp urllib pdb shlex")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 demandload(globals(), "fnmatch difflib patch random signal tempfile time")
13 demandload(globals(), "fnmatch difflib patch random signal tempfile time")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15 demandload(globals(), "archival cStringIO changegroup")
15 demandload(globals(), "archival cStringIO changegroup")
16 demandload(globals(), "cmdutil hgweb.server sshserver")
16 demandload(globals(), "cmdutil hgweb.server sshserver")
17
17
18 class UnknownCommand(Exception):
18 class UnknownCommand(Exception):
19 """Exception raised if command is not in the command table."""
19 """Exception raised if command is not in the command table."""
20 class AmbiguousCommand(Exception):
20 class AmbiguousCommand(Exception):
21 """Exception raised if command shortcut matches more than one command."""
21 """Exception raised if command shortcut matches more than one command."""
22
22
23 def bail_if_changed(repo):
23 def bail_if_changed(repo):
24 modified, added, removed, deleted = repo.status()[:4]
24 modified, added, removed, deleted = repo.status()[:4]
25 if modified or added or removed or deleted:
25 if modified or added or removed or deleted:
26 raise util.Abort(_("outstanding uncommitted changes"))
26 raise util.Abort(_("outstanding uncommitted changes"))
27
27
28 def relpath(repo, args):
28 def relpath(repo, args):
29 cwd = repo.getcwd()
29 cwd = repo.getcwd()
30 if cwd:
30 if cwd:
31 return [util.normpath(os.path.join(cwd, x)) for x in args]
31 return [util.normpath(os.path.join(cwd, x)) for x in args]
32 return args
32 return args
33
33
34 def logmessage(opts):
34 def logmessage(opts):
35 """ get the log message according to -m and -l option """
35 """ get the log message according to -m and -l option """
36 message = opts['message']
36 message = opts['message']
37 logfile = opts['logfile']
37 logfile = opts['logfile']
38
38
39 if message and logfile:
39 if message and logfile:
40 raise util.Abort(_('options --message and --logfile are mutually '
40 raise util.Abort(_('options --message and --logfile are mutually '
41 'exclusive'))
41 'exclusive'))
42 if not message and logfile:
42 if not message and logfile:
43 try:
43 try:
44 if logfile == '-':
44 if logfile == '-':
45 message = sys.stdin.read()
45 message = sys.stdin.read()
46 else:
46 else:
47 message = open(logfile).read()
47 message = open(logfile).read()
48 except IOError, inst:
48 except IOError, inst:
49 raise util.Abort(_("can't read commit message '%s': %s") %
49 raise util.Abort(_("can't read commit message '%s': %s") %
50 (logfile, inst.strerror))
50 (logfile, inst.strerror))
51 return message
51 return message
52
52
53 def walkchangerevs(ui, repo, pats, opts):
53 def walkchangerevs(ui, repo, pats, opts):
54 '''Iterate over files and the revs they changed in.
54 '''Iterate over files and the revs they changed in.
55
55
56 Callers most commonly need to iterate backwards over the history
56 Callers most commonly need to iterate backwards over the history
57 it is interested in. Doing so has awful (quadratic-looking)
57 it is interested in. Doing so has awful (quadratic-looking)
58 performance, so we use iterators in a "windowed" way.
58 performance, so we use iterators in a "windowed" way.
59
59
60 We walk a window of revisions in the desired order. Within the
60 We walk a window of revisions in the desired order. Within the
61 window, we first walk forwards to gather data, then in the desired
61 window, we first walk forwards to gather data, then in the desired
62 order (usually backwards) to display it.
62 order (usually backwards) to display it.
63
63
64 This function returns an (iterator, getchange, matchfn) tuple. The
64 This function returns an (iterator, getchange, matchfn) tuple. The
65 getchange function returns the changelog entry for a numeric
65 getchange function returns the changelog entry for a numeric
66 revision. The iterator yields 3-tuples. They will be of one of
66 revision. The iterator yields 3-tuples. They will be of one of
67 the following forms:
67 the following forms:
68
68
69 "window", incrementing, lastrev: stepping through a window,
69 "window", incrementing, lastrev: stepping through a window,
70 positive if walking forwards through revs, last rev in the
70 positive if walking forwards through revs, last rev in the
71 sequence iterated over - use to reset state for the current window
71 sequence iterated over - use to reset state for the current window
72
72
73 "add", rev, fns: out-of-order traversal of the given file names
73 "add", rev, fns: out-of-order traversal of the given file names
74 fns, which changed during revision rev - use to gather data for
74 fns, which changed during revision rev - use to gather data for
75 possible display
75 possible display
76
76
77 "iter", rev, None: in-order traversal of the revs earlier iterated
77 "iter", rev, None: in-order traversal of the revs earlier iterated
78 over with "add" - use to display data'''
78 over with "add" - use to display data'''
79
79
80 def increasing_windows(start, end, windowsize=8, sizelimit=512):
80 def increasing_windows(start, end, windowsize=8, sizelimit=512):
81 if start < end:
81 if start < end:
82 while start < end:
82 while start < end:
83 yield start, min(windowsize, end-start)
83 yield start, min(windowsize, end-start)
84 start += windowsize
84 start += windowsize
85 if windowsize < sizelimit:
85 if windowsize < sizelimit:
86 windowsize *= 2
86 windowsize *= 2
87 else:
87 else:
88 while start > end:
88 while start > end:
89 yield start, min(windowsize, start-end-1)
89 yield start, min(windowsize, start-end-1)
90 start -= windowsize
90 start -= windowsize
91 if windowsize < sizelimit:
91 if windowsize < sizelimit:
92 windowsize *= 2
92 windowsize *= 2
93
93
94
94
95 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
95 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
96 follow = opts.get('follow') or opts.get('follow_first')
96 follow = opts.get('follow') or opts.get('follow_first')
97
97
98 if repo.changelog.count() == 0:
98 if repo.changelog.count() == 0:
99 return [], False, matchfn
99 return [], False, matchfn
100
100
101 if follow:
101 if follow:
102 defrange = '%s:0' % repo.changectx().rev()
102 defrange = '%s:0' % repo.changectx().rev()
103 else:
103 else:
104 defrange = 'tip:0'
104 defrange = 'tip:0'
105 revs = map(int, cmdutil.revrange(ui, repo, opts['rev'] or [defrange]))
105 revs = cmdutil.revrange(ui, repo, opts['rev'] or [defrange])
106 wanted = {}
106 wanted = {}
107 slowpath = anypats
107 slowpath = anypats
108 fncache = {}
108 fncache = {}
109
109
110 chcache = {}
110 chcache = {}
111 def getchange(rev):
111 def getchange(rev):
112 ch = chcache.get(rev)
112 ch = chcache.get(rev)
113 if ch is None:
113 if ch is None:
114 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
114 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
115 return ch
115 return ch
116
116
117 if not slowpath and not files:
117 if not slowpath and not files:
118 # No files, no patterns. Display all revs.
118 # No files, no patterns. Display all revs.
119 wanted = dict(zip(revs, revs))
119 wanted = dict(zip(revs, revs))
120 copies = []
120 copies = []
121 if not slowpath:
121 if not slowpath:
122 # Only files, no patterns. Check the history of each file.
122 # Only files, no patterns. Check the history of each file.
123 def filerevgen(filelog, node):
123 def filerevgen(filelog, node):
124 cl_count = repo.changelog.count()
124 cl_count = repo.changelog.count()
125 if node is None:
125 if node is None:
126 last = filelog.count() - 1
126 last = filelog.count() - 1
127 else:
127 else:
128 last = filelog.rev(node)
128 last = filelog.rev(node)
129 for i, window in increasing_windows(last, -1):
129 for i, window in increasing_windows(last, -1):
130 revs = []
130 revs = []
131 for j in xrange(i - window, i + 1):
131 for j in xrange(i - window, i + 1):
132 n = filelog.node(j)
132 n = filelog.node(j)
133 revs.append((filelog.linkrev(n),
133 revs.append((filelog.linkrev(n),
134 follow and filelog.renamed(n)))
134 follow and filelog.renamed(n)))
135 revs.reverse()
135 revs.reverse()
136 for rev in revs:
136 for rev in revs:
137 # only yield rev for which we have the changelog, it can
137 # only yield rev for which we have the changelog, it can
138 # happen while doing "hg log" during a pull or commit
138 # happen while doing "hg log" during a pull or commit
139 if rev[0] < cl_count:
139 if rev[0] < cl_count:
140 yield rev
140 yield rev
141 def iterfiles():
141 def iterfiles():
142 for filename in files:
142 for filename in files:
143 yield filename, None
143 yield filename, None
144 for filename_node in copies:
144 for filename_node in copies:
145 yield filename_node
145 yield filename_node
146 minrev, maxrev = min(revs), max(revs)
146 minrev, maxrev = min(revs), max(revs)
147 for file_, node in iterfiles():
147 for file_, node in iterfiles():
148 filelog = repo.file(file_)
148 filelog = repo.file(file_)
149 # A zero count may be a directory or deleted file, so
149 # A zero count may be a directory or deleted file, so
150 # try to find matching entries on the slow path.
150 # try to find matching entries on the slow path.
151 if filelog.count() == 0:
151 if filelog.count() == 0:
152 slowpath = True
152 slowpath = True
153 break
153 break
154 for rev, copied in filerevgen(filelog, node):
154 for rev, copied in filerevgen(filelog, node):
155 if rev <= maxrev:
155 if rev <= maxrev:
156 if rev < minrev:
156 if rev < minrev:
157 break
157 break
158 fncache.setdefault(rev, [])
158 fncache.setdefault(rev, [])
159 fncache[rev].append(file_)
159 fncache[rev].append(file_)
160 wanted[rev] = 1
160 wanted[rev] = 1
161 if follow and copied:
161 if follow and copied:
162 copies.append(copied)
162 copies.append(copied)
163 if slowpath:
163 if slowpath:
164 if follow:
164 if follow:
165 raise util.Abort(_('can only follow copies/renames for explicit '
165 raise util.Abort(_('can only follow copies/renames for explicit '
166 'file names'))
166 'file names'))
167
167
168 # The slow path checks files modified in every changeset.
168 # The slow path checks files modified in every changeset.
169 def changerevgen():
169 def changerevgen():
170 for i, window in increasing_windows(repo.changelog.count()-1, -1):
170 for i, window in increasing_windows(repo.changelog.count()-1, -1):
171 for j in xrange(i - window, i + 1):
171 for j in xrange(i - window, i + 1):
172 yield j, getchange(j)[3]
172 yield j, getchange(j)[3]
173
173
174 for rev, changefiles in changerevgen():
174 for rev, changefiles in changerevgen():
175 matches = filter(matchfn, changefiles)
175 matches = filter(matchfn, changefiles)
176 if matches:
176 if matches:
177 fncache[rev] = matches
177 fncache[rev] = matches
178 wanted[rev] = 1
178 wanted[rev] = 1
179
179
180 class followfilter:
180 class followfilter:
181 def __init__(self, onlyfirst=False):
181 def __init__(self, onlyfirst=False):
182 self.startrev = -1
182 self.startrev = -1
183 self.roots = []
183 self.roots = []
184 self.onlyfirst = onlyfirst
184 self.onlyfirst = onlyfirst
185
185
186 def match(self, rev):
186 def match(self, rev):
187 def realparents(rev):
187 def realparents(rev):
188 if self.onlyfirst:
188 if self.onlyfirst:
189 return repo.changelog.parentrevs(rev)[0:1]
189 return repo.changelog.parentrevs(rev)[0:1]
190 else:
190 else:
191 return filter(lambda x: x != -1, repo.changelog.parentrevs(rev))
191 return filter(lambda x: x != -1, repo.changelog.parentrevs(rev))
192
192
193 if self.startrev == -1:
193 if self.startrev == -1:
194 self.startrev = rev
194 self.startrev = rev
195 return True
195 return True
196
196
197 if rev > self.startrev:
197 if rev > self.startrev:
198 # forward: all descendants
198 # forward: all descendants
199 if not self.roots:
199 if not self.roots:
200 self.roots.append(self.startrev)
200 self.roots.append(self.startrev)
201 for parent in realparents(rev):
201 for parent in realparents(rev):
202 if parent in self.roots:
202 if parent in self.roots:
203 self.roots.append(rev)
203 self.roots.append(rev)
204 return True
204 return True
205 else:
205 else:
206 # backwards: all parents
206 # backwards: all parents
207 if not self.roots:
207 if not self.roots:
208 self.roots.extend(realparents(self.startrev))
208 self.roots.extend(realparents(self.startrev))
209 if rev in self.roots:
209 if rev in self.roots:
210 self.roots.remove(rev)
210 self.roots.remove(rev)
211 self.roots.extend(realparents(rev))
211 self.roots.extend(realparents(rev))
212 return True
212 return True
213
213
214 return False
214 return False
215
215
216 # it might be worthwhile to do this in the iterator if the rev range
216 # it might be worthwhile to do this in the iterator if the rev range
217 # is descending and the prune args are all within that range
217 # is descending and the prune args are all within that range
218 for rev in opts.get('prune', ()):
218 for rev in opts.get('prune', ()):
219 rev = repo.changelog.rev(repo.lookup(rev))
219 rev = repo.changelog.rev(repo.lookup(rev))
220 ff = followfilter()
220 ff = followfilter()
221 stop = min(revs[0], revs[-1])
221 stop = min(revs[0], revs[-1])
222 for x in xrange(rev, stop-1, -1):
222 for x in xrange(rev, stop-1, -1):
223 if ff.match(x) and wanted.has_key(x):
223 if ff.match(x) and wanted.has_key(x):
224 del wanted[x]
224 del wanted[x]
225
225
226 def iterate():
226 def iterate():
227 if follow and not files:
227 if follow and not files:
228 ff = followfilter(onlyfirst=opts.get('follow_first'))
228 ff = followfilter(onlyfirst=opts.get('follow_first'))
229 def want(rev):
229 def want(rev):
230 if ff.match(rev) and rev in wanted:
230 if ff.match(rev) and rev in wanted:
231 return True
231 return True
232 return False
232 return False
233 else:
233 else:
234 def want(rev):
234 def want(rev):
235 return rev in wanted
235 return rev in wanted
236
236
237 for i, window in increasing_windows(0, len(revs)):
237 for i, window in increasing_windows(0, len(revs)):
238 yield 'window', revs[0] < revs[-1], revs[-1]
238 yield 'window', revs[0] < revs[-1], revs[-1]
239 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
239 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
240 srevs = list(nrevs)
240 srevs = list(nrevs)
241 srevs.sort()
241 srevs.sort()
242 for rev in srevs:
242 for rev in srevs:
243 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
243 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
244 yield 'add', rev, fns
244 yield 'add', rev, fns
245 for rev in nrevs:
245 for rev in nrevs:
246 yield 'iter', rev, None
246 yield 'iter', rev, None
247 return iterate(), getchange, matchfn
247 return iterate(), getchange, matchfn
248
248
249 def write_bundle(cg, filename=None, compress=True):
249 def write_bundle(cg, filename=None, compress=True):
250 """Write a bundle file and return its filename.
250 """Write a bundle file and return its filename.
251
251
252 Existing files will not be overwritten.
252 Existing files will not be overwritten.
253 If no filename is specified, a temporary file is created.
253 If no filename is specified, a temporary file is created.
254 bz2 compression can be turned off.
254 bz2 compression can be turned off.
255 The bundle file will be deleted in case of errors.
255 The bundle file will be deleted in case of errors.
256 """
256 """
257 class nocompress(object):
257 class nocompress(object):
258 def compress(self, x):
258 def compress(self, x):
259 return x
259 return x
260 def flush(self):
260 def flush(self):
261 return ""
261 return ""
262
262
263 fh = None
263 fh = None
264 cleanup = None
264 cleanup = None
265 try:
265 try:
266 if filename:
266 if filename:
267 if os.path.exists(filename):
267 if os.path.exists(filename):
268 raise util.Abort(_("file '%s' already exists") % filename)
268 raise util.Abort(_("file '%s' already exists") % filename)
269 fh = open(filename, "wb")
269 fh = open(filename, "wb")
270 else:
270 else:
271 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
271 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
272 fh = os.fdopen(fd, "wb")
272 fh = os.fdopen(fd, "wb")
273 cleanup = filename
273 cleanup = filename
274
274
275 if compress:
275 if compress:
276 fh.write("HG10")
276 fh.write("HG10")
277 z = bz2.BZ2Compressor(9)
277 z = bz2.BZ2Compressor(9)
278 else:
278 else:
279 fh.write("HG10UN")
279 fh.write("HG10UN")
280 z = nocompress()
280 z = nocompress()
281 # parse the changegroup data, otherwise we will block
281 # parse the changegroup data, otherwise we will block
282 # in case of sshrepo because we don't know the end of the stream
282 # in case of sshrepo because we don't know the end of the stream
283
283
284 # an empty chunkiter is the end of the changegroup
284 # an empty chunkiter is the end of the changegroup
285 empty = False
285 empty = False
286 while not empty:
286 while not empty:
287 empty = True
287 empty = True
288 for chunk in changegroup.chunkiter(cg):
288 for chunk in changegroup.chunkiter(cg):
289 empty = False
289 empty = False
290 fh.write(z.compress(changegroup.genchunk(chunk)))
290 fh.write(z.compress(changegroup.genchunk(chunk)))
291 fh.write(z.compress(changegroup.closechunk()))
291 fh.write(z.compress(changegroup.closechunk()))
292 fh.write(z.flush())
292 fh.write(z.flush())
293 cleanup = None
293 cleanup = None
294 return filename
294 return filename
295 finally:
295 finally:
296 if fh is not None:
296 if fh is not None:
297 fh.close()
297 fh.close()
298 if cleanup is not None:
298 if cleanup is not None:
299 os.unlink(cleanup)
299 os.unlink(cleanup)
300
300
301 class changeset_printer(object):
301 class changeset_printer(object):
302 '''show changeset information when templating not requested.'''
302 '''show changeset information when templating not requested.'''
303
303
304 def __init__(self, ui, repo):
304 def __init__(self, ui, repo):
305 self.ui = ui
305 self.ui = ui
306 self.repo = repo
306 self.repo = repo
307
307
308 def show(self, rev=0, changenode=None, brinfo=None, copies=None):
308 def show(self, rev=0, changenode=None, brinfo=None, copies=None):
309 '''show a single changeset or file revision'''
309 '''show a single changeset or file revision'''
310 log = self.repo.changelog
310 log = self.repo.changelog
311 if changenode is None:
311 if changenode is None:
312 changenode = log.node(rev)
312 changenode = log.node(rev)
313 elif not rev:
313 elif not rev:
314 rev = log.rev(changenode)
314 rev = log.rev(changenode)
315
315
316 if self.ui.quiet:
316 if self.ui.quiet:
317 self.ui.write("%d:%s\n" % (rev, short(changenode)))
317 self.ui.write("%d:%s\n" % (rev, short(changenode)))
318 return
318 return
319
319
320 changes = log.read(changenode)
320 changes = log.read(changenode)
321 date = util.datestr(changes[2])
321 date = util.datestr(changes[2])
322 extra = changes[5]
322 extra = changes[5]
323 branch = extra.get("branch")
323 branch = extra.get("branch")
324
324
325 hexfunc = self.ui.debugflag and hex or short
325 hexfunc = self.ui.debugflag and hex or short
326
326
327 parents = [(log.rev(p), hexfunc(p)) for p in log.parents(changenode)
327 parents = [(log.rev(p), hexfunc(p)) for p in log.parents(changenode)
328 if self.ui.debugflag or p != nullid]
328 if self.ui.debugflag or p != nullid]
329 if (not self.ui.debugflag and len(parents) == 1 and
329 if (not self.ui.debugflag and len(parents) == 1 and
330 parents[0][0] == rev-1):
330 parents[0][0] == rev-1):
331 parents = []
331 parents = []
332
332
333 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
333 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
334
334
335 if branch:
335 if branch:
336 self.ui.status(_("branch: %s\n") % branch)
336 self.ui.status(_("branch: %s\n") % branch)
337 for tag in self.repo.nodetags(changenode):
337 for tag in self.repo.nodetags(changenode):
338 self.ui.status(_("tag: %s\n") % tag)
338 self.ui.status(_("tag: %s\n") % tag)
339 for parent in parents:
339 for parent in parents:
340 self.ui.write(_("parent: %d:%s\n") % parent)
340 self.ui.write(_("parent: %d:%s\n") % parent)
341
341
342 if brinfo and changenode in brinfo:
342 if brinfo and changenode in brinfo:
343 br = brinfo[changenode]
343 br = brinfo[changenode]
344 self.ui.write(_("branch: %s\n") % " ".join(br))
344 self.ui.write(_("branch: %s\n") % " ".join(br))
345
345
346 self.ui.debug(_("manifest: %d:%s\n") %
346 self.ui.debug(_("manifest: %d:%s\n") %
347 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
347 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
348 self.ui.status(_("user: %s\n") % changes[1])
348 self.ui.status(_("user: %s\n") % changes[1])
349 self.ui.status(_("date: %s\n") % date)
349 self.ui.status(_("date: %s\n") % date)
350
350
351 if self.ui.debugflag:
351 if self.ui.debugflag:
352 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
352 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
353 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
353 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
354 files):
354 files):
355 if value:
355 if value:
356 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
356 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
357 elif changes[3]:
357 elif changes[3]:
358 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
358 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
359 if copies:
359 if copies:
360 copies = ['%s (%s)' % c for c in copies]
360 copies = ['%s (%s)' % c for c in copies]
361 self.ui.note(_("copies: %s\n") % ' '.join(copies))
361 self.ui.note(_("copies: %s\n") % ' '.join(copies))
362
362
363 if extra and self.ui.debugflag:
363 if extra and self.ui.debugflag:
364 extraitems = extra.items()
364 extraitems = extra.items()
365 extraitems.sort()
365 extraitems.sort()
366 for key, value in extraitems:
366 for key, value in extraitems:
367 self.ui.debug(_("extra: %s=%s\n")
367 self.ui.debug(_("extra: %s=%s\n")
368 % (key, value.encode('string_escape')))
368 % (key, value.encode('string_escape')))
369
369
370 description = changes[4].strip()
370 description = changes[4].strip()
371 if description:
371 if description:
372 if self.ui.verbose:
372 if self.ui.verbose:
373 self.ui.status(_("description:\n"))
373 self.ui.status(_("description:\n"))
374 self.ui.status(description)
374 self.ui.status(description)
375 self.ui.status("\n\n")
375 self.ui.status("\n\n")
376 else:
376 else:
377 self.ui.status(_("summary: %s\n") %
377 self.ui.status(_("summary: %s\n") %
378 description.splitlines()[0])
378 description.splitlines()[0])
379 self.ui.status("\n")
379 self.ui.status("\n")
380
380
381 def show_changeset(ui, repo, opts):
381 def show_changeset(ui, repo, opts):
382 """show one changeset using template or regular display.
382 """show one changeset using template or regular display.
383
383
384 Display format will be the first non-empty hit of:
384 Display format will be the first non-empty hit of:
385 1. option 'template'
385 1. option 'template'
386 2. option 'style'
386 2. option 'style'
387 3. [ui] setting 'logtemplate'
387 3. [ui] setting 'logtemplate'
388 4. [ui] setting 'style'
388 4. [ui] setting 'style'
389 If all of these values are either the unset or the empty string,
389 If all of these values are either the unset or the empty string,
390 regular display via changeset_printer() is done.
390 regular display via changeset_printer() is done.
391 """
391 """
392 # options
392 # options
393 tmpl = opts.get('template')
393 tmpl = opts.get('template')
394 mapfile = None
394 mapfile = None
395 if tmpl:
395 if tmpl:
396 tmpl = templater.parsestring(tmpl, quoted=False)
396 tmpl = templater.parsestring(tmpl, quoted=False)
397 else:
397 else:
398 mapfile = opts.get('style')
398 mapfile = opts.get('style')
399 # ui settings
399 # ui settings
400 if not mapfile:
400 if not mapfile:
401 tmpl = ui.config('ui', 'logtemplate')
401 tmpl = ui.config('ui', 'logtemplate')
402 if tmpl:
402 if tmpl:
403 tmpl = templater.parsestring(tmpl)
403 tmpl = templater.parsestring(tmpl)
404 else:
404 else:
405 mapfile = ui.config('ui', 'style')
405 mapfile = ui.config('ui', 'style')
406
406
407 if tmpl or mapfile:
407 if tmpl or mapfile:
408 if mapfile:
408 if mapfile:
409 if not os.path.split(mapfile)[0]:
409 if not os.path.split(mapfile)[0]:
410 mapname = (templater.templatepath('map-cmdline.' + mapfile)
410 mapname = (templater.templatepath('map-cmdline.' + mapfile)
411 or templater.templatepath(mapfile))
411 or templater.templatepath(mapfile))
412 if mapname: mapfile = mapname
412 if mapname: mapfile = mapname
413 try:
413 try:
414 t = templater.changeset_templater(ui, repo, mapfile)
414 t = templater.changeset_templater(ui, repo, mapfile)
415 except SyntaxError, inst:
415 except SyntaxError, inst:
416 raise util.Abort(inst.args[0])
416 raise util.Abort(inst.args[0])
417 if tmpl: t.use_template(tmpl)
417 if tmpl: t.use_template(tmpl)
418 return t
418 return t
419 return changeset_printer(ui, repo)
419 return changeset_printer(ui, repo)
420
420
421 def setremoteconfig(ui, opts):
421 def setremoteconfig(ui, opts):
422 "copy remote options to ui tree"
422 "copy remote options to ui tree"
423 if opts.get('ssh'):
423 if opts.get('ssh'):
424 ui.setconfig("ui", "ssh", opts['ssh'])
424 ui.setconfig("ui", "ssh", opts['ssh'])
425 if opts.get('remotecmd'):
425 if opts.get('remotecmd'):
426 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
426 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
427
427
428 def show_version(ui):
428 def show_version(ui):
429 """output version and copyright information"""
429 """output version and copyright information"""
430 ui.write(_("Mercurial Distributed SCM (version %s)\n")
430 ui.write(_("Mercurial Distributed SCM (version %s)\n")
431 % version.get_version())
431 % version.get_version())
432 ui.status(_(
432 ui.status(_(
433 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
433 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
434 "This is free software; see the source for copying conditions. "
434 "This is free software; see the source for copying conditions. "
435 "There is NO\nwarranty; "
435 "There is NO\nwarranty; "
436 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
436 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
437 ))
437 ))
438
438
439 def help_(ui, name=None, with_version=False):
439 def help_(ui, name=None, with_version=False):
440 """show help for a command, extension, or list of commands
440 """show help for a command, extension, or list of commands
441
441
442 With no arguments, print a list of commands and short help.
442 With no arguments, print a list of commands and short help.
443
443
444 Given a command name, print help for that command.
444 Given a command name, print help for that command.
445
445
446 Given an extension name, print help for that extension, and the
446 Given an extension name, print help for that extension, and the
447 commands it provides."""
447 commands it provides."""
448 option_lists = []
448 option_lists = []
449
449
450 def helpcmd(name):
450 def helpcmd(name):
451 if with_version:
451 if with_version:
452 show_version(ui)
452 show_version(ui)
453 ui.write('\n')
453 ui.write('\n')
454 aliases, i = findcmd(ui, name)
454 aliases, i = findcmd(ui, name)
455 # synopsis
455 # synopsis
456 ui.write("%s\n\n" % i[2])
456 ui.write("%s\n\n" % i[2])
457
457
458 # description
458 # description
459 doc = i[0].__doc__
459 doc = i[0].__doc__
460 if not doc:
460 if not doc:
461 doc = _("(No help text available)")
461 doc = _("(No help text available)")
462 if ui.quiet:
462 if ui.quiet:
463 doc = doc.splitlines(0)[0]
463 doc = doc.splitlines(0)[0]
464 ui.write("%s\n" % doc.rstrip())
464 ui.write("%s\n" % doc.rstrip())
465
465
466 if not ui.quiet:
466 if not ui.quiet:
467 # aliases
467 # aliases
468 if len(aliases) > 1:
468 if len(aliases) > 1:
469 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
469 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
470
470
471 # options
471 # options
472 if i[1]:
472 if i[1]:
473 option_lists.append(("options", i[1]))
473 option_lists.append(("options", i[1]))
474
474
475 def helplist(select=None):
475 def helplist(select=None):
476 h = {}
476 h = {}
477 cmds = {}
477 cmds = {}
478 for c, e in table.items():
478 for c, e in table.items():
479 f = c.split("|", 1)[0]
479 f = c.split("|", 1)[0]
480 if select and not select(f):
480 if select and not select(f):
481 continue
481 continue
482 if name == "shortlist" and not f.startswith("^"):
482 if name == "shortlist" and not f.startswith("^"):
483 continue
483 continue
484 f = f.lstrip("^")
484 f = f.lstrip("^")
485 if not ui.debugflag and f.startswith("debug"):
485 if not ui.debugflag and f.startswith("debug"):
486 continue
486 continue
487 doc = e[0].__doc__
487 doc = e[0].__doc__
488 if not doc:
488 if not doc:
489 doc = _("(No help text available)")
489 doc = _("(No help text available)")
490 h[f] = doc.splitlines(0)[0].rstrip()
490 h[f] = doc.splitlines(0)[0].rstrip()
491 cmds[f] = c.lstrip("^")
491 cmds[f] = c.lstrip("^")
492
492
493 fns = h.keys()
493 fns = h.keys()
494 fns.sort()
494 fns.sort()
495 m = max(map(len, fns))
495 m = max(map(len, fns))
496 for f in fns:
496 for f in fns:
497 if ui.verbose:
497 if ui.verbose:
498 commands = cmds[f].replace("|",", ")
498 commands = cmds[f].replace("|",", ")
499 ui.write(" %s:\n %s\n"%(commands, h[f]))
499 ui.write(" %s:\n %s\n"%(commands, h[f]))
500 else:
500 else:
501 ui.write(' %-*s %s\n' % (m, f, h[f]))
501 ui.write(' %-*s %s\n' % (m, f, h[f]))
502
502
503 def helpext(name):
503 def helpext(name):
504 try:
504 try:
505 mod = findext(name)
505 mod = findext(name)
506 except KeyError:
506 except KeyError:
507 raise UnknownCommand(name)
507 raise UnknownCommand(name)
508
508
509 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
509 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
510 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
510 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
511 for d in doc[1:]:
511 for d in doc[1:]:
512 ui.write(d, '\n')
512 ui.write(d, '\n')
513
513
514 ui.status('\n')
514 ui.status('\n')
515 if ui.verbose:
515 if ui.verbose:
516 ui.status(_('list of commands:\n\n'))
516 ui.status(_('list of commands:\n\n'))
517 else:
517 else:
518 ui.status(_('list of commands (use "hg help -v %s" '
518 ui.status(_('list of commands (use "hg help -v %s" '
519 'to show aliases and global options):\n\n') % name)
519 'to show aliases and global options):\n\n') % name)
520
520
521 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
521 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
522 helplist(modcmds.has_key)
522 helplist(modcmds.has_key)
523
523
524 if name and name != 'shortlist':
524 if name and name != 'shortlist':
525 try:
525 try:
526 helpcmd(name)
526 helpcmd(name)
527 except UnknownCommand:
527 except UnknownCommand:
528 helpext(name)
528 helpext(name)
529
529
530 else:
530 else:
531 # program name
531 # program name
532 if ui.verbose or with_version:
532 if ui.verbose or with_version:
533 show_version(ui)
533 show_version(ui)
534 else:
534 else:
535 ui.status(_("Mercurial Distributed SCM\n"))
535 ui.status(_("Mercurial Distributed SCM\n"))
536 ui.status('\n')
536 ui.status('\n')
537
537
538 # list of commands
538 # list of commands
539 if name == "shortlist":
539 if name == "shortlist":
540 ui.status(_('basic commands (use "hg help" '
540 ui.status(_('basic commands (use "hg help" '
541 'for the full list or option "-v" for details):\n\n'))
541 'for the full list or option "-v" for details):\n\n'))
542 elif ui.verbose:
542 elif ui.verbose:
543 ui.status(_('list of commands:\n\n'))
543 ui.status(_('list of commands:\n\n'))
544 else:
544 else:
545 ui.status(_('list of commands (use "hg help -v" '
545 ui.status(_('list of commands (use "hg help -v" '
546 'to show aliases and global options):\n\n'))
546 'to show aliases and global options):\n\n'))
547
547
548 helplist()
548 helplist()
549
549
550 # global options
550 # global options
551 if ui.verbose:
551 if ui.verbose:
552 option_lists.append(("global options", globalopts))
552 option_lists.append(("global options", globalopts))
553
553
554 # list all option lists
554 # list all option lists
555 opt_output = []
555 opt_output = []
556 for title, options in option_lists:
556 for title, options in option_lists:
557 opt_output.append(("\n%s:\n" % title, None))
557 opt_output.append(("\n%s:\n" % title, None))
558 for shortopt, longopt, default, desc in options:
558 for shortopt, longopt, default, desc in options:
559 if "DEPRECATED" in desc and not ui.verbose: continue
559 if "DEPRECATED" in desc and not ui.verbose: continue
560 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
560 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
561 longopt and " --%s" % longopt),
561 longopt and " --%s" % longopt),
562 "%s%s" % (desc,
562 "%s%s" % (desc,
563 default
563 default
564 and _(" (default: %s)") % default
564 and _(" (default: %s)") % default
565 or "")))
565 or "")))
566
566
567 if opt_output:
567 if opt_output:
568 opts_len = max([len(line[0]) for line in opt_output if line[1]])
568 opts_len = max([len(line[0]) for line in opt_output if line[1]])
569 for first, second in opt_output:
569 for first, second in opt_output:
570 if second:
570 if second:
571 ui.write(" %-*s %s\n" % (opts_len, first, second))
571 ui.write(" %-*s %s\n" % (opts_len, first, second))
572 else:
572 else:
573 ui.write("%s\n" % first)
573 ui.write("%s\n" % first)
574
574
575 # Commands start here, listed alphabetically
575 # Commands start here, listed alphabetically
576
576
577 def add(ui, repo, *pats, **opts):
577 def add(ui, repo, *pats, **opts):
578 """add the specified files on the next commit
578 """add the specified files on the next commit
579
579
580 Schedule files to be version controlled and added to the repository.
580 Schedule files to be version controlled and added to the repository.
581
581
582 The files will be added to the repository at the next commit.
582 The files will be added to the repository at the next commit.
583
583
584 If no names are given, add all files in the repository.
584 If no names are given, add all files in the repository.
585 """
585 """
586
586
587 names = []
587 names = []
588 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
588 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
589 if exact:
589 if exact:
590 if ui.verbose:
590 if ui.verbose:
591 ui.status(_('adding %s\n') % rel)
591 ui.status(_('adding %s\n') % rel)
592 names.append(abs)
592 names.append(abs)
593 elif repo.dirstate.state(abs) == '?':
593 elif repo.dirstate.state(abs) == '?':
594 ui.status(_('adding %s\n') % rel)
594 ui.status(_('adding %s\n') % rel)
595 names.append(abs)
595 names.append(abs)
596 if not opts.get('dry_run'):
596 if not opts.get('dry_run'):
597 repo.add(names)
597 repo.add(names)
598
598
599 def addremove(ui, repo, *pats, **opts):
599 def addremove(ui, repo, *pats, **opts):
600 """add all new files, delete all missing files
600 """add all new files, delete all missing files
601
601
602 Add all new files and remove all missing files from the repository.
602 Add all new files and remove all missing files from the repository.
603
603
604 New files are ignored if they match any of the patterns in .hgignore. As
604 New files are ignored if they match any of the patterns in .hgignore. As
605 with add, these changes take effect at the next commit.
605 with add, these changes take effect at the next commit.
606
606
607 Use the -s option to detect renamed files. With a parameter > 0,
607 Use the -s option to detect renamed files. With a parameter > 0,
608 this compares every removed file with every added file and records
608 this compares every removed file with every added file and records
609 those similar enough as renames. This option takes a percentage
609 those similar enough as renames. This option takes a percentage
610 between 0 (disabled) and 100 (files must be identical) as its
610 between 0 (disabled) and 100 (files must be identical) as its
611 parameter. Detecting renamed files this way can be expensive.
611 parameter. Detecting renamed files this way can be expensive.
612 """
612 """
613 sim = float(opts.get('similarity') or 0)
613 sim = float(opts.get('similarity') or 0)
614 if sim < 0 or sim > 100:
614 if sim < 0 or sim > 100:
615 raise util.Abort(_('similarity must be between 0 and 100'))
615 raise util.Abort(_('similarity must be between 0 and 100'))
616 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
616 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
617
617
618 def annotate(ui, repo, *pats, **opts):
618 def annotate(ui, repo, *pats, **opts):
619 """show changeset information per file line
619 """show changeset information per file line
620
620
621 List changes in files, showing the revision id responsible for each line
621 List changes in files, showing the revision id responsible for each line
622
622
623 This command is useful to discover who did a change or when a change took
623 This command is useful to discover who did a change or when a change took
624 place.
624 place.
625
625
626 Without the -a option, annotate will avoid processing files it
626 Without the -a option, annotate will avoid processing files it
627 detects as binary. With -a, annotate will generate an annotation
627 detects as binary. With -a, annotate will generate an annotation
628 anyway, probably with undesirable results.
628 anyway, probably with undesirable results.
629 """
629 """
630 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
630 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
631
631
632 if not pats:
632 if not pats:
633 raise util.Abort(_('at least one file name or pattern required'))
633 raise util.Abort(_('at least one file name or pattern required'))
634
634
635 opmap = [['user', lambda x: ui.shortuser(x.user())],
635 opmap = [['user', lambda x: ui.shortuser(x.user())],
636 ['number', lambda x: str(x.rev())],
636 ['number', lambda x: str(x.rev())],
637 ['changeset', lambda x: short(x.node())],
637 ['changeset', lambda x: short(x.node())],
638 ['date', getdate], ['follow', lambda x: x.path()]]
638 ['date', getdate], ['follow', lambda x: x.path()]]
639 if (not opts['user'] and not opts['changeset'] and not opts['date']
639 if (not opts['user'] and not opts['changeset'] and not opts['date']
640 and not opts['follow']):
640 and not opts['follow']):
641 opts['number'] = 1
641 opts['number'] = 1
642
642
643 ctx = repo.changectx(opts['rev'])
643 ctx = repo.changectx(opts['rev'])
644
644
645 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
645 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
646 node=ctx.node()):
646 node=ctx.node()):
647 fctx = ctx.filectx(abs)
647 fctx = ctx.filectx(abs)
648 if not opts['text'] and util.binary(fctx.data()):
648 if not opts['text'] and util.binary(fctx.data()):
649 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
649 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
650 continue
650 continue
651
651
652 lines = fctx.annotate(follow=opts.get('follow'))
652 lines = fctx.annotate(follow=opts.get('follow'))
653 pieces = []
653 pieces = []
654
654
655 for o, f in opmap:
655 for o, f in opmap:
656 if opts[o]:
656 if opts[o]:
657 l = [f(n) for n, dummy in lines]
657 l = [f(n) for n, dummy in lines]
658 if l:
658 if l:
659 m = max(map(len, l))
659 m = max(map(len, l))
660 pieces.append(["%*s" % (m, x) for x in l])
660 pieces.append(["%*s" % (m, x) for x in l])
661
661
662 if pieces:
662 if pieces:
663 for p, l in zip(zip(*pieces), lines):
663 for p, l in zip(zip(*pieces), lines):
664 ui.write("%s: %s" % (" ".join(p), l[1]))
664 ui.write("%s: %s" % (" ".join(p), l[1]))
665
665
666 def archive(ui, repo, dest, **opts):
666 def archive(ui, repo, dest, **opts):
667 '''create unversioned archive of a repository revision
667 '''create unversioned archive of a repository revision
668
668
669 By default, the revision used is the parent of the working
669 By default, the revision used is the parent of the working
670 directory; use "-r" to specify a different revision.
670 directory; use "-r" to specify a different revision.
671
671
672 To specify the type of archive to create, use "-t". Valid
672 To specify the type of archive to create, use "-t". Valid
673 types are:
673 types are:
674
674
675 "files" (default): a directory full of files
675 "files" (default): a directory full of files
676 "tar": tar archive, uncompressed
676 "tar": tar archive, uncompressed
677 "tbz2": tar archive, compressed using bzip2
677 "tbz2": tar archive, compressed using bzip2
678 "tgz": tar archive, compressed using gzip
678 "tgz": tar archive, compressed using gzip
679 "uzip": zip archive, uncompressed
679 "uzip": zip archive, uncompressed
680 "zip": zip archive, compressed using deflate
680 "zip": zip archive, compressed using deflate
681
681
682 The exact name of the destination archive or directory is given
682 The exact name of the destination archive or directory is given
683 using a format string; see "hg help export" for details.
683 using a format string; see "hg help export" for details.
684
684
685 Each member added to an archive file has a directory prefix
685 Each member added to an archive file has a directory prefix
686 prepended. Use "-p" to specify a format string for the prefix.
686 prepended. Use "-p" to specify a format string for the prefix.
687 The default is the basename of the archive, with suffixes removed.
687 The default is the basename of the archive, with suffixes removed.
688 '''
688 '''
689
689
690 node = repo.changectx(opts['rev']).node()
690 node = repo.changectx(opts['rev']).node()
691 dest = cmdutil.make_filename(repo, dest, node)
691 dest = cmdutil.make_filename(repo, dest, node)
692 if os.path.realpath(dest) == repo.root:
692 if os.path.realpath(dest) == repo.root:
693 raise util.Abort(_('repository root cannot be destination'))
693 raise util.Abort(_('repository root cannot be destination'))
694 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
694 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
695 kind = opts.get('type') or 'files'
695 kind = opts.get('type') or 'files'
696 prefix = opts['prefix']
696 prefix = opts['prefix']
697 if dest == '-':
697 if dest == '-':
698 if kind == 'files':
698 if kind == 'files':
699 raise util.Abort(_('cannot archive plain files to stdout'))
699 raise util.Abort(_('cannot archive plain files to stdout'))
700 dest = sys.stdout
700 dest = sys.stdout
701 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
701 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
702 prefix = cmdutil.make_filename(repo, prefix, node)
702 prefix = cmdutil.make_filename(repo, prefix, node)
703 archival.archive(repo, dest, node, kind, not opts['no_decode'],
703 archival.archive(repo, dest, node, kind, not opts['no_decode'],
704 matchfn, prefix)
704 matchfn, prefix)
705
705
706 def backout(ui, repo, rev, **opts):
706 def backout(ui, repo, rev, **opts):
707 '''reverse effect of earlier changeset
707 '''reverse effect of earlier changeset
708
708
709 Commit the backed out changes as a new changeset. The new
709 Commit the backed out changes as a new changeset. The new
710 changeset is a child of the backed out changeset.
710 changeset is a child of the backed out changeset.
711
711
712 If you back out a changeset other than the tip, a new head is
712 If you back out a changeset other than the tip, a new head is
713 created. This head is the parent of the working directory. If
713 created. This head is the parent of the working directory. If
714 you back out an old changeset, your working directory will appear
714 you back out an old changeset, your working directory will appear
715 old after the backout. You should merge the backout changeset
715 old after the backout. You should merge the backout changeset
716 with another head.
716 with another head.
717
717
718 The --merge option remembers the parent of the working directory
718 The --merge option remembers the parent of the working directory
719 before starting the backout, then merges the new head with that
719 before starting the backout, then merges the new head with that
720 changeset afterwards. This saves you from doing the merge by
720 changeset afterwards. This saves you from doing the merge by
721 hand. The result of this merge is not committed, as for a normal
721 hand. The result of this merge is not committed, as for a normal
722 merge.'''
722 merge.'''
723
723
724 bail_if_changed(repo)
724 bail_if_changed(repo)
725 op1, op2 = repo.dirstate.parents()
725 op1, op2 = repo.dirstate.parents()
726 if op2 != nullid:
726 if op2 != nullid:
727 raise util.Abort(_('outstanding uncommitted merge'))
727 raise util.Abort(_('outstanding uncommitted merge'))
728 node = repo.lookup(rev)
728 node = repo.lookup(rev)
729 p1, p2 = repo.changelog.parents(node)
729 p1, p2 = repo.changelog.parents(node)
730 if p1 == nullid:
730 if p1 == nullid:
731 raise util.Abort(_('cannot back out a change with no parents'))
731 raise util.Abort(_('cannot back out a change with no parents'))
732 if p2 != nullid:
732 if p2 != nullid:
733 if not opts['parent']:
733 if not opts['parent']:
734 raise util.Abort(_('cannot back out a merge changeset without '
734 raise util.Abort(_('cannot back out a merge changeset without '
735 '--parent'))
735 '--parent'))
736 p = repo.lookup(opts['parent'])
736 p = repo.lookup(opts['parent'])
737 if p not in (p1, p2):
737 if p not in (p1, p2):
738 raise util.Abort(_('%s is not a parent of %s' %
738 raise util.Abort(_('%s is not a parent of %s' %
739 (short(p), short(node))))
739 (short(p), short(node))))
740 parent = p
740 parent = p
741 else:
741 else:
742 if opts['parent']:
742 if opts['parent']:
743 raise util.Abort(_('cannot use --parent on non-merge changeset'))
743 raise util.Abort(_('cannot use --parent on non-merge changeset'))
744 parent = p1
744 parent = p1
745 hg.clean(repo, node, show_stats=False)
745 hg.clean(repo, node, show_stats=False)
746 revert_opts = opts.copy()
746 revert_opts = opts.copy()
747 revert_opts['all'] = True
747 revert_opts['all'] = True
748 revert_opts['rev'] = hex(parent)
748 revert_opts['rev'] = hex(parent)
749 revert(ui, repo, **revert_opts)
749 revert(ui, repo, **revert_opts)
750 commit_opts = opts.copy()
750 commit_opts = opts.copy()
751 commit_opts['addremove'] = False
751 commit_opts['addremove'] = False
752 if not commit_opts['message'] and not commit_opts['logfile']:
752 if not commit_opts['message'] and not commit_opts['logfile']:
753 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
753 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
754 commit_opts['force_editor'] = True
754 commit_opts['force_editor'] = True
755 commit(ui, repo, **commit_opts)
755 commit(ui, repo, **commit_opts)
756 def nice(node):
756 def nice(node):
757 return '%d:%s' % (repo.changelog.rev(node), short(node))
757 return '%d:%s' % (repo.changelog.rev(node), short(node))
758 ui.status(_('changeset %s backs out changeset %s\n') %
758 ui.status(_('changeset %s backs out changeset %s\n') %
759 (nice(repo.changelog.tip()), nice(node)))
759 (nice(repo.changelog.tip()), nice(node)))
760 if op1 != node:
760 if op1 != node:
761 if opts['merge']:
761 if opts['merge']:
762 ui.status(_('merging with changeset %s\n') % nice(op1))
762 ui.status(_('merging with changeset %s\n') % nice(op1))
763 n = _lookup(repo, hex(op1))
763 n = _lookup(repo, hex(op1))
764 hg.merge(repo, n)
764 hg.merge(repo, n)
765 else:
765 else:
766 ui.status(_('the backout changeset is a new head - '
766 ui.status(_('the backout changeset is a new head - '
767 'do not forget to merge\n'))
767 'do not forget to merge\n'))
768 ui.status(_('(use "backout --merge" '
768 ui.status(_('(use "backout --merge" '
769 'if you want to auto-merge)\n'))
769 'if you want to auto-merge)\n'))
770
770
771 def branch(ui, repo, label=None):
771 def branch(ui, repo, label=None):
772 """set or show the current branch name
772 """set or show the current branch name
773
773
774 With <name>, set the current branch name. Otherwise, show the
774 With <name>, set the current branch name. Otherwise, show the
775 current branch name.
775 current branch name.
776 """
776 """
777
777
778 if label is not None:
778 if label is not None:
779 repo.opener("branch", "w").write(label)
779 repo.opener("branch", "w").write(label)
780 else:
780 else:
781 b = repo.workingctx().branch()
781 b = repo.workingctx().branch()
782 if b:
782 if b:
783 ui.write("%s\n" % b)
783 ui.write("%s\n" % b)
784
784
785 def branches(ui, repo):
785 def branches(ui, repo):
786 """list repository named branches
786 """list repository named branches
787
787
788 List the repository's named branches.
788 List the repository's named branches.
789 """
789 """
790 b = repo.branchtags()
790 b = repo.branchtags()
791 l = [(-repo.changelog.rev(n), n, t) for t,n in b.items()]
791 l = [(-repo.changelog.rev(n), n, t) for t,n in b.items()]
792 l.sort()
792 l.sort()
793 for r, n, t in l:
793 for r, n, t in l:
794 hexfunc = ui.debugflag and hex or short
794 hexfunc = ui.debugflag and hex or short
795 if ui.quiet:
795 if ui.quiet:
796 ui.write("%s\n" % t)
796 ui.write("%s\n" % t)
797 else:
797 else:
798 ui.write("%-30s %s:%s\n" % (t, -r, hexfunc(n)))
798 ui.write("%-30s %s:%s\n" % (t, -r, hexfunc(n)))
799
799
800 def bundle(ui, repo, fname, dest=None, **opts):
800 def bundle(ui, repo, fname, dest=None, **opts):
801 """create a changegroup file
801 """create a changegroup file
802
802
803 Generate a compressed changegroup file collecting changesets not
803 Generate a compressed changegroup file collecting changesets not
804 found in the other repository.
804 found in the other repository.
805
805
806 If no destination repository is specified the destination is assumed
806 If no destination repository is specified the destination is assumed
807 to have all the nodes specified by one or more --base parameters.
807 to have all the nodes specified by one or more --base parameters.
808
808
809 The bundle file can then be transferred using conventional means and
809 The bundle file can then be transferred using conventional means and
810 applied to another repository with the unbundle or pull command.
810 applied to another repository with the unbundle or pull command.
811 This is useful when direct push and pull are not available or when
811 This is useful when direct push and pull are not available or when
812 exporting an entire repository is undesirable.
812 exporting an entire repository is undesirable.
813
813
814 Applying bundles preserves all changeset contents including
814 Applying bundles preserves all changeset contents including
815 permissions, copy/rename information, and revision history.
815 permissions, copy/rename information, and revision history.
816 """
816 """
817 revs = opts.get('rev') or None
817 revs = opts.get('rev') or None
818 if revs:
818 if revs:
819 revs = [repo.lookup(rev) for rev in revs]
819 revs = [repo.lookup(rev) for rev in revs]
820 base = opts.get('base')
820 base = opts.get('base')
821 if base:
821 if base:
822 if dest:
822 if dest:
823 raise util.Abort(_("--base is incompatible with specifiying "
823 raise util.Abort(_("--base is incompatible with specifiying "
824 "a destination"))
824 "a destination"))
825 base = [repo.lookup(rev) for rev in base]
825 base = [repo.lookup(rev) for rev in base]
826 # create the right base
826 # create the right base
827 # XXX: nodesbetween / changegroup* should be "fixed" instead
827 # XXX: nodesbetween / changegroup* should be "fixed" instead
828 o = []
828 o = []
829 has_set = sets.Set(base)
829 has_set = sets.Set(base)
830 for n in base:
830 for n in base:
831 has_set.update(repo.changelog.reachable(n))
831 has_set.update(repo.changelog.reachable(n))
832 if revs:
832 if revs:
833 visit = list(revs)
833 visit = list(revs)
834 else:
834 else:
835 visit = repo.changelog.heads()
835 visit = repo.changelog.heads()
836 seen = sets.Set(visit)
836 seen = sets.Set(visit)
837 while visit:
837 while visit:
838 n = visit.pop(0)
838 n = visit.pop(0)
839 parents = [p for p in repo.changelog.parents(n)
839 parents = [p for p in repo.changelog.parents(n)
840 if p != nullid and p not in has_set]
840 if p != nullid and p not in has_set]
841 if len(parents) == 0:
841 if len(parents) == 0:
842 o.insert(0, n)
842 o.insert(0, n)
843 else:
843 else:
844 for p in parents:
844 for p in parents:
845 if p not in seen:
845 if p not in seen:
846 seen.add(p)
846 seen.add(p)
847 visit.append(p)
847 visit.append(p)
848 else:
848 else:
849 setremoteconfig(ui, opts)
849 setremoteconfig(ui, opts)
850 dest = ui.expandpath(dest or 'default-push', dest or 'default')
850 dest = ui.expandpath(dest or 'default-push', dest or 'default')
851 other = hg.repository(ui, dest)
851 other = hg.repository(ui, dest)
852 o = repo.findoutgoing(other, force=opts['force'])
852 o = repo.findoutgoing(other, force=opts['force'])
853
853
854 if revs:
854 if revs:
855 cg = repo.changegroupsubset(o, revs, 'bundle')
855 cg = repo.changegroupsubset(o, revs, 'bundle')
856 else:
856 else:
857 cg = repo.changegroup(o, 'bundle')
857 cg = repo.changegroup(o, 'bundle')
858 write_bundle(cg, fname)
858 write_bundle(cg, fname)
859
859
860 def cat(ui, repo, file1, *pats, **opts):
860 def cat(ui, repo, file1, *pats, **opts):
861 """output the latest or given revisions of files
861 """output the latest or given revisions of files
862
862
863 Print the specified files as they were at the given revision.
863 Print the specified files as they were at the given revision.
864 If no revision is given then working dir parent is used, or tip
864 If no revision is given then working dir parent is used, or tip
865 if no revision is checked out.
865 if no revision is checked out.
866
866
867 Output may be to a file, in which case the name of the file is
867 Output may be to a file, in which case the name of the file is
868 given using a format string. The formatting rules are the same as
868 given using a format string. The formatting rules are the same as
869 for the export command, with the following additions:
869 for the export command, with the following additions:
870
870
871 %s basename of file being printed
871 %s basename of file being printed
872 %d dirname of file being printed, or '.' if in repo root
872 %d dirname of file being printed, or '.' if in repo root
873 %p root-relative path name of file being printed
873 %p root-relative path name of file being printed
874 """
874 """
875 ctx = repo.changectx(opts['rev'])
875 ctx = repo.changectx(opts['rev'])
876 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
876 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
877 ctx.node()):
877 ctx.node()):
878 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
878 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
879 fp.write(ctx.filectx(abs).data())
879 fp.write(ctx.filectx(abs).data())
880
880
881 def clone(ui, source, dest=None, **opts):
881 def clone(ui, source, dest=None, **opts):
882 """make a copy of an existing repository
882 """make a copy of an existing repository
883
883
884 Create a copy of an existing repository in a new directory.
884 Create a copy of an existing repository in a new directory.
885
885
886 If no destination directory name is specified, it defaults to the
886 If no destination directory name is specified, it defaults to the
887 basename of the source.
887 basename of the source.
888
888
889 The location of the source is added to the new repository's
889 The location of the source is added to the new repository's
890 .hg/hgrc file, as the default to be used for future pulls.
890 .hg/hgrc file, as the default to be used for future pulls.
891
891
892 For efficiency, hardlinks are used for cloning whenever the source
892 For efficiency, hardlinks are used for cloning whenever the source
893 and destination are on the same filesystem (note this applies only
893 and destination are on the same filesystem (note this applies only
894 to the repository data, not to the checked out files). Some
894 to the repository data, not to the checked out files). Some
895 filesystems, such as AFS, implement hardlinking incorrectly, but
895 filesystems, such as AFS, implement hardlinking incorrectly, but
896 do not report errors. In these cases, use the --pull option to
896 do not report errors. In these cases, use the --pull option to
897 avoid hardlinking.
897 avoid hardlinking.
898
898
899 You can safely clone repositories and checked out files using full
899 You can safely clone repositories and checked out files using full
900 hardlinks with
900 hardlinks with
901
901
902 $ cp -al REPO REPOCLONE
902 $ cp -al REPO REPOCLONE
903
903
904 which is the fastest way to clone. However, the operation is not
904 which is the fastest way to clone. However, the operation is not
905 atomic (making sure REPO is not modified during the operation is
905 atomic (making sure REPO is not modified during the operation is
906 up to you) and you have to make sure your editor breaks hardlinks
906 up to you) and you have to make sure your editor breaks hardlinks
907 (Emacs and most Linux Kernel tools do so).
907 (Emacs and most Linux Kernel tools do so).
908
908
909 If you use the -r option to clone up to a specific revision, no
909 If you use the -r option to clone up to a specific revision, no
910 subsequent revisions will be present in the cloned repository.
910 subsequent revisions will be present in the cloned repository.
911 This option implies --pull, even on local repositories.
911 This option implies --pull, even on local repositories.
912
912
913 See pull for valid source format details.
913 See pull for valid source format details.
914
914
915 It is possible to specify an ssh:// URL as the destination, but no
915 It is possible to specify an ssh:// URL as the destination, but no
916 .hg/hgrc will be created on the remote side. Look at the help text
916 .hg/hgrc will be created on the remote side. Look at the help text
917 for the pull command for important details about ssh:// URLs.
917 for the pull command for important details about ssh:// URLs.
918 """
918 """
919 setremoteconfig(ui, opts)
919 setremoteconfig(ui, opts)
920 hg.clone(ui, ui.expandpath(source), dest,
920 hg.clone(ui, ui.expandpath(source), dest,
921 pull=opts['pull'],
921 pull=opts['pull'],
922 stream=opts['uncompressed'],
922 stream=opts['uncompressed'],
923 rev=opts['rev'],
923 rev=opts['rev'],
924 update=not opts['noupdate'])
924 update=not opts['noupdate'])
925
925
926 def commit(ui, repo, *pats, **opts):
926 def commit(ui, repo, *pats, **opts):
927 """commit the specified files or all outstanding changes
927 """commit the specified files or all outstanding changes
928
928
929 Commit changes to the given files into the repository.
929 Commit changes to the given files into the repository.
930
930
931 If a list of files is omitted, all changes reported by "hg status"
931 If a list of files is omitted, all changes reported by "hg status"
932 will be committed.
932 will be committed.
933
933
934 If no commit message is specified, the editor configured in your hgrc
934 If no commit message is specified, the editor configured in your hgrc
935 or in the EDITOR environment variable is started to enter a message.
935 or in the EDITOR environment variable is started to enter a message.
936 """
936 """
937 message = logmessage(opts)
937 message = logmessage(opts)
938
938
939 if opts['addremove']:
939 if opts['addremove']:
940 cmdutil.addremove(repo, pats, opts)
940 cmdutil.addremove(repo, pats, opts)
941 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
941 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
942 if pats:
942 if pats:
943 modified, added, removed = repo.status(files=fns, match=match)[:3]
943 modified, added, removed = repo.status(files=fns, match=match)[:3]
944 files = modified + added + removed
944 files = modified + added + removed
945 else:
945 else:
946 files = []
946 files = []
947 try:
947 try:
948 repo.commit(files, message, opts['user'], opts['date'], match,
948 repo.commit(files, message, opts['user'], opts['date'], match,
949 force_editor=opts.get('force_editor'))
949 force_editor=opts.get('force_editor'))
950 except ValueError, inst:
950 except ValueError, inst:
951 raise util.Abort(str(inst))
951 raise util.Abort(str(inst))
952
952
953 def docopy(ui, repo, pats, opts, wlock):
953 def docopy(ui, repo, pats, opts, wlock):
954 # called with the repo lock held
954 # called with the repo lock held
955 cwd = repo.getcwd()
955 cwd = repo.getcwd()
956 errors = 0
956 errors = 0
957 copied = []
957 copied = []
958 targets = {}
958 targets = {}
959
959
960 def okaytocopy(abs, rel, exact):
960 def okaytocopy(abs, rel, exact):
961 reasons = {'?': _('is not managed'),
961 reasons = {'?': _('is not managed'),
962 'a': _('has been marked for add'),
962 'a': _('has been marked for add'),
963 'r': _('has been marked for remove')}
963 'r': _('has been marked for remove')}
964 state = repo.dirstate.state(abs)
964 state = repo.dirstate.state(abs)
965 reason = reasons.get(state)
965 reason = reasons.get(state)
966 if reason:
966 if reason:
967 if state == 'a':
967 if state == 'a':
968 origsrc = repo.dirstate.copied(abs)
968 origsrc = repo.dirstate.copied(abs)
969 if origsrc is not None:
969 if origsrc is not None:
970 return origsrc
970 return origsrc
971 if exact:
971 if exact:
972 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
972 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
973 else:
973 else:
974 return abs
974 return abs
975
975
976 def copy(origsrc, abssrc, relsrc, target, exact):
976 def copy(origsrc, abssrc, relsrc, target, exact):
977 abstarget = util.canonpath(repo.root, cwd, target)
977 abstarget = util.canonpath(repo.root, cwd, target)
978 reltarget = util.pathto(cwd, abstarget)
978 reltarget = util.pathto(cwd, abstarget)
979 prevsrc = targets.get(abstarget)
979 prevsrc = targets.get(abstarget)
980 if prevsrc is not None:
980 if prevsrc is not None:
981 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
981 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
982 (reltarget, abssrc, prevsrc))
982 (reltarget, abssrc, prevsrc))
983 return
983 return
984 if (not opts['after'] and os.path.exists(reltarget) or
984 if (not opts['after'] and os.path.exists(reltarget) or
985 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
985 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
986 if not opts['force']:
986 if not opts['force']:
987 ui.warn(_('%s: not overwriting - file exists\n') %
987 ui.warn(_('%s: not overwriting - file exists\n') %
988 reltarget)
988 reltarget)
989 return
989 return
990 if not opts['after'] and not opts.get('dry_run'):
990 if not opts['after'] and not opts.get('dry_run'):
991 os.unlink(reltarget)
991 os.unlink(reltarget)
992 if opts['after']:
992 if opts['after']:
993 if not os.path.exists(reltarget):
993 if not os.path.exists(reltarget):
994 return
994 return
995 else:
995 else:
996 targetdir = os.path.dirname(reltarget) or '.'
996 targetdir = os.path.dirname(reltarget) or '.'
997 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
997 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
998 os.makedirs(targetdir)
998 os.makedirs(targetdir)
999 try:
999 try:
1000 restore = repo.dirstate.state(abstarget) == 'r'
1000 restore = repo.dirstate.state(abstarget) == 'r'
1001 if restore and not opts.get('dry_run'):
1001 if restore and not opts.get('dry_run'):
1002 repo.undelete([abstarget], wlock)
1002 repo.undelete([abstarget], wlock)
1003 try:
1003 try:
1004 if not opts.get('dry_run'):
1004 if not opts.get('dry_run'):
1005 shutil.copyfile(relsrc, reltarget)
1005 shutil.copyfile(relsrc, reltarget)
1006 shutil.copymode(relsrc, reltarget)
1006 shutil.copymode(relsrc, reltarget)
1007 restore = False
1007 restore = False
1008 finally:
1008 finally:
1009 if restore:
1009 if restore:
1010 repo.remove([abstarget], wlock)
1010 repo.remove([abstarget], wlock)
1011 except shutil.Error, inst:
1011 except shutil.Error, inst:
1012 raise util.Abort(str(inst))
1012 raise util.Abort(str(inst))
1013 except IOError, inst:
1013 except IOError, inst:
1014 if inst.errno == errno.ENOENT:
1014 if inst.errno == errno.ENOENT:
1015 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1015 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1016 else:
1016 else:
1017 ui.warn(_('%s: cannot copy - %s\n') %
1017 ui.warn(_('%s: cannot copy - %s\n') %
1018 (relsrc, inst.strerror))
1018 (relsrc, inst.strerror))
1019 errors += 1
1019 errors += 1
1020 return
1020 return
1021 if ui.verbose or not exact:
1021 if ui.verbose or not exact:
1022 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1022 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1023 targets[abstarget] = abssrc
1023 targets[abstarget] = abssrc
1024 if abstarget != origsrc and not opts.get('dry_run'):
1024 if abstarget != origsrc and not opts.get('dry_run'):
1025 repo.copy(origsrc, abstarget, wlock)
1025 repo.copy(origsrc, abstarget, wlock)
1026 copied.append((abssrc, relsrc, exact))
1026 copied.append((abssrc, relsrc, exact))
1027
1027
1028 def targetpathfn(pat, dest, srcs):
1028 def targetpathfn(pat, dest, srcs):
1029 if os.path.isdir(pat):
1029 if os.path.isdir(pat):
1030 abspfx = util.canonpath(repo.root, cwd, pat)
1030 abspfx = util.canonpath(repo.root, cwd, pat)
1031 if destdirexists:
1031 if destdirexists:
1032 striplen = len(os.path.split(abspfx)[0])
1032 striplen = len(os.path.split(abspfx)[0])
1033 else:
1033 else:
1034 striplen = len(abspfx)
1034 striplen = len(abspfx)
1035 if striplen:
1035 if striplen:
1036 striplen += len(os.sep)
1036 striplen += len(os.sep)
1037 res = lambda p: os.path.join(dest, p[striplen:])
1037 res = lambda p: os.path.join(dest, p[striplen:])
1038 elif destdirexists:
1038 elif destdirexists:
1039 res = lambda p: os.path.join(dest, os.path.basename(p))
1039 res = lambda p: os.path.join(dest, os.path.basename(p))
1040 else:
1040 else:
1041 res = lambda p: dest
1041 res = lambda p: dest
1042 return res
1042 return res
1043
1043
1044 def targetpathafterfn(pat, dest, srcs):
1044 def targetpathafterfn(pat, dest, srcs):
1045 if util.patkind(pat, None)[0]:
1045 if util.patkind(pat, None)[0]:
1046 # a mercurial pattern
1046 # a mercurial pattern
1047 res = lambda p: os.path.join(dest, os.path.basename(p))
1047 res = lambda p: os.path.join(dest, os.path.basename(p))
1048 else:
1048 else:
1049 abspfx = util.canonpath(repo.root, cwd, pat)
1049 abspfx = util.canonpath(repo.root, cwd, pat)
1050 if len(abspfx) < len(srcs[0][0]):
1050 if len(abspfx) < len(srcs[0][0]):
1051 # A directory. Either the target path contains the last
1051 # A directory. Either the target path contains the last
1052 # component of the source path or it does not.
1052 # component of the source path or it does not.
1053 def evalpath(striplen):
1053 def evalpath(striplen):
1054 score = 0
1054 score = 0
1055 for s in srcs:
1055 for s in srcs:
1056 t = os.path.join(dest, s[0][striplen:])
1056 t = os.path.join(dest, s[0][striplen:])
1057 if os.path.exists(t):
1057 if os.path.exists(t):
1058 score += 1
1058 score += 1
1059 return score
1059 return score
1060
1060
1061 striplen = len(abspfx)
1061 striplen = len(abspfx)
1062 if striplen:
1062 if striplen:
1063 striplen += len(os.sep)
1063 striplen += len(os.sep)
1064 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1064 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1065 score = evalpath(striplen)
1065 score = evalpath(striplen)
1066 striplen1 = len(os.path.split(abspfx)[0])
1066 striplen1 = len(os.path.split(abspfx)[0])
1067 if striplen1:
1067 if striplen1:
1068 striplen1 += len(os.sep)
1068 striplen1 += len(os.sep)
1069 if evalpath(striplen1) > score:
1069 if evalpath(striplen1) > score:
1070 striplen = striplen1
1070 striplen = striplen1
1071 res = lambda p: os.path.join(dest, p[striplen:])
1071 res = lambda p: os.path.join(dest, p[striplen:])
1072 else:
1072 else:
1073 # a file
1073 # a file
1074 if destdirexists:
1074 if destdirexists:
1075 res = lambda p: os.path.join(dest, os.path.basename(p))
1075 res = lambda p: os.path.join(dest, os.path.basename(p))
1076 else:
1076 else:
1077 res = lambda p: dest
1077 res = lambda p: dest
1078 return res
1078 return res
1079
1079
1080
1080
1081 pats = list(pats)
1081 pats = list(pats)
1082 if not pats:
1082 if not pats:
1083 raise util.Abort(_('no source or destination specified'))
1083 raise util.Abort(_('no source or destination specified'))
1084 if len(pats) == 1:
1084 if len(pats) == 1:
1085 raise util.Abort(_('no destination specified'))
1085 raise util.Abort(_('no destination specified'))
1086 dest = pats.pop()
1086 dest = pats.pop()
1087 destdirexists = os.path.isdir(dest)
1087 destdirexists = os.path.isdir(dest)
1088 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1088 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1089 raise util.Abort(_('with multiple sources, destination must be an '
1089 raise util.Abort(_('with multiple sources, destination must be an '
1090 'existing directory'))
1090 'existing directory'))
1091 if opts['after']:
1091 if opts['after']:
1092 tfn = targetpathafterfn
1092 tfn = targetpathafterfn
1093 else:
1093 else:
1094 tfn = targetpathfn
1094 tfn = targetpathfn
1095 copylist = []
1095 copylist = []
1096 for pat in pats:
1096 for pat in pats:
1097 srcs = []
1097 srcs = []
1098 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
1098 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
1099 origsrc = okaytocopy(abssrc, relsrc, exact)
1099 origsrc = okaytocopy(abssrc, relsrc, exact)
1100 if origsrc:
1100 if origsrc:
1101 srcs.append((origsrc, abssrc, relsrc, exact))
1101 srcs.append((origsrc, abssrc, relsrc, exact))
1102 if not srcs:
1102 if not srcs:
1103 continue
1103 continue
1104 copylist.append((tfn(pat, dest, srcs), srcs))
1104 copylist.append((tfn(pat, dest, srcs), srcs))
1105 if not copylist:
1105 if not copylist:
1106 raise util.Abort(_('no files to copy'))
1106 raise util.Abort(_('no files to copy'))
1107
1107
1108 for targetpath, srcs in copylist:
1108 for targetpath, srcs in copylist:
1109 for origsrc, abssrc, relsrc, exact in srcs:
1109 for origsrc, abssrc, relsrc, exact in srcs:
1110 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1110 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1111
1111
1112 if errors:
1112 if errors:
1113 ui.warn(_('(consider using --after)\n'))
1113 ui.warn(_('(consider using --after)\n'))
1114 return errors, copied
1114 return errors, copied
1115
1115
1116 def copy(ui, repo, *pats, **opts):
1116 def copy(ui, repo, *pats, **opts):
1117 """mark files as copied for the next commit
1117 """mark files as copied for the next commit
1118
1118
1119 Mark dest as having copies of source files. If dest is a
1119 Mark dest as having copies of source files. If dest is a
1120 directory, copies are put in that directory. If dest is a file,
1120 directory, copies are put in that directory. If dest is a file,
1121 there can only be one source.
1121 there can only be one source.
1122
1122
1123 By default, this command copies the contents of files as they
1123 By default, this command copies the contents of files as they
1124 stand in the working directory. If invoked with --after, the
1124 stand in the working directory. If invoked with --after, the
1125 operation is recorded, but no copying is performed.
1125 operation is recorded, but no copying is performed.
1126
1126
1127 This command takes effect in the next commit.
1127 This command takes effect in the next commit.
1128
1128
1129 NOTE: This command should be treated as experimental. While it
1129 NOTE: This command should be treated as experimental. While it
1130 should properly record copied files, this information is not yet
1130 should properly record copied files, this information is not yet
1131 fully used by merge, nor fully reported by log.
1131 fully used by merge, nor fully reported by log.
1132 """
1132 """
1133 wlock = repo.wlock(0)
1133 wlock = repo.wlock(0)
1134 errs, copied = docopy(ui, repo, pats, opts, wlock)
1134 errs, copied = docopy(ui, repo, pats, opts, wlock)
1135 return errs
1135 return errs
1136
1136
1137 def debugancestor(ui, index, rev1, rev2):
1137 def debugancestor(ui, index, rev1, rev2):
1138 """find the ancestor revision of two revisions in a given index"""
1138 """find the ancestor revision of two revisions in a given index"""
1139 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1139 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1140 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1140 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1141 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1141 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1142
1142
1143 def debugcomplete(ui, cmd='', **opts):
1143 def debugcomplete(ui, cmd='', **opts):
1144 """returns the completion list associated with the given command"""
1144 """returns the completion list associated with the given command"""
1145
1145
1146 if opts['options']:
1146 if opts['options']:
1147 options = []
1147 options = []
1148 otables = [globalopts]
1148 otables = [globalopts]
1149 if cmd:
1149 if cmd:
1150 aliases, entry = findcmd(ui, cmd)
1150 aliases, entry = findcmd(ui, cmd)
1151 otables.append(entry[1])
1151 otables.append(entry[1])
1152 for t in otables:
1152 for t in otables:
1153 for o in t:
1153 for o in t:
1154 if o[0]:
1154 if o[0]:
1155 options.append('-%s' % o[0])
1155 options.append('-%s' % o[0])
1156 options.append('--%s' % o[1])
1156 options.append('--%s' % o[1])
1157 ui.write("%s\n" % "\n".join(options))
1157 ui.write("%s\n" % "\n".join(options))
1158 return
1158 return
1159
1159
1160 clist = findpossible(ui, cmd).keys()
1160 clist = findpossible(ui, cmd).keys()
1161 clist.sort()
1161 clist.sort()
1162 ui.write("%s\n" % "\n".join(clist))
1162 ui.write("%s\n" % "\n".join(clist))
1163
1163
1164 def debugrebuildstate(ui, repo, rev=None):
1164 def debugrebuildstate(ui, repo, rev=None):
1165 """rebuild the dirstate as it would look like for the given revision"""
1165 """rebuild the dirstate as it would look like for the given revision"""
1166 if not rev:
1166 if not rev:
1167 rev = repo.changelog.tip()
1167 rev = repo.changelog.tip()
1168 else:
1168 else:
1169 rev = repo.lookup(rev)
1169 rev = repo.lookup(rev)
1170 change = repo.changelog.read(rev)
1170 change = repo.changelog.read(rev)
1171 n = change[0]
1171 n = change[0]
1172 files = repo.manifest.read(n)
1172 files = repo.manifest.read(n)
1173 wlock = repo.wlock()
1173 wlock = repo.wlock()
1174 repo.dirstate.rebuild(rev, files)
1174 repo.dirstate.rebuild(rev, files)
1175
1175
1176 def debugcheckstate(ui, repo):
1176 def debugcheckstate(ui, repo):
1177 """validate the correctness of the current dirstate"""
1177 """validate the correctness of the current dirstate"""
1178 parent1, parent2 = repo.dirstate.parents()
1178 parent1, parent2 = repo.dirstate.parents()
1179 repo.dirstate.read()
1179 repo.dirstate.read()
1180 dc = repo.dirstate.map
1180 dc = repo.dirstate.map
1181 keys = dc.keys()
1181 keys = dc.keys()
1182 keys.sort()
1182 keys.sort()
1183 m1n = repo.changelog.read(parent1)[0]
1183 m1n = repo.changelog.read(parent1)[0]
1184 m2n = repo.changelog.read(parent2)[0]
1184 m2n = repo.changelog.read(parent2)[0]
1185 m1 = repo.manifest.read(m1n)
1185 m1 = repo.manifest.read(m1n)
1186 m2 = repo.manifest.read(m2n)
1186 m2 = repo.manifest.read(m2n)
1187 errors = 0
1187 errors = 0
1188 for f in dc:
1188 for f in dc:
1189 state = repo.dirstate.state(f)
1189 state = repo.dirstate.state(f)
1190 if state in "nr" and f not in m1:
1190 if state in "nr" and f not in m1:
1191 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1191 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1192 errors += 1
1192 errors += 1
1193 if state in "a" and f in m1:
1193 if state in "a" and f in m1:
1194 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1194 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1195 errors += 1
1195 errors += 1
1196 if state in "m" and f not in m1 and f not in m2:
1196 if state in "m" and f not in m1 and f not in m2:
1197 ui.warn(_("%s in state %s, but not in either manifest\n") %
1197 ui.warn(_("%s in state %s, but not in either manifest\n") %
1198 (f, state))
1198 (f, state))
1199 errors += 1
1199 errors += 1
1200 for f in m1:
1200 for f in m1:
1201 state = repo.dirstate.state(f)
1201 state = repo.dirstate.state(f)
1202 if state not in "nrm":
1202 if state not in "nrm":
1203 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1203 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1204 errors += 1
1204 errors += 1
1205 if errors:
1205 if errors:
1206 error = _(".hg/dirstate inconsistent with current parent's manifest")
1206 error = _(".hg/dirstate inconsistent with current parent's manifest")
1207 raise util.Abort(error)
1207 raise util.Abort(error)
1208
1208
1209 def showconfig(ui, repo, *values):
1209 def showconfig(ui, repo, *values):
1210 """show combined config settings from all hgrc files
1210 """show combined config settings from all hgrc files
1211
1211
1212 With no args, print names and values of all config items.
1212 With no args, print names and values of all config items.
1213
1213
1214 With one arg of the form section.name, print just the value of
1214 With one arg of the form section.name, print just the value of
1215 that config item.
1215 that config item.
1216
1216
1217 With multiple args, print names and values of all config items
1217 With multiple args, print names and values of all config items
1218 with matching section names."""
1218 with matching section names."""
1219
1219
1220 if values:
1220 if values:
1221 if len([v for v in values if '.' in v]) > 1:
1221 if len([v for v in values if '.' in v]) > 1:
1222 raise util.Abort(_('only one config item permitted'))
1222 raise util.Abort(_('only one config item permitted'))
1223 for section, name, value in ui.walkconfig():
1223 for section, name, value in ui.walkconfig():
1224 sectname = section + '.' + name
1224 sectname = section + '.' + name
1225 if values:
1225 if values:
1226 for v in values:
1226 for v in values:
1227 if v == section:
1227 if v == section:
1228 ui.write('%s=%s\n' % (sectname, value))
1228 ui.write('%s=%s\n' % (sectname, value))
1229 elif v == sectname:
1229 elif v == sectname:
1230 ui.write(value, '\n')
1230 ui.write(value, '\n')
1231 else:
1231 else:
1232 ui.write('%s=%s\n' % (sectname, value))
1232 ui.write('%s=%s\n' % (sectname, value))
1233
1233
1234 def debugsetparents(ui, repo, rev1, rev2=None):
1234 def debugsetparents(ui, repo, rev1, rev2=None):
1235 """manually set the parents of the current working directory
1235 """manually set the parents of the current working directory
1236
1236
1237 This is useful for writing repository conversion tools, but should
1237 This is useful for writing repository conversion tools, but should
1238 be used with care.
1238 be used with care.
1239 """
1239 """
1240
1240
1241 if not rev2:
1241 if not rev2:
1242 rev2 = hex(nullid)
1242 rev2 = hex(nullid)
1243
1243
1244 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1244 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1245
1245
1246 def debugstate(ui, repo):
1246 def debugstate(ui, repo):
1247 """show the contents of the current dirstate"""
1247 """show the contents of the current dirstate"""
1248 repo.dirstate.read()
1248 repo.dirstate.read()
1249 dc = repo.dirstate.map
1249 dc = repo.dirstate.map
1250 keys = dc.keys()
1250 keys = dc.keys()
1251 keys.sort()
1251 keys.sort()
1252 for file_ in keys:
1252 for file_ in keys:
1253 ui.write("%c %3o %10d %s %s\n"
1253 ui.write("%c %3o %10d %s %s\n"
1254 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1254 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1255 time.strftime("%x %X",
1255 time.strftime("%x %X",
1256 time.localtime(dc[file_][3])), file_))
1256 time.localtime(dc[file_][3])), file_))
1257 for f in repo.dirstate.copies():
1257 for f in repo.dirstate.copies():
1258 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1258 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1259
1259
1260 def debugdata(ui, file_, rev):
1260 def debugdata(ui, file_, rev):
1261 """dump the contents of an data file revision"""
1261 """dump the contents of an data file revision"""
1262 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1262 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1263 file_[:-2] + ".i", file_, 0)
1263 file_[:-2] + ".i", file_, 0)
1264 try:
1264 try:
1265 ui.write(r.revision(r.lookup(rev)))
1265 ui.write(r.revision(r.lookup(rev)))
1266 except KeyError:
1266 except KeyError:
1267 raise util.Abort(_('invalid revision identifier %s') % rev)
1267 raise util.Abort(_('invalid revision identifier %s') % rev)
1268
1268
1269 def debugindex(ui, file_):
1269 def debugindex(ui, file_):
1270 """dump the contents of an index file"""
1270 """dump the contents of an index file"""
1271 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1271 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1272 ui.write(" rev offset length base linkrev" +
1272 ui.write(" rev offset length base linkrev" +
1273 " nodeid p1 p2\n")
1273 " nodeid p1 p2\n")
1274 for i in xrange(r.count()):
1274 for i in xrange(r.count()):
1275 node = r.node(i)
1275 node = r.node(i)
1276 pp = r.parents(node)
1276 pp = r.parents(node)
1277 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1277 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1278 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1278 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1279 short(node), short(pp[0]), short(pp[1])))
1279 short(node), short(pp[0]), short(pp[1])))
1280
1280
1281 def debugindexdot(ui, file_):
1281 def debugindexdot(ui, file_):
1282 """dump an index DAG as a .dot file"""
1282 """dump an index DAG as a .dot file"""
1283 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1283 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1284 ui.write("digraph G {\n")
1284 ui.write("digraph G {\n")
1285 for i in xrange(r.count()):
1285 for i in xrange(r.count()):
1286 node = r.node(i)
1286 node = r.node(i)
1287 pp = r.parents(node)
1287 pp = r.parents(node)
1288 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1288 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1289 if pp[1] != nullid:
1289 if pp[1] != nullid:
1290 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1290 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1291 ui.write("}\n")
1291 ui.write("}\n")
1292
1292
1293 def debugrename(ui, repo, file, rev=None):
1293 def debugrename(ui, repo, file, rev=None):
1294 """dump rename information"""
1294 """dump rename information"""
1295 r = repo.file(relpath(repo, [file])[0])
1295 r = repo.file(relpath(repo, [file])[0])
1296 if rev:
1296 if rev:
1297 try:
1297 try:
1298 # assume all revision numbers are for changesets
1298 # assume all revision numbers are for changesets
1299 n = repo.lookup(rev)
1299 n = repo.lookup(rev)
1300 change = repo.changelog.read(n)
1300 change = repo.changelog.read(n)
1301 m = repo.manifest.read(change[0])
1301 m = repo.manifest.read(change[0])
1302 n = m[relpath(repo, [file])[0]]
1302 n = m[relpath(repo, [file])[0]]
1303 except (hg.RepoError, KeyError):
1303 except (hg.RepoError, KeyError):
1304 n = r.lookup(rev)
1304 n = r.lookup(rev)
1305 else:
1305 else:
1306 n = r.tip()
1306 n = r.tip()
1307 m = r.renamed(n)
1307 m = r.renamed(n)
1308 if m:
1308 if m:
1309 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1309 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1310 else:
1310 else:
1311 ui.write(_("not renamed\n"))
1311 ui.write(_("not renamed\n"))
1312
1312
1313 def debugwalk(ui, repo, *pats, **opts):
1313 def debugwalk(ui, repo, *pats, **opts):
1314 """show how files match on given patterns"""
1314 """show how files match on given patterns"""
1315 items = list(cmdutil.walk(repo, pats, opts))
1315 items = list(cmdutil.walk(repo, pats, opts))
1316 if not items:
1316 if not items:
1317 return
1317 return
1318 fmt = '%%s %%-%ds %%-%ds %%s' % (
1318 fmt = '%%s %%-%ds %%-%ds %%s' % (
1319 max([len(abs) for (src, abs, rel, exact) in items]),
1319 max([len(abs) for (src, abs, rel, exact) in items]),
1320 max([len(rel) for (src, abs, rel, exact) in items]))
1320 max([len(rel) for (src, abs, rel, exact) in items]))
1321 for src, abs, rel, exact in items:
1321 for src, abs, rel, exact in items:
1322 line = fmt % (src, abs, rel, exact and 'exact' or '')
1322 line = fmt % (src, abs, rel, exact and 'exact' or '')
1323 ui.write("%s\n" % line.rstrip())
1323 ui.write("%s\n" % line.rstrip())
1324
1324
1325 def diff(ui, repo, *pats, **opts):
1325 def diff(ui, repo, *pats, **opts):
1326 """diff repository (or selected files)
1326 """diff repository (or selected files)
1327
1327
1328 Show differences between revisions for the specified files.
1328 Show differences between revisions for the specified files.
1329
1329
1330 Differences between files are shown using the unified diff format.
1330 Differences between files are shown using the unified diff format.
1331
1331
1332 When two revision arguments are given, then changes are shown
1332 When two revision arguments are given, then changes are shown
1333 between those revisions. If only one revision is specified then
1333 between those revisions. If only one revision is specified then
1334 that revision is compared to the working directory, and, when no
1334 that revision is compared to the working directory, and, when no
1335 revisions are specified, the working directory files are compared
1335 revisions are specified, the working directory files are compared
1336 to its parent.
1336 to its parent.
1337
1337
1338 Without the -a option, diff will avoid generating diffs of files
1338 Without the -a option, diff will avoid generating diffs of files
1339 it detects as binary. With -a, diff will generate a diff anyway,
1339 it detects as binary. With -a, diff will generate a diff anyway,
1340 probably with undesirable results.
1340 probably with undesirable results.
1341 """
1341 """
1342 node1, node2 = cmdutil.revpair(ui, repo, opts['rev'])
1342 node1, node2 = cmdutil.revpair(ui, repo, opts['rev'])
1343
1343
1344 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1344 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1345
1345
1346 patch.diff(repo, node1, node2, fns, match=matchfn,
1346 patch.diff(repo, node1, node2, fns, match=matchfn,
1347 opts=patch.diffopts(ui, opts))
1347 opts=patch.diffopts(ui, opts))
1348
1348
1349 def export(ui, repo, *changesets, **opts):
1349 def export(ui, repo, *changesets, **opts):
1350 """dump the header and diffs for one or more changesets
1350 """dump the header and diffs for one or more changesets
1351
1351
1352 Print the changeset header and diffs for one or more revisions.
1352 Print the changeset header and diffs for one or more revisions.
1353
1353
1354 The information shown in the changeset header is: author,
1354 The information shown in the changeset header is: author,
1355 changeset hash, parent and commit comment.
1355 changeset hash, parent and commit comment.
1356
1356
1357 Output may be to a file, in which case the name of the file is
1357 Output may be to a file, in which case the name of the file is
1358 given using a format string. The formatting rules are as follows:
1358 given using a format string. The formatting rules are as follows:
1359
1359
1360 %% literal "%" character
1360 %% literal "%" character
1361 %H changeset hash (40 bytes of hexadecimal)
1361 %H changeset hash (40 bytes of hexadecimal)
1362 %N number of patches being generated
1362 %N number of patches being generated
1363 %R changeset revision number
1363 %R changeset revision number
1364 %b basename of the exporting repository
1364 %b basename of the exporting repository
1365 %h short-form changeset hash (12 bytes of hexadecimal)
1365 %h short-form changeset hash (12 bytes of hexadecimal)
1366 %n zero-padded sequence number, starting at 1
1366 %n zero-padded sequence number, starting at 1
1367 %r zero-padded changeset revision number
1367 %r zero-padded changeset revision number
1368
1368
1369 Without the -a option, export will avoid generating diffs of files
1369 Without the -a option, export will avoid generating diffs of files
1370 it detects as binary. With -a, export will generate a diff anyway,
1370 it detects as binary. With -a, export will generate a diff anyway,
1371 probably with undesirable results.
1371 probably with undesirable results.
1372
1372
1373 With the --switch-parent option, the diff will be against the second
1373 With the --switch-parent option, the diff will be against the second
1374 parent. It can be useful to review a merge.
1374 parent. It can be useful to review a merge.
1375 """
1375 """
1376 if not changesets:
1376 if not changesets:
1377 raise util.Abort(_("export requires at least one changeset"))
1377 raise util.Abort(_("export requires at least one changeset"))
1378 revs = list(cmdutil.revrange(ui, repo, changesets))
1378 revs = cmdutil.revrange(ui, repo, changesets)
1379 if len(revs) > 1:
1379 if len(revs) > 1:
1380 ui.note(_('exporting patches:\n'))
1380 ui.note(_('exporting patches:\n'))
1381 else:
1381 else:
1382 ui.note(_('exporting patch:\n'))
1382 ui.note(_('exporting patch:\n'))
1383 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1383 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1384 switch_parent=opts['switch_parent'],
1384 switch_parent=opts['switch_parent'],
1385 opts=patch.diffopts(ui, opts))
1385 opts=patch.diffopts(ui, opts))
1386
1386
1387 def grep(ui, repo, pattern, *pats, **opts):
1387 def grep(ui, repo, pattern, *pats, **opts):
1388 """search for a pattern in specified files and revisions
1388 """search for a pattern in specified files and revisions
1389
1389
1390 Search revisions of files for a regular expression.
1390 Search revisions of files for a regular expression.
1391
1391
1392 This command behaves differently than Unix grep. It only accepts
1392 This command behaves differently than Unix grep. It only accepts
1393 Python/Perl regexps. It searches repository history, not the
1393 Python/Perl regexps. It searches repository history, not the
1394 working directory. It always prints the revision number in which
1394 working directory. It always prints the revision number in which
1395 a match appears.
1395 a match appears.
1396
1396
1397 By default, grep only prints output for the first revision of a
1397 By default, grep only prints output for the first revision of a
1398 file in which it finds a match. To get it to print every revision
1398 file in which it finds a match. To get it to print every revision
1399 that contains a change in match status ("-" for a match that
1399 that contains a change in match status ("-" for a match that
1400 becomes a non-match, or "+" for a non-match that becomes a match),
1400 becomes a non-match, or "+" for a non-match that becomes a match),
1401 use the --all flag.
1401 use the --all flag.
1402 """
1402 """
1403 reflags = 0
1403 reflags = 0
1404 if opts['ignore_case']:
1404 if opts['ignore_case']:
1405 reflags |= re.I
1405 reflags |= re.I
1406 regexp = re.compile(pattern, reflags)
1406 regexp = re.compile(pattern, reflags)
1407 sep, eol = ':', '\n'
1407 sep, eol = ':', '\n'
1408 if opts['print0']:
1408 if opts['print0']:
1409 sep = eol = '\0'
1409 sep = eol = '\0'
1410
1410
1411 fcache = {}
1411 fcache = {}
1412 def getfile(fn):
1412 def getfile(fn):
1413 if fn not in fcache:
1413 if fn not in fcache:
1414 fcache[fn] = repo.file(fn)
1414 fcache[fn] = repo.file(fn)
1415 return fcache[fn]
1415 return fcache[fn]
1416
1416
1417 def matchlines(body):
1417 def matchlines(body):
1418 begin = 0
1418 begin = 0
1419 linenum = 0
1419 linenum = 0
1420 while True:
1420 while True:
1421 match = regexp.search(body, begin)
1421 match = regexp.search(body, begin)
1422 if not match:
1422 if not match:
1423 break
1423 break
1424 mstart, mend = match.span()
1424 mstart, mend = match.span()
1425 linenum += body.count('\n', begin, mstart) + 1
1425 linenum += body.count('\n', begin, mstart) + 1
1426 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1426 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1427 lend = body.find('\n', mend)
1427 lend = body.find('\n', mend)
1428 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1428 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1429 begin = lend + 1
1429 begin = lend + 1
1430
1430
1431 class linestate(object):
1431 class linestate(object):
1432 def __init__(self, line, linenum, colstart, colend):
1432 def __init__(self, line, linenum, colstart, colend):
1433 self.line = line
1433 self.line = line
1434 self.linenum = linenum
1434 self.linenum = linenum
1435 self.colstart = colstart
1435 self.colstart = colstart
1436 self.colend = colend
1436 self.colend = colend
1437
1437
1438 def __eq__(self, other):
1438 def __eq__(self, other):
1439 return self.line == other.line
1439 return self.line == other.line
1440
1440
1441 matches = {}
1441 matches = {}
1442 copies = {}
1442 copies = {}
1443 def grepbody(fn, rev, body):
1443 def grepbody(fn, rev, body):
1444 matches[rev].setdefault(fn, [])
1444 matches[rev].setdefault(fn, [])
1445 m = matches[rev][fn]
1445 m = matches[rev][fn]
1446 for lnum, cstart, cend, line in matchlines(body):
1446 for lnum, cstart, cend, line in matchlines(body):
1447 s = linestate(line, lnum, cstart, cend)
1447 s = linestate(line, lnum, cstart, cend)
1448 m.append(s)
1448 m.append(s)
1449
1449
1450 def difflinestates(a, b):
1450 def difflinestates(a, b):
1451 sm = difflib.SequenceMatcher(None, a, b)
1451 sm = difflib.SequenceMatcher(None, a, b)
1452 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1452 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1453 if tag == 'insert':
1453 if tag == 'insert':
1454 for i in xrange(blo, bhi):
1454 for i in xrange(blo, bhi):
1455 yield ('+', b[i])
1455 yield ('+', b[i])
1456 elif tag == 'delete':
1456 elif tag == 'delete':
1457 for i in xrange(alo, ahi):
1457 for i in xrange(alo, ahi):
1458 yield ('-', a[i])
1458 yield ('-', a[i])
1459 elif tag == 'replace':
1459 elif tag == 'replace':
1460 for i in xrange(alo, ahi):
1460 for i in xrange(alo, ahi):
1461 yield ('-', a[i])
1461 yield ('-', a[i])
1462 for i in xrange(blo, bhi):
1462 for i in xrange(blo, bhi):
1463 yield ('+', b[i])
1463 yield ('+', b[i])
1464
1464
1465 prev = {}
1465 prev = {}
1466 def display(fn, rev, states, prevstates):
1466 def display(fn, rev, states, prevstates):
1467 counts = {'-': 0, '+': 0}
1467 counts = {'-': 0, '+': 0}
1468 filerevmatches = {}
1468 filerevmatches = {}
1469 if incrementing or not opts['all']:
1469 if incrementing or not opts['all']:
1470 a, b, r = prevstates, states, rev
1470 a, b, r = prevstates, states, rev
1471 else:
1471 else:
1472 a, b, r = states, prevstates, prev.get(fn, -1)
1472 a, b, r = states, prevstates, prev.get(fn, -1)
1473 for change, l in difflinestates(a, b):
1473 for change, l in difflinestates(a, b):
1474 cols = [fn, str(r)]
1474 cols = [fn, str(r)]
1475 if opts['line_number']:
1475 if opts['line_number']:
1476 cols.append(str(l.linenum))
1476 cols.append(str(l.linenum))
1477 if opts['all']:
1477 if opts['all']:
1478 cols.append(change)
1478 cols.append(change)
1479 if opts['user']:
1479 if opts['user']:
1480 cols.append(ui.shortuser(getchange(r)[1]))
1480 cols.append(ui.shortuser(getchange(r)[1]))
1481 if opts['files_with_matches']:
1481 if opts['files_with_matches']:
1482 c = (fn, r)
1482 c = (fn, r)
1483 if c in filerevmatches:
1483 if c in filerevmatches:
1484 continue
1484 continue
1485 filerevmatches[c] = 1
1485 filerevmatches[c] = 1
1486 else:
1486 else:
1487 cols.append(l.line)
1487 cols.append(l.line)
1488 ui.write(sep.join(cols), eol)
1488 ui.write(sep.join(cols), eol)
1489 counts[change] += 1
1489 counts[change] += 1
1490 return counts['+'], counts['-']
1490 return counts['+'], counts['-']
1491
1491
1492 fstate = {}
1492 fstate = {}
1493 skip = {}
1493 skip = {}
1494 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1494 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1495 count = 0
1495 count = 0
1496 incrementing = False
1496 incrementing = False
1497 follow = opts.get('follow')
1497 follow = opts.get('follow')
1498 for st, rev, fns in changeiter:
1498 for st, rev, fns in changeiter:
1499 if st == 'window':
1499 if st == 'window':
1500 incrementing = rev
1500 incrementing = rev
1501 matches.clear()
1501 matches.clear()
1502 elif st == 'add':
1502 elif st == 'add':
1503 change = repo.changelog.read(repo.lookup(str(rev)))
1503 change = repo.changelog.read(repo.lookup(str(rev)))
1504 mf = repo.manifest.read(change[0])
1504 mf = repo.manifest.read(change[0])
1505 matches[rev] = {}
1505 matches[rev] = {}
1506 for fn in fns:
1506 for fn in fns:
1507 if fn in skip:
1507 if fn in skip:
1508 continue
1508 continue
1509 fstate.setdefault(fn, {})
1509 fstate.setdefault(fn, {})
1510 try:
1510 try:
1511 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1511 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1512 if follow:
1512 if follow:
1513 copied = getfile(fn).renamed(mf[fn])
1513 copied = getfile(fn).renamed(mf[fn])
1514 if copied:
1514 if copied:
1515 copies.setdefault(rev, {})[fn] = copied[0]
1515 copies.setdefault(rev, {})[fn] = copied[0]
1516 except KeyError:
1516 except KeyError:
1517 pass
1517 pass
1518 elif st == 'iter':
1518 elif st == 'iter':
1519 states = matches[rev].items()
1519 states = matches[rev].items()
1520 states.sort()
1520 states.sort()
1521 for fn, m in states:
1521 for fn, m in states:
1522 copy = copies.get(rev, {}).get(fn)
1522 copy = copies.get(rev, {}).get(fn)
1523 if fn in skip:
1523 if fn in skip:
1524 if copy:
1524 if copy:
1525 skip[copy] = True
1525 skip[copy] = True
1526 continue
1526 continue
1527 if incrementing or not opts['all'] or fstate[fn]:
1527 if incrementing or not opts['all'] or fstate[fn]:
1528 pos, neg = display(fn, rev, m, fstate[fn])
1528 pos, neg = display(fn, rev, m, fstate[fn])
1529 count += pos + neg
1529 count += pos + neg
1530 if pos and not opts['all']:
1530 if pos and not opts['all']:
1531 skip[fn] = True
1531 skip[fn] = True
1532 if copy:
1532 if copy:
1533 skip[copy] = True
1533 skip[copy] = True
1534 fstate[fn] = m
1534 fstate[fn] = m
1535 if copy:
1535 if copy:
1536 fstate[copy] = m
1536 fstate[copy] = m
1537 prev[fn] = rev
1537 prev[fn] = rev
1538
1538
1539 if not incrementing:
1539 if not incrementing:
1540 fstate = fstate.items()
1540 fstate = fstate.items()
1541 fstate.sort()
1541 fstate.sort()
1542 for fn, state in fstate:
1542 for fn, state in fstate:
1543 if fn in skip:
1543 if fn in skip:
1544 continue
1544 continue
1545 if fn not in copies.get(prev[fn], {}):
1545 if fn not in copies.get(prev[fn], {}):
1546 display(fn, rev, {}, state)
1546 display(fn, rev, {}, state)
1547 return (count == 0 and 1) or 0
1547 return (count == 0 and 1) or 0
1548
1548
1549 def heads(ui, repo, **opts):
1549 def heads(ui, repo, **opts):
1550 """show current repository heads
1550 """show current repository heads
1551
1551
1552 Show all repository head changesets.
1552 Show all repository head changesets.
1553
1553
1554 Repository "heads" are changesets that don't have children
1554 Repository "heads" are changesets that don't have children
1555 changesets. They are where development generally takes place and
1555 changesets. They are where development generally takes place and
1556 are the usual targets for update and merge operations.
1556 are the usual targets for update and merge operations.
1557 """
1557 """
1558 if opts['rev']:
1558 if opts['rev']:
1559 heads = repo.heads(repo.lookup(opts['rev']))
1559 heads = repo.heads(repo.lookup(opts['rev']))
1560 else:
1560 else:
1561 heads = repo.heads()
1561 heads = repo.heads()
1562 br = None
1562 br = None
1563 if opts['branches']:
1563 if opts['branches']:
1564 ui.warn(_("the --branches option is deprecated, "
1564 ui.warn(_("the --branches option is deprecated, "
1565 "please use 'hg branches' instead\n"))
1565 "please use 'hg branches' instead\n"))
1566 br = repo.branchlookup(heads)
1566 br = repo.branchlookup(heads)
1567 displayer = show_changeset(ui, repo, opts)
1567 displayer = show_changeset(ui, repo, opts)
1568 for n in heads:
1568 for n in heads:
1569 displayer.show(changenode=n, brinfo=br)
1569 displayer.show(changenode=n, brinfo=br)
1570
1570
1571 def identify(ui, repo):
1571 def identify(ui, repo):
1572 """print information about the working copy
1572 """print information about the working copy
1573
1573
1574 Print a short summary of the current state of the repo.
1574 Print a short summary of the current state of the repo.
1575
1575
1576 This summary identifies the repository state using one or two parent
1576 This summary identifies the repository state using one or two parent
1577 hash identifiers, followed by a "+" if there are uncommitted changes
1577 hash identifiers, followed by a "+" if there are uncommitted changes
1578 in the working directory, followed by a list of tags for this revision.
1578 in the working directory, followed by a list of tags for this revision.
1579 """
1579 """
1580 parents = [p for p in repo.dirstate.parents() if p != nullid]
1580 parents = [p for p in repo.dirstate.parents() if p != nullid]
1581 if not parents:
1581 if not parents:
1582 ui.write(_("unknown\n"))
1582 ui.write(_("unknown\n"))
1583 return
1583 return
1584
1584
1585 hexfunc = ui.debugflag and hex or short
1585 hexfunc = ui.debugflag and hex or short
1586 modified, added, removed, deleted = repo.status()[:4]
1586 modified, added, removed, deleted = repo.status()[:4]
1587 output = ["%s%s" %
1587 output = ["%s%s" %
1588 ('+'.join([hexfunc(parent) for parent in parents]),
1588 ('+'.join([hexfunc(parent) for parent in parents]),
1589 (modified or added or removed or deleted) and "+" or "")]
1589 (modified or added or removed or deleted) and "+" or "")]
1590
1590
1591 if not ui.quiet:
1591 if not ui.quiet:
1592
1592
1593 branch = repo.workingctx().branch()
1593 branch = repo.workingctx().branch()
1594 if branch:
1594 if branch:
1595 output.append("(%s)" % branch)
1595 output.append("(%s)" % branch)
1596
1596
1597 # multiple tags for a single parent separated by '/'
1597 # multiple tags for a single parent separated by '/'
1598 parenttags = ['/'.join(tags)
1598 parenttags = ['/'.join(tags)
1599 for tags in map(repo.nodetags, parents) if tags]
1599 for tags in map(repo.nodetags, parents) if tags]
1600 # tags for multiple parents separated by ' + '
1600 # tags for multiple parents separated by ' + '
1601 if parenttags:
1601 if parenttags:
1602 output.append(' + '.join(parenttags))
1602 output.append(' + '.join(parenttags))
1603
1603
1604 ui.write("%s\n" % ' '.join(output))
1604 ui.write("%s\n" % ' '.join(output))
1605
1605
1606 def import_(ui, repo, patch1, *patches, **opts):
1606 def import_(ui, repo, patch1, *patches, **opts):
1607 """import an ordered set of patches
1607 """import an ordered set of patches
1608
1608
1609 Import a list of patches and commit them individually.
1609 Import a list of patches and commit them individually.
1610
1610
1611 If there are outstanding changes in the working directory, import
1611 If there are outstanding changes in the working directory, import
1612 will abort unless given the -f flag.
1612 will abort unless given the -f flag.
1613
1613
1614 You can import a patch straight from a mail message. Even patches
1614 You can import a patch straight from a mail message. Even patches
1615 as attachments work (body part must be type text/plain or
1615 as attachments work (body part must be type text/plain or
1616 text/x-patch to be used). From and Subject headers of email
1616 text/x-patch to be used). From and Subject headers of email
1617 message are used as default committer and commit message. All
1617 message are used as default committer and commit message. All
1618 text/plain body parts before first diff are added to commit
1618 text/plain body parts before first diff are added to commit
1619 message.
1619 message.
1620
1620
1621 If imported patch was generated by hg export, user and description
1621 If imported patch was generated by hg export, user and description
1622 from patch override values from message headers and body. Values
1622 from patch override values from message headers and body. Values
1623 given on command line with -m and -u override these.
1623 given on command line with -m and -u override these.
1624
1624
1625 To read a patch from standard input, use patch name "-".
1625 To read a patch from standard input, use patch name "-".
1626 """
1626 """
1627 patches = (patch1,) + patches
1627 patches = (patch1,) + patches
1628
1628
1629 if not opts['force']:
1629 if not opts['force']:
1630 bail_if_changed(repo)
1630 bail_if_changed(repo)
1631
1631
1632 d = opts["base"]
1632 d = opts["base"]
1633 strip = opts["strip"]
1633 strip = opts["strip"]
1634
1634
1635 wlock = repo.wlock()
1635 wlock = repo.wlock()
1636 lock = repo.lock()
1636 lock = repo.lock()
1637
1637
1638 for p in patches:
1638 for p in patches:
1639 pf = os.path.join(d, p)
1639 pf = os.path.join(d, p)
1640
1640
1641 if pf == '-':
1641 if pf == '-':
1642 ui.status(_("applying patch from stdin\n"))
1642 ui.status(_("applying patch from stdin\n"))
1643 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1643 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1644 else:
1644 else:
1645 ui.status(_("applying %s\n") % p)
1645 ui.status(_("applying %s\n") % p)
1646 tmpname, message, user, date = patch.extract(ui, file(pf))
1646 tmpname, message, user, date = patch.extract(ui, file(pf))
1647
1647
1648 if tmpname is None:
1648 if tmpname is None:
1649 raise util.Abort(_('no diffs found'))
1649 raise util.Abort(_('no diffs found'))
1650
1650
1651 try:
1651 try:
1652 if opts['message']:
1652 if opts['message']:
1653 # pickup the cmdline msg
1653 # pickup the cmdline msg
1654 message = opts['message']
1654 message = opts['message']
1655 elif message:
1655 elif message:
1656 # pickup the patch msg
1656 # pickup the patch msg
1657 message = message.strip()
1657 message = message.strip()
1658 else:
1658 else:
1659 # launch the editor
1659 # launch the editor
1660 message = None
1660 message = None
1661 ui.debug(_('message:\n%s\n') % message)
1661 ui.debug(_('message:\n%s\n') % message)
1662
1662
1663 files = {}
1663 files = {}
1664 try:
1664 try:
1665 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1665 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1666 files=files)
1666 files=files)
1667 finally:
1667 finally:
1668 files = patch.updatedir(ui, repo, files, wlock=wlock)
1668 files = patch.updatedir(ui, repo, files, wlock=wlock)
1669 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1669 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1670 finally:
1670 finally:
1671 os.unlink(tmpname)
1671 os.unlink(tmpname)
1672
1672
1673 def incoming(ui, repo, source="default", **opts):
1673 def incoming(ui, repo, source="default", **opts):
1674 """show new changesets found in source
1674 """show new changesets found in source
1675
1675
1676 Show new changesets found in the specified path/URL or the default
1676 Show new changesets found in the specified path/URL or the default
1677 pull location. These are the changesets that would be pulled if a pull
1677 pull location. These are the changesets that would be pulled if a pull
1678 was requested.
1678 was requested.
1679
1679
1680 For remote repository, using --bundle avoids downloading the changesets
1680 For remote repository, using --bundle avoids downloading the changesets
1681 twice if the incoming is followed by a pull.
1681 twice if the incoming is followed by a pull.
1682
1682
1683 See pull for valid source format details.
1683 See pull for valid source format details.
1684 """
1684 """
1685 source = ui.expandpath(source)
1685 source = ui.expandpath(source)
1686 setremoteconfig(ui, opts)
1686 setremoteconfig(ui, opts)
1687
1687
1688 other = hg.repository(ui, source)
1688 other = hg.repository(ui, source)
1689 incoming = repo.findincoming(other, force=opts["force"])
1689 incoming = repo.findincoming(other, force=opts["force"])
1690 if not incoming:
1690 if not incoming:
1691 ui.status(_("no changes found\n"))
1691 ui.status(_("no changes found\n"))
1692 return
1692 return
1693
1693
1694 cleanup = None
1694 cleanup = None
1695 try:
1695 try:
1696 fname = opts["bundle"]
1696 fname = opts["bundle"]
1697 if fname or not other.local():
1697 if fname or not other.local():
1698 # create a bundle (uncompressed if other repo is not local)
1698 # create a bundle (uncompressed if other repo is not local)
1699 cg = other.changegroup(incoming, "incoming")
1699 cg = other.changegroup(incoming, "incoming")
1700 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1700 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1701 # keep written bundle?
1701 # keep written bundle?
1702 if opts["bundle"]:
1702 if opts["bundle"]:
1703 cleanup = None
1703 cleanup = None
1704 if not other.local():
1704 if not other.local():
1705 # use the created uncompressed bundlerepo
1705 # use the created uncompressed bundlerepo
1706 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1706 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1707
1707
1708 revs = None
1708 revs = None
1709 if opts['rev']:
1709 if opts['rev']:
1710 revs = [other.lookup(rev) for rev in opts['rev']]
1710 revs = [other.lookup(rev) for rev in opts['rev']]
1711 o = other.changelog.nodesbetween(incoming, revs)[0]
1711 o = other.changelog.nodesbetween(incoming, revs)[0]
1712 if opts['newest_first']:
1712 if opts['newest_first']:
1713 o.reverse()
1713 o.reverse()
1714 displayer = show_changeset(ui, other, opts)
1714 displayer = show_changeset(ui, other, opts)
1715 for n in o:
1715 for n in o:
1716 parents = [p for p in other.changelog.parents(n) if p != nullid]
1716 parents = [p for p in other.changelog.parents(n) if p != nullid]
1717 if opts['no_merges'] and len(parents) == 2:
1717 if opts['no_merges'] and len(parents) == 2:
1718 continue
1718 continue
1719 displayer.show(changenode=n)
1719 displayer.show(changenode=n)
1720 if opts['patch']:
1720 if opts['patch']:
1721 prev = (parents and parents[0]) or nullid
1721 prev = (parents and parents[0]) or nullid
1722 patch.diff(other, prev, n, fp=repo.ui)
1722 patch.diff(other, prev, n, fp=repo.ui)
1723 ui.write("\n")
1723 ui.write("\n")
1724 finally:
1724 finally:
1725 if hasattr(other, 'close'):
1725 if hasattr(other, 'close'):
1726 other.close()
1726 other.close()
1727 if cleanup:
1727 if cleanup:
1728 os.unlink(cleanup)
1728 os.unlink(cleanup)
1729
1729
1730 def init(ui, dest=".", **opts):
1730 def init(ui, dest=".", **opts):
1731 """create a new repository in the given directory
1731 """create a new repository in the given directory
1732
1732
1733 Initialize a new repository in the given directory. If the given
1733 Initialize a new repository in the given directory. If the given
1734 directory does not exist, it is created.
1734 directory does not exist, it is created.
1735
1735
1736 If no directory is given, the current directory is used.
1736 If no directory is given, the current directory is used.
1737
1737
1738 It is possible to specify an ssh:// URL as the destination.
1738 It is possible to specify an ssh:// URL as the destination.
1739 Look at the help text for the pull command for important details
1739 Look at the help text for the pull command for important details
1740 about ssh:// URLs.
1740 about ssh:// URLs.
1741 """
1741 """
1742 setremoteconfig(ui, opts)
1742 setremoteconfig(ui, opts)
1743 hg.repository(ui, dest, create=1)
1743 hg.repository(ui, dest, create=1)
1744
1744
1745 def locate(ui, repo, *pats, **opts):
1745 def locate(ui, repo, *pats, **opts):
1746 """locate files matching specific patterns
1746 """locate files matching specific patterns
1747
1747
1748 Print all files under Mercurial control whose names match the
1748 Print all files under Mercurial control whose names match the
1749 given patterns.
1749 given patterns.
1750
1750
1751 This command searches the current directory and its
1751 This command searches the current directory and its
1752 subdirectories. To search an entire repository, move to the root
1752 subdirectories. To search an entire repository, move to the root
1753 of the repository.
1753 of the repository.
1754
1754
1755 If no patterns are given to match, this command prints all file
1755 If no patterns are given to match, this command prints all file
1756 names.
1756 names.
1757
1757
1758 If you want to feed the output of this command into the "xargs"
1758 If you want to feed the output of this command into the "xargs"
1759 command, use the "-0" option to both this command and "xargs".
1759 command, use the "-0" option to both this command and "xargs".
1760 This will avoid the problem of "xargs" treating single filenames
1760 This will avoid the problem of "xargs" treating single filenames
1761 that contain white space as multiple filenames.
1761 that contain white space as multiple filenames.
1762 """
1762 """
1763 end = opts['print0'] and '\0' or '\n'
1763 end = opts['print0'] and '\0' or '\n'
1764 rev = opts['rev']
1764 rev = opts['rev']
1765 if rev:
1765 if rev:
1766 node = repo.lookup(rev)
1766 node = repo.lookup(rev)
1767 else:
1767 else:
1768 node = None
1768 node = None
1769
1769
1770 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1770 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1771 head='(?:.*/|)'):
1771 head='(?:.*/|)'):
1772 if not node and repo.dirstate.state(abs) == '?':
1772 if not node and repo.dirstate.state(abs) == '?':
1773 continue
1773 continue
1774 if opts['fullpath']:
1774 if opts['fullpath']:
1775 ui.write(os.path.join(repo.root, abs), end)
1775 ui.write(os.path.join(repo.root, abs), end)
1776 else:
1776 else:
1777 ui.write(((pats and rel) or abs), end)
1777 ui.write(((pats and rel) or abs), end)
1778
1778
1779 def log(ui, repo, *pats, **opts):
1779 def log(ui, repo, *pats, **opts):
1780 """show revision history of entire repository or files
1780 """show revision history of entire repository or files
1781
1781
1782 Print the revision history of the specified files or the entire
1782 Print the revision history of the specified files or the entire
1783 project.
1783 project.
1784
1784
1785 File history is shown without following rename or copy history of
1785 File history is shown without following rename or copy history of
1786 files. Use -f/--follow with a file name to follow history across
1786 files. Use -f/--follow with a file name to follow history across
1787 renames and copies. --follow without a file name will only show
1787 renames and copies. --follow without a file name will only show
1788 ancestors or descendants of the starting revision. --follow-first
1788 ancestors or descendants of the starting revision. --follow-first
1789 only follows the first parent of merge revisions.
1789 only follows the first parent of merge revisions.
1790
1790
1791 If no revision range is specified, the default is tip:0 unless
1791 If no revision range is specified, the default is tip:0 unless
1792 --follow is set, in which case the working directory parent is
1792 --follow is set, in which case the working directory parent is
1793 used as the starting revision.
1793 used as the starting revision.
1794
1794
1795 By default this command outputs: changeset id and hash, tags,
1795 By default this command outputs: changeset id and hash, tags,
1796 non-trivial parents, user, date and time, and a summary for each
1796 non-trivial parents, user, date and time, and a summary for each
1797 commit. When the -v/--verbose switch is used, the list of changed
1797 commit. When the -v/--verbose switch is used, the list of changed
1798 files and full commit message is shown.
1798 files and full commit message is shown.
1799 """
1799 """
1800 class dui(object):
1800 class dui(object):
1801 # Implement and delegate some ui protocol. Save hunks of
1801 # Implement and delegate some ui protocol. Save hunks of
1802 # output for later display in the desired order.
1802 # output for later display in the desired order.
1803 def __init__(self, ui):
1803 def __init__(self, ui):
1804 self.ui = ui
1804 self.ui = ui
1805 self.hunk = {}
1805 self.hunk = {}
1806 self.header = {}
1806 self.header = {}
1807 def bump(self, rev):
1807 def bump(self, rev):
1808 self.rev = rev
1808 self.rev = rev
1809 self.hunk[rev] = []
1809 self.hunk[rev] = []
1810 self.header[rev] = []
1810 self.header[rev] = []
1811 def note(self, *args):
1811 def note(self, *args):
1812 if self.verbose:
1812 if self.verbose:
1813 self.write(*args)
1813 self.write(*args)
1814 def status(self, *args):
1814 def status(self, *args):
1815 if not self.quiet:
1815 if not self.quiet:
1816 self.write(*args)
1816 self.write(*args)
1817 def write(self, *args):
1817 def write(self, *args):
1818 self.hunk[self.rev].append(args)
1818 self.hunk[self.rev].append(args)
1819 def write_header(self, *args):
1819 def write_header(self, *args):
1820 self.header[self.rev].append(args)
1820 self.header[self.rev].append(args)
1821 def debug(self, *args):
1821 def debug(self, *args):
1822 if self.debugflag:
1822 if self.debugflag:
1823 self.write(*args)
1823 self.write(*args)
1824 def __getattr__(self, key):
1824 def __getattr__(self, key):
1825 return getattr(self.ui, key)
1825 return getattr(self.ui, key)
1826
1826
1827 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1827 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1828
1828
1829 if opts['branches']:
1829 if opts['branches']:
1830 ui.warn(_("the --branches option is deprecated, "
1830 ui.warn(_("the --branches option is deprecated, "
1831 "please use 'hg branches' instead\n"))
1831 "please use 'hg branches' instead\n"))
1832
1832
1833 if opts['limit']:
1833 if opts['limit']:
1834 try:
1834 try:
1835 limit = int(opts['limit'])
1835 limit = int(opts['limit'])
1836 except ValueError:
1836 except ValueError:
1837 raise util.Abort(_('limit must be a positive integer'))
1837 raise util.Abort(_('limit must be a positive integer'))
1838 if limit <= 0: raise util.Abort(_('limit must be positive'))
1838 if limit <= 0: raise util.Abort(_('limit must be positive'))
1839 else:
1839 else:
1840 limit = sys.maxint
1840 limit = sys.maxint
1841 count = 0
1841 count = 0
1842
1842
1843 if opts['copies'] and opts['rev']:
1843 if opts['copies'] and opts['rev']:
1844 endrev = max([int(i)
1844 endrev = max(cmdutil.revrange(ui, repo, opts['rev'])) + 1
1845 for i in cmdutil.revrange(ui, repo, opts['rev'])]) + 1
1846 else:
1845 else:
1847 endrev = repo.changelog.count()
1846 endrev = repo.changelog.count()
1848 rcache = {}
1847 rcache = {}
1849 ncache = {}
1848 ncache = {}
1850 dcache = []
1849 dcache = []
1851 def getrenamed(fn, rev, man):
1850 def getrenamed(fn, rev, man):
1852 '''looks up all renames for a file (up to endrev) the first
1851 '''looks up all renames for a file (up to endrev) the first
1853 time the file is given. It indexes on the changerev and only
1852 time the file is given. It indexes on the changerev and only
1854 parses the manifest if linkrev != changerev.
1853 parses the manifest if linkrev != changerev.
1855 Returns rename info for fn at changerev rev.'''
1854 Returns rename info for fn at changerev rev.'''
1856 if fn not in rcache:
1855 if fn not in rcache:
1857 rcache[fn] = {}
1856 rcache[fn] = {}
1858 ncache[fn] = {}
1857 ncache[fn] = {}
1859 fl = repo.file(fn)
1858 fl = repo.file(fn)
1860 for i in xrange(fl.count()):
1859 for i in xrange(fl.count()):
1861 node = fl.node(i)
1860 node = fl.node(i)
1862 lr = fl.linkrev(node)
1861 lr = fl.linkrev(node)
1863 renamed = fl.renamed(node)
1862 renamed = fl.renamed(node)
1864 rcache[fn][lr] = renamed
1863 rcache[fn][lr] = renamed
1865 if renamed:
1864 if renamed:
1866 ncache[fn][node] = renamed
1865 ncache[fn][node] = renamed
1867 if lr >= endrev:
1866 if lr >= endrev:
1868 break
1867 break
1869 if rev in rcache[fn]:
1868 if rev in rcache[fn]:
1870 return rcache[fn][rev]
1869 return rcache[fn][rev]
1871 mr = repo.manifest.rev(man)
1870 mr = repo.manifest.rev(man)
1872 if repo.manifest.parentrevs(mr) != (mr - 1, -1):
1871 if repo.manifest.parentrevs(mr) != (mr - 1, -1):
1873 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1872 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1874 if not dcache or dcache[0] != man:
1873 if not dcache or dcache[0] != man:
1875 dcache[:] = [man, repo.manifest.readdelta(man)]
1874 dcache[:] = [man, repo.manifest.readdelta(man)]
1876 if fn in dcache[1]:
1875 if fn in dcache[1]:
1877 return ncache[fn].get(dcache[1][fn])
1876 return ncache[fn].get(dcache[1][fn])
1878 return None
1877 return None
1879
1878
1880 displayer = show_changeset(ui, repo, opts)
1879 displayer = show_changeset(ui, repo, opts)
1881 for st, rev, fns in changeiter:
1880 for st, rev, fns in changeiter:
1882 if st == 'window':
1881 if st == 'window':
1883 du = dui(ui)
1882 du = dui(ui)
1884 displayer.ui = du
1883 displayer.ui = du
1885 elif st == 'add':
1884 elif st == 'add':
1886 du.bump(rev)
1885 du.bump(rev)
1887 changenode = repo.changelog.node(rev)
1886 changenode = repo.changelog.node(rev)
1888 parents = [p for p in repo.changelog.parents(changenode)
1887 parents = [p for p in repo.changelog.parents(changenode)
1889 if p != nullid]
1888 if p != nullid]
1890 if opts['no_merges'] and len(parents) == 2:
1889 if opts['no_merges'] and len(parents) == 2:
1891 continue
1890 continue
1892 if opts['only_merges'] and len(parents) != 2:
1891 if opts['only_merges'] and len(parents) != 2:
1893 continue
1892 continue
1894
1893
1895 if opts['keyword']:
1894 if opts['keyword']:
1896 changes = getchange(rev)
1895 changes = getchange(rev)
1897 miss = 0
1896 miss = 0
1898 for k in [kw.lower() for kw in opts['keyword']]:
1897 for k in [kw.lower() for kw in opts['keyword']]:
1899 if not (k in changes[1].lower() or
1898 if not (k in changes[1].lower() or
1900 k in changes[4].lower() or
1899 k in changes[4].lower() or
1901 k in " ".join(changes[3][:20]).lower()):
1900 k in " ".join(changes[3][:20]).lower()):
1902 miss = 1
1901 miss = 1
1903 break
1902 break
1904 if miss:
1903 if miss:
1905 continue
1904 continue
1906
1905
1907 br = None
1906 br = None
1908 if opts['branches']:
1907 if opts['branches']:
1909 br = repo.branchlookup([repo.changelog.node(rev)])
1908 br = repo.branchlookup([repo.changelog.node(rev)])
1910
1909
1911 copies = []
1910 copies = []
1912 if opts.get('copies') and rev:
1911 if opts.get('copies') and rev:
1913 mf = getchange(rev)[0]
1912 mf = getchange(rev)[0]
1914 for fn in getchange(rev)[3]:
1913 for fn in getchange(rev)[3]:
1915 rename = getrenamed(fn, rev, mf)
1914 rename = getrenamed(fn, rev, mf)
1916 if rename:
1915 if rename:
1917 copies.append((fn, rename[0]))
1916 copies.append((fn, rename[0]))
1918 displayer.show(rev, brinfo=br, copies=copies)
1917 displayer.show(rev, brinfo=br, copies=copies)
1919 if opts['patch']:
1918 if opts['patch']:
1920 prev = (parents and parents[0]) or nullid
1919 prev = (parents and parents[0]) or nullid
1921 patch.diff(repo, prev, changenode, match=matchfn, fp=du)
1920 patch.diff(repo, prev, changenode, match=matchfn, fp=du)
1922 du.write("\n\n")
1921 du.write("\n\n")
1923 elif st == 'iter':
1922 elif st == 'iter':
1924 if count == limit: break
1923 if count == limit: break
1925 if du.header[rev]:
1924 if du.header[rev]:
1926 for args in du.header[rev]:
1925 for args in du.header[rev]:
1927 ui.write_header(*args)
1926 ui.write_header(*args)
1928 if du.hunk[rev]:
1927 if du.hunk[rev]:
1929 count += 1
1928 count += 1
1930 for args in du.hunk[rev]:
1929 for args in du.hunk[rev]:
1931 ui.write(*args)
1930 ui.write(*args)
1932
1931
1933 def manifest(ui, repo, rev=None):
1932 def manifest(ui, repo, rev=None):
1934 """output the latest or given revision of the project manifest
1933 """output the latest or given revision of the project manifest
1935
1934
1936 Print a list of version controlled files for the given revision.
1935 Print a list of version controlled files for the given revision.
1937
1936
1938 The manifest is the list of files being version controlled. If no revision
1937 The manifest is the list of files being version controlled. If no revision
1939 is given then the tip is used.
1938 is given then the tip is used.
1940 """
1939 """
1941 if rev:
1940 if rev:
1942 try:
1941 try:
1943 # assume all revision numbers are for changesets
1942 # assume all revision numbers are for changesets
1944 n = repo.lookup(rev)
1943 n = repo.lookup(rev)
1945 change = repo.changelog.read(n)
1944 change = repo.changelog.read(n)
1946 n = change[0]
1945 n = change[0]
1947 except hg.RepoError:
1946 except hg.RepoError:
1948 n = repo.manifest.lookup(rev)
1947 n = repo.manifest.lookup(rev)
1949 else:
1948 else:
1950 n = repo.manifest.tip()
1949 n = repo.manifest.tip()
1951 m = repo.manifest.read(n)
1950 m = repo.manifest.read(n)
1952 files = m.keys()
1951 files = m.keys()
1953 files.sort()
1952 files.sort()
1954
1953
1955 for f in files:
1954 for f in files:
1956 ui.write("%40s %3s %s\n" % (hex(m[f]),
1955 ui.write("%40s %3s %s\n" % (hex(m[f]),
1957 m.execf(f) and "755" or "644", f))
1956 m.execf(f) and "755" or "644", f))
1958
1957
1959 def merge(ui, repo, node=None, force=None, branch=None):
1958 def merge(ui, repo, node=None, force=None, branch=None):
1960 """Merge working directory with another revision
1959 """Merge working directory with another revision
1961
1960
1962 Merge the contents of the current working directory and the
1961 Merge the contents of the current working directory and the
1963 requested revision. Files that changed between either parent are
1962 requested revision. Files that changed between either parent are
1964 marked as changed for the next commit and a commit must be
1963 marked as changed for the next commit and a commit must be
1965 performed before any further updates are allowed.
1964 performed before any further updates are allowed.
1966
1965
1967 If no revision is specified, the working directory's parent is a
1966 If no revision is specified, the working directory's parent is a
1968 head revision, and the repository contains exactly one other head,
1967 head revision, and the repository contains exactly one other head,
1969 the other head is merged with by default. Otherwise, an explicit
1968 the other head is merged with by default. Otherwise, an explicit
1970 revision to merge with must be provided.
1969 revision to merge with must be provided.
1971 """
1970 """
1972
1971
1973 if node or branch:
1972 if node or branch:
1974 node = _lookup(repo, node, branch)
1973 node = _lookup(repo, node, branch)
1975 else:
1974 else:
1976 heads = repo.heads()
1975 heads = repo.heads()
1977 if len(heads) > 2:
1976 if len(heads) > 2:
1978 raise util.Abort(_('repo has %d heads - '
1977 raise util.Abort(_('repo has %d heads - '
1979 'please merge with an explicit rev') %
1978 'please merge with an explicit rev') %
1980 len(heads))
1979 len(heads))
1981 if len(heads) == 1:
1980 if len(heads) == 1:
1982 raise util.Abort(_('there is nothing to merge - '
1981 raise util.Abort(_('there is nothing to merge - '
1983 'use "hg update" instead'))
1982 'use "hg update" instead'))
1984 parent = repo.dirstate.parents()[0]
1983 parent = repo.dirstate.parents()[0]
1985 if parent not in heads:
1984 if parent not in heads:
1986 raise util.Abort(_('working dir not at a head rev - '
1985 raise util.Abort(_('working dir not at a head rev - '
1987 'use "hg update" or merge with an explicit rev'))
1986 'use "hg update" or merge with an explicit rev'))
1988 node = parent == heads[0] and heads[-1] or heads[0]
1987 node = parent == heads[0] and heads[-1] or heads[0]
1989 return hg.merge(repo, node, force=force)
1988 return hg.merge(repo, node, force=force)
1990
1989
1991 def outgoing(ui, repo, dest=None, **opts):
1990 def outgoing(ui, repo, dest=None, **opts):
1992 """show changesets not found in destination
1991 """show changesets not found in destination
1993
1992
1994 Show changesets not found in the specified destination repository or
1993 Show changesets not found in the specified destination repository or
1995 the default push location. These are the changesets that would be pushed
1994 the default push location. These are the changesets that would be pushed
1996 if a push was requested.
1995 if a push was requested.
1997
1996
1998 See pull for valid destination format details.
1997 See pull for valid destination format details.
1999 """
1998 """
2000 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1999 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2001 setremoteconfig(ui, opts)
2000 setremoteconfig(ui, opts)
2002 revs = None
2001 revs = None
2003 if opts['rev']:
2002 if opts['rev']:
2004 revs = [repo.lookup(rev) for rev in opts['rev']]
2003 revs = [repo.lookup(rev) for rev in opts['rev']]
2005
2004
2006 other = hg.repository(ui, dest)
2005 other = hg.repository(ui, dest)
2007 o = repo.findoutgoing(other, force=opts['force'])
2006 o = repo.findoutgoing(other, force=opts['force'])
2008 if not o:
2007 if not o:
2009 ui.status(_("no changes found\n"))
2008 ui.status(_("no changes found\n"))
2010 return
2009 return
2011 o = repo.changelog.nodesbetween(o, revs)[0]
2010 o = repo.changelog.nodesbetween(o, revs)[0]
2012 if opts['newest_first']:
2011 if opts['newest_first']:
2013 o.reverse()
2012 o.reverse()
2014 displayer = show_changeset(ui, repo, opts)
2013 displayer = show_changeset(ui, repo, opts)
2015 for n in o:
2014 for n in o:
2016 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2015 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2017 if opts['no_merges'] and len(parents) == 2:
2016 if opts['no_merges'] and len(parents) == 2:
2018 continue
2017 continue
2019 displayer.show(changenode=n)
2018 displayer.show(changenode=n)
2020 if opts['patch']:
2019 if opts['patch']:
2021 prev = (parents and parents[0]) or nullid
2020 prev = (parents and parents[0]) or nullid
2022 patch.diff(repo, prev, n)
2021 patch.diff(repo, prev, n)
2023 ui.write("\n")
2022 ui.write("\n")
2024
2023
2025 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
2024 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
2026 """show the parents of the working dir or revision
2025 """show the parents of the working dir or revision
2027
2026
2028 Print the working directory's parent revisions.
2027 Print the working directory's parent revisions.
2029 """
2028 """
2030 # legacy
2029 # legacy
2031 if file_ and not rev:
2030 if file_ and not rev:
2032 try:
2031 try:
2033 rev = repo.lookup(file_)
2032 rev = repo.lookup(file_)
2034 file_ = None
2033 file_ = None
2035 except hg.RepoError:
2034 except hg.RepoError:
2036 pass
2035 pass
2037 else:
2036 else:
2038 ui.warn(_("'hg parent REV' is deprecated, "
2037 ui.warn(_("'hg parent REV' is deprecated, "
2039 "please use 'hg parents -r REV instead\n"))
2038 "please use 'hg parents -r REV instead\n"))
2040
2039
2041 if rev:
2040 if rev:
2042 if file_:
2041 if file_:
2043 ctx = repo.filectx(file_, changeid=rev)
2042 ctx = repo.filectx(file_, changeid=rev)
2044 else:
2043 else:
2045 ctx = repo.changectx(rev)
2044 ctx = repo.changectx(rev)
2046 p = [cp.node() for cp in ctx.parents()]
2045 p = [cp.node() for cp in ctx.parents()]
2047 else:
2046 else:
2048 p = repo.dirstate.parents()
2047 p = repo.dirstate.parents()
2049
2048
2050 br = None
2049 br = None
2051 if branches is not None:
2050 if branches is not None:
2052 ui.warn(_("the --branches option is deprecated, "
2051 ui.warn(_("the --branches option is deprecated, "
2053 "please use 'hg branches' instead\n"))
2052 "please use 'hg branches' instead\n"))
2054 br = repo.branchlookup(p)
2053 br = repo.branchlookup(p)
2055 displayer = show_changeset(ui, repo, opts)
2054 displayer = show_changeset(ui, repo, opts)
2056 for n in p:
2055 for n in p:
2057 if n != nullid:
2056 if n != nullid:
2058 displayer.show(changenode=n, brinfo=br)
2057 displayer.show(changenode=n, brinfo=br)
2059
2058
2060 def paths(ui, repo, search=None):
2059 def paths(ui, repo, search=None):
2061 """show definition of symbolic path names
2060 """show definition of symbolic path names
2062
2061
2063 Show definition of symbolic path name NAME. If no name is given, show
2062 Show definition of symbolic path name NAME. If no name is given, show
2064 definition of available names.
2063 definition of available names.
2065
2064
2066 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2065 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2067 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2066 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2068 """
2067 """
2069 if search:
2068 if search:
2070 for name, path in ui.configitems("paths"):
2069 for name, path in ui.configitems("paths"):
2071 if name == search:
2070 if name == search:
2072 ui.write("%s\n" % path)
2071 ui.write("%s\n" % path)
2073 return
2072 return
2074 ui.warn(_("not found!\n"))
2073 ui.warn(_("not found!\n"))
2075 return 1
2074 return 1
2076 else:
2075 else:
2077 for name, path in ui.configitems("paths"):
2076 for name, path in ui.configitems("paths"):
2078 ui.write("%s = %s\n" % (name, path))
2077 ui.write("%s = %s\n" % (name, path))
2079
2078
2080 def postincoming(ui, repo, modheads, optupdate):
2079 def postincoming(ui, repo, modheads, optupdate):
2081 if modheads == 0:
2080 if modheads == 0:
2082 return
2081 return
2083 if optupdate:
2082 if optupdate:
2084 if modheads == 1:
2083 if modheads == 1:
2085 return hg.update(repo, repo.changelog.tip()) # update
2084 return hg.update(repo, repo.changelog.tip()) # update
2086 else:
2085 else:
2087 ui.status(_("not updating, since new heads added\n"))
2086 ui.status(_("not updating, since new heads added\n"))
2088 if modheads > 1:
2087 if modheads > 1:
2089 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2088 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2090 else:
2089 else:
2091 ui.status(_("(run 'hg update' to get a working copy)\n"))
2090 ui.status(_("(run 'hg update' to get a working copy)\n"))
2092
2091
2093 def pull(ui, repo, source="default", **opts):
2092 def pull(ui, repo, source="default", **opts):
2094 """pull changes from the specified source
2093 """pull changes from the specified source
2095
2094
2096 Pull changes from a remote repository to a local one.
2095 Pull changes from a remote repository to a local one.
2097
2096
2098 This finds all changes from the repository at the specified path
2097 This finds all changes from the repository at the specified path
2099 or URL and adds them to the local repository. By default, this
2098 or URL and adds them to the local repository. By default, this
2100 does not update the copy of the project in the working directory.
2099 does not update the copy of the project in the working directory.
2101
2100
2102 Valid URLs are of the form:
2101 Valid URLs are of the form:
2103
2102
2104 local/filesystem/path
2103 local/filesystem/path
2105 http://[user@]host[:port]/[path]
2104 http://[user@]host[:port]/[path]
2106 https://[user@]host[:port]/[path]
2105 https://[user@]host[:port]/[path]
2107 ssh://[user@]host[:port]/[path]
2106 ssh://[user@]host[:port]/[path]
2108
2107
2109 Some notes about using SSH with Mercurial:
2108 Some notes about using SSH with Mercurial:
2110 - SSH requires an accessible shell account on the destination machine
2109 - SSH requires an accessible shell account on the destination machine
2111 and a copy of hg in the remote path or specified with as remotecmd.
2110 and a copy of hg in the remote path or specified with as remotecmd.
2112 - path is relative to the remote user's home directory by default.
2111 - path is relative to the remote user's home directory by default.
2113 Use an extra slash at the start of a path to specify an absolute path:
2112 Use an extra slash at the start of a path to specify an absolute path:
2114 ssh://example.com//tmp/repository
2113 ssh://example.com//tmp/repository
2115 - Mercurial doesn't use its own compression via SSH; the right thing
2114 - Mercurial doesn't use its own compression via SSH; the right thing
2116 to do is to configure it in your ~/.ssh/config, e.g.:
2115 to do is to configure it in your ~/.ssh/config, e.g.:
2117 Host *.mylocalnetwork.example.com
2116 Host *.mylocalnetwork.example.com
2118 Compression no
2117 Compression no
2119 Host *
2118 Host *
2120 Compression yes
2119 Compression yes
2121 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2120 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2122 with the --ssh command line option.
2121 with the --ssh command line option.
2123 """
2122 """
2124 source = ui.expandpath(source)
2123 source = ui.expandpath(source)
2125 setremoteconfig(ui, opts)
2124 setremoteconfig(ui, opts)
2126
2125
2127 other = hg.repository(ui, source)
2126 other = hg.repository(ui, source)
2128 ui.status(_('pulling from %s\n') % (source))
2127 ui.status(_('pulling from %s\n') % (source))
2129 revs = None
2128 revs = None
2130 if opts['rev']:
2129 if opts['rev']:
2131 if 'lookup' in other.capabilities:
2130 if 'lookup' in other.capabilities:
2132 revs = [other.lookup(rev) for rev in opts['rev']]
2131 revs = [other.lookup(rev) for rev in opts['rev']]
2133 else:
2132 else:
2134 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
2133 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
2135 raise util.Abort(error)
2134 raise util.Abort(error)
2136 modheads = repo.pull(other, heads=revs, force=opts['force'])
2135 modheads = repo.pull(other, heads=revs, force=opts['force'])
2137 return postincoming(ui, repo, modheads, opts['update'])
2136 return postincoming(ui, repo, modheads, opts['update'])
2138
2137
2139 def push(ui, repo, dest=None, **opts):
2138 def push(ui, repo, dest=None, **opts):
2140 """push changes to the specified destination
2139 """push changes to the specified destination
2141
2140
2142 Push changes from the local repository to the given destination.
2141 Push changes from the local repository to the given destination.
2143
2142
2144 This is the symmetrical operation for pull. It helps to move
2143 This is the symmetrical operation for pull. It helps to move
2145 changes from the current repository to a different one. If the
2144 changes from the current repository to a different one. If the
2146 destination is local this is identical to a pull in that directory
2145 destination is local this is identical to a pull in that directory
2147 from the current one.
2146 from the current one.
2148
2147
2149 By default, push will refuse to run if it detects the result would
2148 By default, push will refuse to run if it detects the result would
2150 increase the number of remote heads. This generally indicates the
2149 increase the number of remote heads. This generally indicates the
2151 the client has forgotten to sync and merge before pushing.
2150 the client has forgotten to sync and merge before pushing.
2152
2151
2153 Valid URLs are of the form:
2152 Valid URLs are of the form:
2154
2153
2155 local/filesystem/path
2154 local/filesystem/path
2156 ssh://[user@]host[:port]/[path]
2155 ssh://[user@]host[:port]/[path]
2157
2156
2158 Look at the help text for the pull command for important details
2157 Look at the help text for the pull command for important details
2159 about ssh:// URLs.
2158 about ssh:// URLs.
2160
2159
2161 Pushing to http:// and https:// URLs is possible, too, if this
2160 Pushing to http:// and https:// URLs is possible, too, if this
2162 feature is enabled on the remote Mercurial server.
2161 feature is enabled on the remote Mercurial server.
2163 """
2162 """
2164 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2163 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2165 setremoteconfig(ui, opts)
2164 setremoteconfig(ui, opts)
2166
2165
2167 other = hg.repository(ui, dest)
2166 other = hg.repository(ui, dest)
2168 ui.status('pushing to %s\n' % (dest))
2167 ui.status('pushing to %s\n' % (dest))
2169 revs = None
2168 revs = None
2170 if opts['rev']:
2169 if opts['rev']:
2171 revs = [repo.lookup(rev) for rev in opts['rev']]
2170 revs = [repo.lookup(rev) for rev in opts['rev']]
2172 r = repo.push(other, opts['force'], revs=revs)
2171 r = repo.push(other, opts['force'], revs=revs)
2173 return r == 0
2172 return r == 0
2174
2173
2175 def rawcommit(ui, repo, *flist, **rc):
2174 def rawcommit(ui, repo, *flist, **rc):
2176 """raw commit interface (DEPRECATED)
2175 """raw commit interface (DEPRECATED)
2177
2176
2178 (DEPRECATED)
2177 (DEPRECATED)
2179 Lowlevel commit, for use in helper scripts.
2178 Lowlevel commit, for use in helper scripts.
2180
2179
2181 This command is not intended to be used by normal users, as it is
2180 This command is not intended to be used by normal users, as it is
2182 primarily useful for importing from other SCMs.
2181 primarily useful for importing from other SCMs.
2183
2182
2184 This command is now deprecated and will be removed in a future
2183 This command is now deprecated and will be removed in a future
2185 release, please use debugsetparents and commit instead.
2184 release, please use debugsetparents and commit instead.
2186 """
2185 """
2187
2186
2188 ui.warn(_("(the rawcommit command is deprecated)\n"))
2187 ui.warn(_("(the rawcommit command is deprecated)\n"))
2189
2188
2190 message = rc['message']
2189 message = rc['message']
2191 if not message and rc['logfile']:
2190 if not message and rc['logfile']:
2192 try:
2191 try:
2193 message = open(rc['logfile']).read()
2192 message = open(rc['logfile']).read()
2194 except IOError:
2193 except IOError:
2195 pass
2194 pass
2196 if not message and not rc['logfile']:
2195 if not message and not rc['logfile']:
2197 raise util.Abort(_("missing commit message"))
2196 raise util.Abort(_("missing commit message"))
2198
2197
2199 files = relpath(repo, list(flist))
2198 files = relpath(repo, list(flist))
2200 if rc['files']:
2199 if rc['files']:
2201 files += open(rc['files']).read().splitlines()
2200 files += open(rc['files']).read().splitlines()
2202
2201
2203 rc['parent'] = map(repo.lookup, rc['parent'])
2202 rc['parent'] = map(repo.lookup, rc['parent'])
2204
2203
2205 try:
2204 try:
2206 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2205 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2207 except ValueError, inst:
2206 except ValueError, inst:
2208 raise util.Abort(str(inst))
2207 raise util.Abort(str(inst))
2209
2208
2210 def recover(ui, repo):
2209 def recover(ui, repo):
2211 """roll back an interrupted transaction
2210 """roll back an interrupted transaction
2212
2211
2213 Recover from an interrupted commit or pull.
2212 Recover from an interrupted commit or pull.
2214
2213
2215 This command tries to fix the repository status after an interrupted
2214 This command tries to fix the repository status after an interrupted
2216 operation. It should only be necessary when Mercurial suggests it.
2215 operation. It should only be necessary when Mercurial suggests it.
2217 """
2216 """
2218 if repo.recover():
2217 if repo.recover():
2219 return hg.verify(repo)
2218 return hg.verify(repo)
2220 return 1
2219 return 1
2221
2220
2222 def remove(ui, repo, *pats, **opts):
2221 def remove(ui, repo, *pats, **opts):
2223 """remove the specified files on the next commit
2222 """remove the specified files on the next commit
2224
2223
2225 Schedule the indicated files for removal from the repository.
2224 Schedule the indicated files for removal from the repository.
2226
2225
2227 This command schedules the files to be removed at the next commit.
2226 This command schedules the files to be removed at the next commit.
2228 This only removes files from the current branch, not from the
2227 This only removes files from the current branch, not from the
2229 entire project history. If the files still exist in the working
2228 entire project history. If the files still exist in the working
2230 directory, they will be deleted from it. If invoked with --after,
2229 directory, they will be deleted from it. If invoked with --after,
2231 files that have been manually deleted are marked as removed.
2230 files that have been manually deleted are marked as removed.
2232
2231
2233 Modified files and added files are not removed by default. To
2232 Modified files and added files are not removed by default. To
2234 remove them, use the -f/--force option.
2233 remove them, use the -f/--force option.
2235 """
2234 """
2236 names = []
2235 names = []
2237 if not opts['after'] and not pats:
2236 if not opts['after'] and not pats:
2238 raise util.Abort(_('no files specified'))
2237 raise util.Abort(_('no files specified'))
2239 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2238 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2240 exact = dict.fromkeys(files)
2239 exact = dict.fromkeys(files)
2241 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2240 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2242 modified, added, removed, deleted, unknown = mardu
2241 modified, added, removed, deleted, unknown = mardu
2243 remove, forget = [], []
2242 remove, forget = [], []
2244 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2243 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2245 reason = None
2244 reason = None
2246 if abs not in deleted and opts['after']:
2245 if abs not in deleted and opts['after']:
2247 reason = _('is still present')
2246 reason = _('is still present')
2248 elif abs in modified and not opts['force']:
2247 elif abs in modified and not opts['force']:
2249 reason = _('is modified (use -f to force removal)')
2248 reason = _('is modified (use -f to force removal)')
2250 elif abs in added:
2249 elif abs in added:
2251 if opts['force']:
2250 if opts['force']:
2252 forget.append(abs)
2251 forget.append(abs)
2253 continue
2252 continue
2254 reason = _('has been marked for add (use -f to force removal)')
2253 reason = _('has been marked for add (use -f to force removal)')
2255 elif abs in unknown:
2254 elif abs in unknown:
2256 reason = _('is not managed')
2255 reason = _('is not managed')
2257 elif abs in removed:
2256 elif abs in removed:
2258 continue
2257 continue
2259 if reason:
2258 if reason:
2260 if exact:
2259 if exact:
2261 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2260 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2262 else:
2261 else:
2263 if ui.verbose or not exact:
2262 if ui.verbose or not exact:
2264 ui.status(_('removing %s\n') % rel)
2263 ui.status(_('removing %s\n') % rel)
2265 remove.append(abs)
2264 remove.append(abs)
2266 repo.forget(forget)
2265 repo.forget(forget)
2267 repo.remove(remove, unlink=not opts['after'])
2266 repo.remove(remove, unlink=not opts['after'])
2268
2267
2269 def rename(ui, repo, *pats, **opts):
2268 def rename(ui, repo, *pats, **opts):
2270 """rename files; equivalent of copy + remove
2269 """rename files; equivalent of copy + remove
2271
2270
2272 Mark dest as copies of sources; mark sources for deletion. If
2271 Mark dest as copies of sources; mark sources for deletion. If
2273 dest is a directory, copies are put in that directory. If dest is
2272 dest is a directory, copies are put in that directory. If dest is
2274 a file, there can only be one source.
2273 a file, there can only be one source.
2275
2274
2276 By default, this command copies the contents of files as they
2275 By default, this command copies the contents of files as they
2277 stand in the working directory. If invoked with --after, the
2276 stand in the working directory. If invoked with --after, the
2278 operation is recorded, but no copying is performed.
2277 operation is recorded, but no copying is performed.
2279
2278
2280 This command takes effect in the next commit.
2279 This command takes effect in the next commit.
2281
2280
2282 NOTE: This command should be treated as experimental. While it
2281 NOTE: This command should be treated as experimental. While it
2283 should properly record rename files, this information is not yet
2282 should properly record rename files, this information is not yet
2284 fully used by merge, nor fully reported by log.
2283 fully used by merge, nor fully reported by log.
2285 """
2284 """
2286 wlock = repo.wlock(0)
2285 wlock = repo.wlock(0)
2287 errs, copied = docopy(ui, repo, pats, opts, wlock)
2286 errs, copied = docopy(ui, repo, pats, opts, wlock)
2288 names = []
2287 names = []
2289 for abs, rel, exact in copied:
2288 for abs, rel, exact in copied:
2290 if ui.verbose or not exact:
2289 if ui.verbose or not exact:
2291 ui.status(_('removing %s\n') % rel)
2290 ui.status(_('removing %s\n') % rel)
2292 names.append(abs)
2291 names.append(abs)
2293 if not opts.get('dry_run'):
2292 if not opts.get('dry_run'):
2294 repo.remove(names, True, wlock)
2293 repo.remove(names, True, wlock)
2295 return errs
2294 return errs
2296
2295
2297 def revert(ui, repo, *pats, **opts):
2296 def revert(ui, repo, *pats, **opts):
2298 """revert files or dirs to their states as of some revision
2297 """revert files or dirs to their states as of some revision
2299
2298
2300 With no revision specified, revert the named files or directories
2299 With no revision specified, revert the named files or directories
2301 to the contents they had in the parent of the working directory.
2300 to the contents they had in the parent of the working directory.
2302 This restores the contents of the affected files to an unmodified
2301 This restores the contents of the affected files to an unmodified
2303 state. If the working directory has two parents, you must
2302 state. If the working directory has two parents, you must
2304 explicitly specify the revision to revert to.
2303 explicitly specify the revision to revert to.
2305
2304
2306 Modified files are saved with a .orig suffix before reverting.
2305 Modified files are saved with a .orig suffix before reverting.
2307 To disable these backups, use --no-backup.
2306 To disable these backups, use --no-backup.
2308
2307
2309 Using the -r option, revert the given files or directories to their
2308 Using the -r option, revert the given files or directories to their
2310 contents as of a specific revision. This can be helpful to "roll
2309 contents as of a specific revision. This can be helpful to "roll
2311 back" some or all of a change that should not have been committed.
2310 back" some or all of a change that should not have been committed.
2312
2311
2313 Revert modifies the working directory. It does not commit any
2312 Revert modifies the working directory. It does not commit any
2314 changes, or change the parent of the working directory. If you
2313 changes, or change the parent of the working directory. If you
2315 revert to a revision other than the parent of the working
2314 revert to a revision other than the parent of the working
2316 directory, the reverted files will thus appear modified
2315 directory, the reverted files will thus appear modified
2317 afterwards.
2316 afterwards.
2318
2317
2319 If a file has been deleted, it is recreated. If the executable
2318 If a file has been deleted, it is recreated. If the executable
2320 mode of a file was changed, it is reset.
2319 mode of a file was changed, it is reset.
2321
2320
2322 If names are given, all files matching the names are reverted.
2321 If names are given, all files matching the names are reverted.
2323
2322
2324 If no arguments are given, no files are reverted.
2323 If no arguments are given, no files are reverted.
2325 """
2324 """
2326
2325
2327 if not pats and not opts['all']:
2326 if not pats and not opts['all']:
2328 raise util.Abort(_('no files or directories specified; '
2327 raise util.Abort(_('no files or directories specified; '
2329 'use --all to revert the whole repo'))
2328 'use --all to revert the whole repo'))
2330
2329
2331 parent, p2 = repo.dirstate.parents()
2330 parent, p2 = repo.dirstate.parents()
2332 if not opts['rev'] and p2 != nullid:
2331 if not opts['rev'] and p2 != nullid:
2333 raise util.Abort(_('uncommitted merge - please provide a '
2332 raise util.Abort(_('uncommitted merge - please provide a '
2334 'specific revision'))
2333 'specific revision'))
2335 node = repo.changectx(opts['rev']).node()
2334 node = repo.changectx(opts['rev']).node()
2336 mf = repo.manifest.read(repo.changelog.read(node)[0])
2335 mf = repo.manifest.read(repo.changelog.read(node)[0])
2337 if node == parent:
2336 if node == parent:
2338 pmf = mf
2337 pmf = mf
2339 else:
2338 else:
2340 pmf = None
2339 pmf = None
2341
2340
2342 wlock = repo.wlock()
2341 wlock = repo.wlock()
2343
2342
2344 # need all matching names in dirstate and manifest of target rev,
2343 # need all matching names in dirstate and manifest of target rev,
2345 # so have to walk both. do not print errors if files exist in one
2344 # so have to walk both. do not print errors if files exist in one
2346 # but not other.
2345 # but not other.
2347
2346
2348 names = {}
2347 names = {}
2349 target_only = {}
2348 target_only = {}
2350
2349
2351 # walk dirstate.
2350 # walk dirstate.
2352
2351
2353 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2352 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2354 badmatch=mf.has_key):
2353 badmatch=mf.has_key):
2355 names[abs] = (rel, exact)
2354 names[abs] = (rel, exact)
2356 if src == 'b':
2355 if src == 'b':
2357 target_only[abs] = True
2356 target_only[abs] = True
2358
2357
2359 # walk target manifest.
2358 # walk target manifest.
2360
2359
2361 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2360 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2362 badmatch=names.has_key):
2361 badmatch=names.has_key):
2363 if abs in names: continue
2362 if abs in names: continue
2364 names[abs] = (rel, exact)
2363 names[abs] = (rel, exact)
2365 target_only[abs] = True
2364 target_only[abs] = True
2366
2365
2367 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2366 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2368 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2367 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2369
2368
2370 revert = ([], _('reverting %s\n'))
2369 revert = ([], _('reverting %s\n'))
2371 add = ([], _('adding %s\n'))
2370 add = ([], _('adding %s\n'))
2372 remove = ([], _('removing %s\n'))
2371 remove = ([], _('removing %s\n'))
2373 forget = ([], _('forgetting %s\n'))
2372 forget = ([], _('forgetting %s\n'))
2374 undelete = ([], _('undeleting %s\n'))
2373 undelete = ([], _('undeleting %s\n'))
2375 update = {}
2374 update = {}
2376
2375
2377 disptable = (
2376 disptable = (
2378 # dispatch table:
2377 # dispatch table:
2379 # file state
2378 # file state
2380 # action if in target manifest
2379 # action if in target manifest
2381 # action if not in target manifest
2380 # action if not in target manifest
2382 # make backup if in target manifest
2381 # make backup if in target manifest
2383 # make backup if not in target manifest
2382 # make backup if not in target manifest
2384 (modified, revert, remove, True, True),
2383 (modified, revert, remove, True, True),
2385 (added, revert, forget, True, False),
2384 (added, revert, forget, True, False),
2386 (removed, undelete, None, False, False),
2385 (removed, undelete, None, False, False),
2387 (deleted, revert, remove, False, False),
2386 (deleted, revert, remove, False, False),
2388 (unknown, add, None, True, False),
2387 (unknown, add, None, True, False),
2389 (target_only, add, None, False, False),
2388 (target_only, add, None, False, False),
2390 )
2389 )
2391
2390
2392 entries = names.items()
2391 entries = names.items()
2393 entries.sort()
2392 entries.sort()
2394
2393
2395 for abs, (rel, exact) in entries:
2394 for abs, (rel, exact) in entries:
2396 mfentry = mf.get(abs)
2395 mfentry = mf.get(abs)
2397 def handle(xlist, dobackup):
2396 def handle(xlist, dobackup):
2398 xlist[0].append(abs)
2397 xlist[0].append(abs)
2399 update[abs] = 1
2398 update[abs] = 1
2400 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2399 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2401 bakname = "%s.orig" % rel
2400 bakname = "%s.orig" % rel
2402 ui.note(_('saving current version of %s as %s\n') %
2401 ui.note(_('saving current version of %s as %s\n') %
2403 (rel, bakname))
2402 (rel, bakname))
2404 if not opts.get('dry_run'):
2403 if not opts.get('dry_run'):
2405 shutil.copyfile(rel, bakname)
2404 shutil.copyfile(rel, bakname)
2406 shutil.copymode(rel, bakname)
2405 shutil.copymode(rel, bakname)
2407 if ui.verbose or not exact:
2406 if ui.verbose or not exact:
2408 ui.status(xlist[1] % rel)
2407 ui.status(xlist[1] % rel)
2409 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2408 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2410 if abs not in table: continue
2409 if abs not in table: continue
2411 # file has changed in dirstate
2410 # file has changed in dirstate
2412 if mfentry:
2411 if mfentry:
2413 handle(hitlist, backuphit)
2412 handle(hitlist, backuphit)
2414 elif misslist is not None:
2413 elif misslist is not None:
2415 handle(misslist, backupmiss)
2414 handle(misslist, backupmiss)
2416 else:
2415 else:
2417 if exact: ui.warn(_('file not managed: %s\n' % rel))
2416 if exact: ui.warn(_('file not managed: %s\n' % rel))
2418 break
2417 break
2419 else:
2418 else:
2420 # file has not changed in dirstate
2419 # file has not changed in dirstate
2421 if node == parent:
2420 if node == parent:
2422 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2421 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2423 continue
2422 continue
2424 if pmf is None:
2423 if pmf is None:
2425 # only need parent manifest in this unlikely case,
2424 # only need parent manifest in this unlikely case,
2426 # so do not read by default
2425 # so do not read by default
2427 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2426 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2428 if abs in pmf:
2427 if abs in pmf:
2429 if mfentry:
2428 if mfentry:
2430 # if version of file is same in parent and target
2429 # if version of file is same in parent and target
2431 # manifests, do nothing
2430 # manifests, do nothing
2432 if pmf[abs] != mfentry:
2431 if pmf[abs] != mfentry:
2433 handle(revert, False)
2432 handle(revert, False)
2434 else:
2433 else:
2435 handle(remove, False)
2434 handle(remove, False)
2436
2435
2437 if not opts.get('dry_run'):
2436 if not opts.get('dry_run'):
2438 repo.dirstate.forget(forget[0])
2437 repo.dirstate.forget(forget[0])
2439 r = hg.revert(repo, node, update.has_key, wlock)
2438 r = hg.revert(repo, node, update.has_key, wlock)
2440 repo.dirstate.update(add[0], 'a')
2439 repo.dirstate.update(add[0], 'a')
2441 repo.dirstate.update(undelete[0], 'n')
2440 repo.dirstate.update(undelete[0], 'n')
2442 repo.dirstate.update(remove[0], 'r')
2441 repo.dirstate.update(remove[0], 'r')
2443 return r
2442 return r
2444
2443
2445 def rollback(ui, repo):
2444 def rollback(ui, repo):
2446 """roll back the last transaction in this repository
2445 """roll back the last transaction in this repository
2447
2446
2448 Roll back the last transaction in this repository, restoring the
2447 Roll back the last transaction in this repository, restoring the
2449 project to its state prior to the transaction.
2448 project to its state prior to the transaction.
2450
2449
2451 Transactions are used to encapsulate the effects of all commands
2450 Transactions are used to encapsulate the effects of all commands
2452 that create new changesets or propagate existing changesets into a
2451 that create new changesets or propagate existing changesets into a
2453 repository. For example, the following commands are transactional,
2452 repository. For example, the following commands are transactional,
2454 and their effects can be rolled back:
2453 and their effects can be rolled back:
2455
2454
2456 commit
2455 commit
2457 import
2456 import
2458 pull
2457 pull
2459 push (with this repository as destination)
2458 push (with this repository as destination)
2460 unbundle
2459 unbundle
2461
2460
2462 This command should be used with care. There is only one level of
2461 This command should be used with care. There is only one level of
2463 rollback, and there is no way to undo a rollback.
2462 rollback, and there is no way to undo a rollback.
2464
2463
2465 This command is not intended for use on public repositories. Once
2464 This command is not intended for use on public repositories. Once
2466 changes are visible for pull by other users, rolling a transaction
2465 changes are visible for pull by other users, rolling a transaction
2467 back locally is ineffective (someone else may already have pulled
2466 back locally is ineffective (someone else may already have pulled
2468 the changes). Furthermore, a race is possible with readers of the
2467 the changes). Furthermore, a race is possible with readers of the
2469 repository; for example an in-progress pull from the repository
2468 repository; for example an in-progress pull from the repository
2470 may fail if a rollback is performed.
2469 may fail if a rollback is performed.
2471 """
2470 """
2472 repo.rollback()
2471 repo.rollback()
2473
2472
2474 def root(ui, repo):
2473 def root(ui, repo):
2475 """print the root (top) of the current working dir
2474 """print the root (top) of the current working dir
2476
2475
2477 Print the root directory of the current repository.
2476 Print the root directory of the current repository.
2478 """
2477 """
2479 ui.write(repo.root + "\n")
2478 ui.write(repo.root + "\n")
2480
2479
2481 def serve(ui, repo, **opts):
2480 def serve(ui, repo, **opts):
2482 """export the repository via HTTP
2481 """export the repository via HTTP
2483
2482
2484 Start a local HTTP repository browser and pull server.
2483 Start a local HTTP repository browser and pull server.
2485
2484
2486 By default, the server logs accesses to stdout and errors to
2485 By default, the server logs accesses to stdout and errors to
2487 stderr. Use the "-A" and "-E" options to log to files.
2486 stderr. Use the "-A" and "-E" options to log to files.
2488 """
2487 """
2489
2488
2490 if opts["stdio"]:
2489 if opts["stdio"]:
2491 if repo is None:
2490 if repo is None:
2492 raise hg.RepoError(_("There is no Mercurial repository here"
2491 raise hg.RepoError(_("There is no Mercurial repository here"
2493 " (.hg not found)"))
2492 " (.hg not found)"))
2494 s = sshserver.sshserver(ui, repo)
2493 s = sshserver.sshserver(ui, repo)
2495 s.serve_forever()
2494 s.serve_forever()
2496
2495
2497 optlist = ("name templates style address port ipv6"
2496 optlist = ("name templates style address port ipv6"
2498 " accesslog errorlog webdir_conf")
2497 " accesslog errorlog webdir_conf")
2499 for o in optlist.split():
2498 for o in optlist.split():
2500 if opts[o]:
2499 if opts[o]:
2501 ui.setconfig("web", o, str(opts[o]))
2500 ui.setconfig("web", o, str(opts[o]))
2502
2501
2503 if repo is None and not ui.config("web", "webdir_conf"):
2502 if repo is None and not ui.config("web", "webdir_conf"):
2504 raise hg.RepoError(_("There is no Mercurial repository here"
2503 raise hg.RepoError(_("There is no Mercurial repository here"
2505 " (.hg not found)"))
2504 " (.hg not found)"))
2506
2505
2507 if opts['daemon'] and not opts['daemon_pipefds']:
2506 if opts['daemon'] and not opts['daemon_pipefds']:
2508 rfd, wfd = os.pipe()
2507 rfd, wfd = os.pipe()
2509 args = sys.argv[:]
2508 args = sys.argv[:]
2510 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2509 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2511 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2510 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2512 args[0], args)
2511 args[0], args)
2513 os.close(wfd)
2512 os.close(wfd)
2514 os.read(rfd, 1)
2513 os.read(rfd, 1)
2515 os._exit(0)
2514 os._exit(0)
2516
2515
2517 try:
2516 try:
2518 httpd = hgweb.server.create_server(ui, repo)
2517 httpd = hgweb.server.create_server(ui, repo)
2519 except socket.error, inst:
2518 except socket.error, inst:
2520 raise util.Abort(_('cannot start server: %s') % inst.args[1])
2519 raise util.Abort(_('cannot start server: %s') % inst.args[1])
2521
2520
2522 if ui.verbose:
2521 if ui.verbose:
2523 addr, port = httpd.socket.getsockname()
2522 addr, port = httpd.socket.getsockname()
2524 if addr == '0.0.0.0':
2523 if addr == '0.0.0.0':
2525 addr = socket.gethostname()
2524 addr = socket.gethostname()
2526 else:
2525 else:
2527 try:
2526 try:
2528 addr = socket.gethostbyaddr(addr)[0]
2527 addr = socket.gethostbyaddr(addr)[0]
2529 except socket.error:
2528 except socket.error:
2530 pass
2529 pass
2531 if port != 80:
2530 if port != 80:
2532 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2531 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2533 else:
2532 else:
2534 ui.status(_('listening at http://%s/\n') % addr)
2533 ui.status(_('listening at http://%s/\n') % addr)
2535
2534
2536 if opts['pid_file']:
2535 if opts['pid_file']:
2537 fp = open(opts['pid_file'], 'w')
2536 fp = open(opts['pid_file'], 'w')
2538 fp.write(str(os.getpid()) + '\n')
2537 fp.write(str(os.getpid()) + '\n')
2539 fp.close()
2538 fp.close()
2540
2539
2541 if opts['daemon_pipefds']:
2540 if opts['daemon_pipefds']:
2542 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2541 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2543 os.close(rfd)
2542 os.close(rfd)
2544 os.write(wfd, 'y')
2543 os.write(wfd, 'y')
2545 os.close(wfd)
2544 os.close(wfd)
2546 sys.stdout.flush()
2545 sys.stdout.flush()
2547 sys.stderr.flush()
2546 sys.stderr.flush()
2548 fd = os.open(util.nulldev, os.O_RDWR)
2547 fd = os.open(util.nulldev, os.O_RDWR)
2549 if fd != 0: os.dup2(fd, 0)
2548 if fd != 0: os.dup2(fd, 0)
2550 if fd != 1: os.dup2(fd, 1)
2549 if fd != 1: os.dup2(fd, 1)
2551 if fd != 2: os.dup2(fd, 2)
2550 if fd != 2: os.dup2(fd, 2)
2552 if fd not in (0, 1, 2): os.close(fd)
2551 if fd not in (0, 1, 2): os.close(fd)
2553
2552
2554 httpd.serve_forever()
2553 httpd.serve_forever()
2555
2554
2556 def status(ui, repo, *pats, **opts):
2555 def status(ui, repo, *pats, **opts):
2557 """show changed files in the working directory
2556 """show changed files in the working directory
2558
2557
2559 Show status of files in the repository. If names are given, only
2558 Show status of files in the repository. If names are given, only
2560 files that match are shown. Files that are clean or ignored, are
2559 files that match are shown. Files that are clean or ignored, are
2561 not listed unless -c (clean), -i (ignored) or -A is given.
2560 not listed unless -c (clean), -i (ignored) or -A is given.
2562
2561
2563 If one revision is given, it is used as the base revision.
2562 If one revision is given, it is used as the base revision.
2564 If two revisions are given, the difference between them is shown.
2563 If two revisions are given, the difference between them is shown.
2565
2564
2566 The codes used to show the status of files are:
2565 The codes used to show the status of files are:
2567 M = modified
2566 M = modified
2568 A = added
2567 A = added
2569 R = removed
2568 R = removed
2570 C = clean
2569 C = clean
2571 ! = deleted, but still tracked
2570 ! = deleted, but still tracked
2572 ? = not tracked
2571 ? = not tracked
2573 I = ignored (not shown by default)
2572 I = ignored (not shown by default)
2574 = the previous added file was copied from here
2573 = the previous added file was copied from here
2575 """
2574 """
2576
2575
2577 all = opts['all']
2576 all = opts['all']
2578 node1, node2 = cmdutil.revpair(ui, repo, opts.get('rev'))
2577 node1, node2 = cmdutil.revpair(ui, repo, opts.get('rev'))
2579
2578
2580 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2579 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2581 cwd = (pats and repo.getcwd()) or ''
2580 cwd = (pats and repo.getcwd()) or ''
2582 modified, added, removed, deleted, unknown, ignored, clean = [
2581 modified, added, removed, deleted, unknown, ignored, clean = [
2583 [util.pathto(cwd, x) for x in n]
2582 [util.pathto(cwd, x) for x in n]
2584 for n in repo.status(node1=node1, node2=node2, files=files,
2583 for n in repo.status(node1=node1, node2=node2, files=files,
2585 match=matchfn,
2584 match=matchfn,
2586 list_ignored=all or opts['ignored'],
2585 list_ignored=all or opts['ignored'],
2587 list_clean=all or opts['clean'])]
2586 list_clean=all or opts['clean'])]
2588
2587
2589 changetypes = (('modified', 'M', modified),
2588 changetypes = (('modified', 'M', modified),
2590 ('added', 'A', added),
2589 ('added', 'A', added),
2591 ('removed', 'R', removed),
2590 ('removed', 'R', removed),
2592 ('deleted', '!', deleted),
2591 ('deleted', '!', deleted),
2593 ('unknown', '?', unknown),
2592 ('unknown', '?', unknown),
2594 ('ignored', 'I', ignored))
2593 ('ignored', 'I', ignored))
2595
2594
2596 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2595 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2597
2596
2598 end = opts['print0'] and '\0' or '\n'
2597 end = opts['print0'] and '\0' or '\n'
2599
2598
2600 for opt, char, changes in ([ct for ct in explicit_changetypes
2599 for opt, char, changes in ([ct for ct in explicit_changetypes
2601 if all or opts[ct[0]]]
2600 if all or opts[ct[0]]]
2602 or changetypes):
2601 or changetypes):
2603 if opts['no_status']:
2602 if opts['no_status']:
2604 format = "%%s%s" % end
2603 format = "%%s%s" % end
2605 else:
2604 else:
2606 format = "%s %%s%s" % (char, end)
2605 format = "%s %%s%s" % (char, end)
2607
2606
2608 for f in changes:
2607 for f in changes:
2609 ui.write(format % f)
2608 ui.write(format % f)
2610 if ((all or opts.get('copies')) and not opts.get('no_status')):
2609 if ((all or opts.get('copies')) and not opts.get('no_status')):
2611 copied = repo.dirstate.copied(f)
2610 copied = repo.dirstate.copied(f)
2612 if copied:
2611 if copied:
2613 ui.write(' %s%s' % (copied, end))
2612 ui.write(' %s%s' % (copied, end))
2614
2613
2615 def tag(ui, repo, name, rev_=None, **opts):
2614 def tag(ui, repo, name, rev_=None, **opts):
2616 """add a tag for the current tip or a given revision
2615 """add a tag for the current tip or a given revision
2617
2616
2618 Name a particular revision using <name>.
2617 Name a particular revision using <name>.
2619
2618
2620 Tags are used to name particular revisions of the repository and are
2619 Tags are used to name particular revisions of the repository and are
2621 very useful to compare different revision, to go back to significant
2620 very useful to compare different revision, to go back to significant
2622 earlier versions or to mark branch points as releases, etc.
2621 earlier versions or to mark branch points as releases, etc.
2623
2622
2624 If no revision is given, the parent of the working directory is used.
2623 If no revision is given, the parent of the working directory is used.
2625
2624
2626 To facilitate version control, distribution, and merging of tags,
2625 To facilitate version control, distribution, and merging of tags,
2627 they are stored as a file named ".hgtags" which is managed
2626 they are stored as a file named ".hgtags" which is managed
2628 similarly to other project files and can be hand-edited if
2627 similarly to other project files and can be hand-edited if
2629 necessary. The file '.hg/localtags' is used for local tags (not
2628 necessary. The file '.hg/localtags' is used for local tags (not
2630 shared among repositories).
2629 shared among repositories).
2631 """
2630 """
2632 if name in ['tip', '.']:
2631 if name in ['tip', '.']:
2633 raise util.Abort(_("the name '%s' is reserved") % name)
2632 raise util.Abort(_("the name '%s' is reserved") % name)
2634 if rev_ is not None:
2633 if rev_ is not None:
2635 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2634 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2636 "please use 'hg tag [-r REV] NAME' instead\n"))
2635 "please use 'hg tag [-r REV] NAME' instead\n"))
2637 if opts['rev']:
2636 if opts['rev']:
2638 raise util.Abort(_("use only one form to specify the revision"))
2637 raise util.Abort(_("use only one form to specify the revision"))
2639 if opts['rev']:
2638 if opts['rev']:
2640 rev_ = opts['rev']
2639 rev_ = opts['rev']
2641 if not rev_ and repo.dirstate.parents()[1] != nullid:
2640 if not rev_ and repo.dirstate.parents()[1] != nullid:
2642 raise util.Abort(_('uncommitted merge - please provide a '
2641 raise util.Abort(_('uncommitted merge - please provide a '
2643 'specific revision'))
2642 'specific revision'))
2644 r = repo.changectx(rev_).node()
2643 r = repo.changectx(rev_).node()
2645
2644
2646 message = opts['message']
2645 message = opts['message']
2647 if not message:
2646 if not message:
2648 message = _('Added tag %s for changeset %s') % (name, short(r))
2647 message = _('Added tag %s for changeset %s') % (name, short(r))
2649
2648
2650 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2649 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2651
2650
2652 def tags(ui, repo):
2651 def tags(ui, repo):
2653 """list repository tags
2652 """list repository tags
2654
2653
2655 List the repository tags.
2654 List the repository tags.
2656
2655
2657 This lists both regular and local tags.
2656 This lists both regular and local tags.
2658 """
2657 """
2659
2658
2660 l = repo.tagslist()
2659 l = repo.tagslist()
2661 l.reverse()
2660 l.reverse()
2662 hexfunc = ui.debugflag and hex or short
2661 hexfunc = ui.debugflag and hex or short
2663 for t, n in l:
2662 for t, n in l:
2664 try:
2663 try:
2665 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2664 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2666 except KeyError:
2665 except KeyError:
2667 r = " ?:?"
2666 r = " ?:?"
2668 if ui.quiet:
2667 if ui.quiet:
2669 ui.write("%s\n" % t)
2668 ui.write("%s\n" % t)
2670 else:
2669 else:
2671 ui.write("%-30s %s\n" % (t, r))
2670 ui.write("%-30s %s\n" % (t, r))
2672
2671
2673 def tip(ui, repo, **opts):
2672 def tip(ui, repo, **opts):
2674 """show the tip revision
2673 """show the tip revision
2675
2674
2676 Show the tip revision.
2675 Show the tip revision.
2677 """
2676 """
2678 n = repo.changelog.tip()
2677 n = repo.changelog.tip()
2679 br = None
2678 br = None
2680 if opts['branches']:
2679 if opts['branches']:
2681 ui.warn(_("the --branches option is deprecated, "
2680 ui.warn(_("the --branches option is deprecated, "
2682 "please use 'hg branches' instead\n"))
2681 "please use 'hg branches' instead\n"))
2683 br = repo.branchlookup([n])
2682 br = repo.branchlookup([n])
2684 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2683 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2685 if opts['patch']:
2684 if opts['patch']:
2686 patch.diff(repo, repo.changelog.parents(n)[0], n)
2685 patch.diff(repo, repo.changelog.parents(n)[0], n)
2687
2686
2688 def unbundle(ui, repo, fname, **opts):
2687 def unbundle(ui, repo, fname, **opts):
2689 """apply a changegroup file
2688 """apply a changegroup file
2690
2689
2691 Apply a compressed changegroup file generated by the bundle
2690 Apply a compressed changegroup file generated by the bundle
2692 command.
2691 command.
2693 """
2692 """
2694 f = urllib.urlopen(fname)
2693 f = urllib.urlopen(fname)
2695
2694
2696 header = f.read(6)
2695 header = f.read(6)
2697 if not header.startswith("HG"):
2696 if not header.startswith("HG"):
2698 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2697 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2699 elif not header.startswith("HG10"):
2698 elif not header.startswith("HG10"):
2700 raise util.Abort(_("%s: unknown bundle version") % fname)
2699 raise util.Abort(_("%s: unknown bundle version") % fname)
2701 elif header == "HG10BZ":
2700 elif header == "HG10BZ":
2702 def generator(f):
2701 def generator(f):
2703 zd = bz2.BZ2Decompressor()
2702 zd = bz2.BZ2Decompressor()
2704 zd.decompress("BZ")
2703 zd.decompress("BZ")
2705 for chunk in f:
2704 for chunk in f:
2706 yield zd.decompress(chunk)
2705 yield zd.decompress(chunk)
2707 elif header == "HG10UN":
2706 elif header == "HG10UN":
2708 def generator(f):
2707 def generator(f):
2709 for chunk in f:
2708 for chunk in f:
2710 yield chunk
2709 yield chunk
2711 else:
2710 else:
2712 raise util.Abort(_("%s: unknown bundle compression type")
2711 raise util.Abort(_("%s: unknown bundle compression type")
2713 % fname)
2712 % fname)
2714 gen = generator(util.filechunkiter(f, 4096))
2713 gen = generator(util.filechunkiter(f, 4096))
2715 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle',
2714 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle',
2716 'bundle:' + fname)
2715 'bundle:' + fname)
2717 return postincoming(ui, repo, modheads, opts['update'])
2716 return postincoming(ui, repo, modheads, opts['update'])
2718
2717
2719 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2718 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2720 branch=None):
2719 branch=None):
2721 """update or merge working directory
2720 """update or merge working directory
2722
2721
2723 Update the working directory to the specified revision.
2722 Update the working directory to the specified revision.
2724
2723
2725 If there are no outstanding changes in the working directory and
2724 If there are no outstanding changes in the working directory and
2726 there is a linear relationship between the current version and the
2725 there is a linear relationship between the current version and the
2727 requested version, the result is the requested version.
2726 requested version, the result is the requested version.
2728
2727
2729 To merge the working directory with another revision, use the
2728 To merge the working directory with another revision, use the
2730 merge command.
2729 merge command.
2731
2730
2732 By default, update will refuse to run if doing so would require
2731 By default, update will refuse to run if doing so would require
2733 merging or discarding local changes.
2732 merging or discarding local changes.
2734 """
2733 """
2735 node = _lookup(repo, node, branch)
2734 node = _lookup(repo, node, branch)
2736 if clean:
2735 if clean:
2737 return hg.clean(repo, node)
2736 return hg.clean(repo, node)
2738 else:
2737 else:
2739 return hg.update(repo, node)
2738 return hg.update(repo, node)
2740
2739
2741 def _lookup(repo, node, branch=None):
2740 def _lookup(repo, node, branch=None):
2742 if branch:
2741 if branch:
2743 repo.ui.warn(_("the --branch option is deprecated, "
2742 repo.ui.warn(_("the --branch option is deprecated, "
2744 "please use 'hg branch' instead\n"))
2743 "please use 'hg branch' instead\n"))
2745 br = repo.branchlookup(branch=branch)
2744 br = repo.branchlookup(branch=branch)
2746 found = []
2745 found = []
2747 for x in br:
2746 for x in br:
2748 if branch in br[x]:
2747 if branch in br[x]:
2749 found.append(x)
2748 found.append(x)
2750 if len(found) > 1:
2749 if len(found) > 1:
2751 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2750 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2752 for x in found:
2751 for x in found:
2753 show_changeset(ui, repo, {}).show(changenode=x, brinfo=br)
2752 show_changeset(ui, repo, {}).show(changenode=x, brinfo=br)
2754 raise util.Abort("")
2753 raise util.Abort("")
2755 if len(found) == 1:
2754 if len(found) == 1:
2756 node = found[0]
2755 node = found[0]
2757 repo.ui.warn(_("Using head %s for branch %s\n")
2756 repo.ui.warn(_("Using head %s for branch %s\n")
2758 % (short(node), branch))
2757 % (short(node), branch))
2759 else:
2758 else:
2760 raise util.Abort(_("branch %s not found") % branch)
2759 raise util.Abort(_("branch %s not found") % branch)
2761 else:
2760 else:
2762 node = node and repo.lookup(node) or repo.changelog.tip()
2761 node = node and repo.lookup(node) or repo.changelog.tip()
2763 return node
2762 return node
2764
2763
2765 def verify(ui, repo):
2764 def verify(ui, repo):
2766 """verify the integrity of the repository
2765 """verify the integrity of the repository
2767
2766
2768 Verify the integrity of the current repository.
2767 Verify the integrity of the current repository.
2769
2768
2770 This will perform an extensive check of the repository's
2769 This will perform an extensive check of the repository's
2771 integrity, validating the hashes and checksums of each entry in
2770 integrity, validating the hashes and checksums of each entry in
2772 the changelog, manifest, and tracked files, as well as the
2771 the changelog, manifest, and tracked files, as well as the
2773 integrity of their crosslinks and indices.
2772 integrity of their crosslinks and indices.
2774 """
2773 """
2775 return hg.verify(repo)
2774 return hg.verify(repo)
2776
2775
2777 # Command options and aliases are listed here, alphabetically
2776 # Command options and aliases are listed here, alphabetically
2778
2777
2779 globalopts = [
2778 globalopts = [
2780 ('R', 'repository', '',
2779 ('R', 'repository', '',
2781 _('repository root directory or symbolic path name')),
2780 _('repository root directory or symbolic path name')),
2782 ('', 'cwd', '', _('change working directory')),
2781 ('', 'cwd', '', _('change working directory')),
2783 ('y', 'noninteractive', None,
2782 ('y', 'noninteractive', None,
2784 _('do not prompt, assume \'yes\' for any required answers')),
2783 _('do not prompt, assume \'yes\' for any required answers')),
2785 ('q', 'quiet', None, _('suppress output')),
2784 ('q', 'quiet', None, _('suppress output')),
2786 ('v', 'verbose', None, _('enable additional output')),
2785 ('v', 'verbose', None, _('enable additional output')),
2787 ('', 'config', [], _('set/override config option')),
2786 ('', 'config', [], _('set/override config option')),
2788 ('', 'debug', None, _('enable debugging output')),
2787 ('', 'debug', None, _('enable debugging output')),
2789 ('', 'debugger', None, _('start debugger')),
2788 ('', 'debugger', None, _('start debugger')),
2790 ('', 'lsprof', None, _('print improved command execution profile')),
2789 ('', 'lsprof', None, _('print improved command execution profile')),
2791 ('', 'traceback', None, _('print traceback on exception')),
2790 ('', 'traceback', None, _('print traceback on exception')),
2792 ('', 'time', None, _('time how long the command takes')),
2791 ('', 'time', None, _('time how long the command takes')),
2793 ('', 'profile', None, _('print command execution profile')),
2792 ('', 'profile', None, _('print command execution profile')),
2794 ('', 'version', None, _('output version information and exit')),
2793 ('', 'version', None, _('output version information and exit')),
2795 ('h', 'help', None, _('display help and exit')),
2794 ('h', 'help', None, _('display help and exit')),
2796 ]
2795 ]
2797
2796
2798 dryrunopts = [('n', 'dry-run', None,
2797 dryrunopts = [('n', 'dry-run', None,
2799 _('do not perform actions, just print output'))]
2798 _('do not perform actions, just print output'))]
2800
2799
2801 remoteopts = [
2800 remoteopts = [
2802 ('e', 'ssh', '', _('specify ssh command to use')),
2801 ('e', 'ssh', '', _('specify ssh command to use')),
2803 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2802 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2804 ]
2803 ]
2805
2804
2806 walkopts = [
2805 walkopts = [
2807 ('I', 'include', [], _('include names matching the given patterns')),
2806 ('I', 'include', [], _('include names matching the given patterns')),
2808 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2807 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2809 ]
2808 ]
2810
2809
2811 table = {
2810 table = {
2812 "^add":
2811 "^add":
2813 (add,
2812 (add,
2814 walkopts + dryrunopts,
2813 walkopts + dryrunopts,
2815 _('hg add [OPTION]... [FILE]...')),
2814 _('hg add [OPTION]... [FILE]...')),
2816 "addremove":
2815 "addremove":
2817 (addremove,
2816 (addremove,
2818 [('s', 'similarity', '',
2817 [('s', 'similarity', '',
2819 _('guess renamed files by similarity (0<=s<=100)')),
2818 _('guess renamed files by similarity (0<=s<=100)')),
2820 ] + walkopts + dryrunopts,
2819 ] + walkopts + dryrunopts,
2821 _('hg addremove [OPTION]... [FILE]...')),
2820 _('hg addremove [OPTION]... [FILE]...')),
2822 "^annotate":
2821 "^annotate":
2823 (annotate,
2822 (annotate,
2824 [('r', 'rev', '', _('annotate the specified revision')),
2823 [('r', 'rev', '', _('annotate the specified revision')),
2825 ('f', 'follow', None, _('follow file copies and renames')),
2824 ('f', 'follow', None, _('follow file copies and renames')),
2826 ('a', 'text', None, _('treat all files as text')),
2825 ('a', 'text', None, _('treat all files as text')),
2827 ('u', 'user', None, _('list the author')),
2826 ('u', 'user', None, _('list the author')),
2828 ('d', 'date', None, _('list the date')),
2827 ('d', 'date', None, _('list the date')),
2829 ('n', 'number', None, _('list the revision number (default)')),
2828 ('n', 'number', None, _('list the revision number (default)')),
2830 ('c', 'changeset', None, _('list the changeset')),
2829 ('c', 'changeset', None, _('list the changeset')),
2831 ] + walkopts,
2830 ] + walkopts,
2832 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2831 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2833 "archive":
2832 "archive":
2834 (archive,
2833 (archive,
2835 [('', 'no-decode', None, _('do not pass files through decoders')),
2834 [('', 'no-decode', None, _('do not pass files through decoders')),
2836 ('p', 'prefix', '', _('directory prefix for files in archive')),
2835 ('p', 'prefix', '', _('directory prefix for files in archive')),
2837 ('r', 'rev', '', _('revision to distribute')),
2836 ('r', 'rev', '', _('revision to distribute')),
2838 ('t', 'type', '', _('type of distribution to create')),
2837 ('t', 'type', '', _('type of distribution to create')),
2839 ] + walkopts,
2838 ] + walkopts,
2840 _('hg archive [OPTION]... DEST')),
2839 _('hg archive [OPTION]... DEST')),
2841 "backout":
2840 "backout":
2842 (backout,
2841 (backout,
2843 [('', 'merge', None,
2842 [('', 'merge', None,
2844 _('merge with old dirstate parent after backout')),
2843 _('merge with old dirstate parent after backout')),
2845 ('m', 'message', '', _('use <text> as commit message')),
2844 ('m', 'message', '', _('use <text> as commit message')),
2846 ('l', 'logfile', '', _('read commit message from <file>')),
2845 ('l', 'logfile', '', _('read commit message from <file>')),
2847 ('d', 'date', '', _('record datecode as commit date')),
2846 ('d', 'date', '', _('record datecode as commit date')),
2848 ('', 'parent', '', _('parent to choose when backing out merge')),
2847 ('', 'parent', '', _('parent to choose when backing out merge')),
2849 ('u', 'user', '', _('record user as committer')),
2848 ('u', 'user', '', _('record user as committer')),
2850 ] + walkopts,
2849 ] + walkopts,
2851 _('hg backout [OPTION]... REV')),
2850 _('hg backout [OPTION]... REV')),
2852 "branch": (branch, [], _('hg branch [NAME]')),
2851 "branch": (branch, [], _('hg branch [NAME]')),
2853 "branches": (branches, [], _('hg branches')),
2852 "branches": (branches, [], _('hg branches')),
2854 "bundle":
2853 "bundle":
2855 (bundle,
2854 (bundle,
2856 [('f', 'force', None,
2855 [('f', 'force', None,
2857 _('run even when remote repository is unrelated')),
2856 _('run even when remote repository is unrelated')),
2858 ('r', 'rev', [],
2857 ('r', 'rev', [],
2859 _('a changeset you would like to bundle')),
2858 _('a changeset you would like to bundle')),
2860 ('', 'base', [],
2859 ('', 'base', [],
2861 _('a base changeset to specify instead of a destination')),
2860 _('a base changeset to specify instead of a destination')),
2862 ] + remoteopts,
2861 ] + remoteopts,
2863 _('hg bundle [--base REV]... [--rev REV]... FILE [DEST]')),
2862 _('hg bundle [--base REV]... [--rev REV]... FILE [DEST]')),
2864 "cat":
2863 "cat":
2865 (cat,
2864 (cat,
2866 [('o', 'output', '', _('print output to file with formatted name')),
2865 [('o', 'output', '', _('print output to file with formatted name')),
2867 ('r', 'rev', '', _('print the given revision')),
2866 ('r', 'rev', '', _('print the given revision')),
2868 ] + walkopts,
2867 ] + walkopts,
2869 _('hg cat [OPTION]... FILE...')),
2868 _('hg cat [OPTION]... FILE...')),
2870 "^clone":
2869 "^clone":
2871 (clone,
2870 (clone,
2872 [('U', 'noupdate', None, _('do not update the new working directory')),
2871 [('U', 'noupdate', None, _('do not update the new working directory')),
2873 ('r', 'rev', [],
2872 ('r', 'rev', [],
2874 _('a changeset you would like to have after cloning')),
2873 _('a changeset you would like to have after cloning')),
2875 ('', 'pull', None, _('use pull protocol to copy metadata')),
2874 ('', 'pull', None, _('use pull protocol to copy metadata')),
2876 ('', 'uncompressed', None,
2875 ('', 'uncompressed', None,
2877 _('use uncompressed transfer (fast over LAN)')),
2876 _('use uncompressed transfer (fast over LAN)')),
2878 ] + remoteopts,
2877 ] + remoteopts,
2879 _('hg clone [OPTION]... SOURCE [DEST]')),
2878 _('hg clone [OPTION]... SOURCE [DEST]')),
2880 "^commit|ci":
2879 "^commit|ci":
2881 (commit,
2880 (commit,
2882 [('A', 'addremove', None,
2881 [('A', 'addremove', None,
2883 _('mark new/missing files as added/removed before committing')),
2882 _('mark new/missing files as added/removed before committing')),
2884 ('m', 'message', '', _('use <text> as commit message')),
2883 ('m', 'message', '', _('use <text> as commit message')),
2885 ('l', 'logfile', '', _('read the commit message from <file>')),
2884 ('l', 'logfile', '', _('read the commit message from <file>')),
2886 ('d', 'date', '', _('record datecode as commit date')),
2885 ('d', 'date', '', _('record datecode as commit date')),
2887 ('u', 'user', '', _('record user as commiter')),
2886 ('u', 'user', '', _('record user as commiter')),
2888 ] + walkopts,
2887 ] + walkopts,
2889 _('hg commit [OPTION]... [FILE]...')),
2888 _('hg commit [OPTION]... [FILE]...')),
2890 "copy|cp":
2889 "copy|cp":
2891 (copy,
2890 (copy,
2892 [('A', 'after', None, _('record a copy that has already occurred')),
2891 [('A', 'after', None, _('record a copy that has already occurred')),
2893 ('f', 'force', None,
2892 ('f', 'force', None,
2894 _('forcibly copy over an existing managed file')),
2893 _('forcibly copy over an existing managed file')),
2895 ] + walkopts + dryrunopts,
2894 ] + walkopts + dryrunopts,
2896 _('hg copy [OPTION]... [SOURCE]... DEST')),
2895 _('hg copy [OPTION]... [SOURCE]... DEST')),
2897 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2896 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2898 "debugcomplete":
2897 "debugcomplete":
2899 (debugcomplete,
2898 (debugcomplete,
2900 [('o', 'options', None, _('show the command options'))],
2899 [('o', 'options', None, _('show the command options'))],
2901 _('debugcomplete [-o] CMD')),
2900 _('debugcomplete [-o] CMD')),
2902 "debugrebuildstate":
2901 "debugrebuildstate":
2903 (debugrebuildstate,
2902 (debugrebuildstate,
2904 [('r', 'rev', '', _('revision to rebuild to'))],
2903 [('r', 'rev', '', _('revision to rebuild to'))],
2905 _('debugrebuildstate [-r REV] [REV]')),
2904 _('debugrebuildstate [-r REV] [REV]')),
2906 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2905 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2907 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2906 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2908 "debugstate": (debugstate, [], _('debugstate')),
2907 "debugstate": (debugstate, [], _('debugstate')),
2909 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2908 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2910 "debugindex": (debugindex, [], _('debugindex FILE')),
2909 "debugindex": (debugindex, [], _('debugindex FILE')),
2911 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2910 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2912 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2911 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2913 "debugwalk":
2912 "debugwalk":
2914 (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2913 (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2915 "^diff":
2914 "^diff":
2916 (diff,
2915 (diff,
2917 [('r', 'rev', [], _('revision')),
2916 [('r', 'rev', [], _('revision')),
2918 ('a', 'text', None, _('treat all files as text')),
2917 ('a', 'text', None, _('treat all files as text')),
2919 ('p', 'show-function', None,
2918 ('p', 'show-function', None,
2920 _('show which function each change is in')),
2919 _('show which function each change is in')),
2921 ('g', 'git', None, _('use git extended diff format')),
2920 ('g', 'git', None, _('use git extended diff format')),
2922 ('', 'nodates', None, _("don't include dates in diff headers")),
2921 ('', 'nodates', None, _("don't include dates in diff headers")),
2923 ('w', 'ignore-all-space', None,
2922 ('w', 'ignore-all-space', None,
2924 _('ignore white space when comparing lines')),
2923 _('ignore white space when comparing lines')),
2925 ('b', 'ignore-space-change', None,
2924 ('b', 'ignore-space-change', None,
2926 _('ignore changes in the amount of white space')),
2925 _('ignore changes in the amount of white space')),
2927 ('B', 'ignore-blank-lines', None,
2926 ('B', 'ignore-blank-lines', None,
2928 _('ignore changes whose lines are all blank')),
2927 _('ignore changes whose lines are all blank')),
2929 ] + walkopts,
2928 ] + walkopts,
2930 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2929 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2931 "^export":
2930 "^export":
2932 (export,
2931 (export,
2933 [('o', 'output', '', _('print output to file with formatted name')),
2932 [('o', 'output', '', _('print output to file with formatted name')),
2934 ('a', 'text', None, _('treat all files as text')),
2933 ('a', 'text', None, _('treat all files as text')),
2935 ('g', 'git', None, _('use git extended diff format')),
2934 ('g', 'git', None, _('use git extended diff format')),
2936 ('', 'nodates', None, _("don't include dates in diff headers")),
2935 ('', 'nodates', None, _("don't include dates in diff headers")),
2937 ('', 'switch-parent', None, _('diff against the second parent'))],
2936 ('', 'switch-parent', None, _('diff against the second parent'))],
2938 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2937 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2939 "grep":
2938 "grep":
2940 (grep,
2939 (grep,
2941 [('0', 'print0', None, _('end fields with NUL')),
2940 [('0', 'print0', None, _('end fields with NUL')),
2942 ('', 'all', None, _('print all revisions that match')),
2941 ('', 'all', None, _('print all revisions that match')),
2943 ('f', 'follow', None,
2942 ('f', 'follow', None,
2944 _('follow changeset history, or file history across copies and renames')),
2943 _('follow changeset history, or file history across copies and renames')),
2945 ('i', 'ignore-case', None, _('ignore case when matching')),
2944 ('i', 'ignore-case', None, _('ignore case when matching')),
2946 ('l', 'files-with-matches', None,
2945 ('l', 'files-with-matches', None,
2947 _('print only filenames and revs that match')),
2946 _('print only filenames and revs that match')),
2948 ('n', 'line-number', None, _('print matching line numbers')),
2947 ('n', 'line-number', None, _('print matching line numbers')),
2949 ('r', 'rev', [], _('search in given revision range')),
2948 ('r', 'rev', [], _('search in given revision range')),
2950 ('u', 'user', None, _('print user who committed change')),
2949 ('u', 'user', None, _('print user who committed change')),
2951 ] + walkopts,
2950 ] + walkopts,
2952 _('hg grep [OPTION]... PATTERN [FILE]...')),
2951 _('hg grep [OPTION]... PATTERN [FILE]...')),
2953 "heads":
2952 "heads":
2954 (heads,
2953 (heads,
2955 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2954 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2956 ('', 'style', '', _('display using template map file')),
2955 ('', 'style', '', _('display using template map file')),
2957 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2956 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2958 ('', 'template', '', _('display with template'))],
2957 ('', 'template', '', _('display with template'))],
2959 _('hg heads [-r REV]')),
2958 _('hg heads [-r REV]')),
2960 "help": (help_, [], _('hg help [COMMAND]')),
2959 "help": (help_, [], _('hg help [COMMAND]')),
2961 "identify|id": (identify, [], _('hg identify')),
2960 "identify|id": (identify, [], _('hg identify')),
2962 "import|patch":
2961 "import|patch":
2963 (import_,
2962 (import_,
2964 [('p', 'strip', 1,
2963 [('p', 'strip', 1,
2965 _('directory strip option for patch. This has the same\n'
2964 _('directory strip option for patch. This has the same\n'
2966 'meaning as the corresponding patch option')),
2965 'meaning as the corresponding patch option')),
2967 ('m', 'message', '', _('use <text> as commit message')),
2966 ('m', 'message', '', _('use <text> as commit message')),
2968 ('b', 'base', '', _('base path (DEPRECATED)')),
2967 ('b', 'base', '', _('base path (DEPRECATED)')),
2969 ('f', 'force', None,
2968 ('f', 'force', None,
2970 _('skip check for outstanding uncommitted changes'))],
2969 _('skip check for outstanding uncommitted changes'))],
2971 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2970 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2972 "incoming|in": (incoming,
2971 "incoming|in": (incoming,
2973 [('M', 'no-merges', None, _('do not show merges')),
2972 [('M', 'no-merges', None, _('do not show merges')),
2974 ('f', 'force', None,
2973 ('f', 'force', None,
2975 _('run even when remote repository is unrelated')),
2974 _('run even when remote repository is unrelated')),
2976 ('', 'style', '', _('display using template map file')),
2975 ('', 'style', '', _('display using template map file')),
2977 ('n', 'newest-first', None, _('show newest record first')),
2976 ('n', 'newest-first', None, _('show newest record first')),
2978 ('', 'bundle', '', _('file to store the bundles into')),
2977 ('', 'bundle', '', _('file to store the bundles into')),
2979 ('p', 'patch', None, _('show patch')),
2978 ('p', 'patch', None, _('show patch')),
2980 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2979 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2981 ('', 'template', '', _('display with template')),
2980 ('', 'template', '', _('display with template')),
2982 ] + remoteopts,
2981 ] + remoteopts,
2983 _('hg incoming [-p] [-n] [-M] [-r REV]...'
2982 _('hg incoming [-p] [-n] [-M] [-r REV]...'
2984 ' [--bundle FILENAME] [SOURCE]')),
2983 ' [--bundle FILENAME] [SOURCE]')),
2985 "^init":
2984 "^init":
2986 (init, remoteopts, _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
2985 (init, remoteopts, _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
2987 "locate":
2986 "locate":
2988 (locate,
2987 (locate,
2989 [('r', 'rev', '', _('search the repository as it stood at rev')),
2988 [('r', 'rev', '', _('search the repository as it stood at rev')),
2990 ('0', 'print0', None,
2989 ('0', 'print0', None,
2991 _('end filenames with NUL, for use with xargs')),
2990 _('end filenames with NUL, for use with xargs')),
2992 ('f', 'fullpath', None,
2991 ('f', 'fullpath', None,
2993 _('print complete paths from the filesystem root')),
2992 _('print complete paths from the filesystem root')),
2994 ] + walkopts,
2993 ] + walkopts,
2995 _('hg locate [OPTION]... [PATTERN]...')),
2994 _('hg locate [OPTION]... [PATTERN]...')),
2996 "^log|history":
2995 "^log|history":
2997 (log,
2996 (log,
2998 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2997 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2999 ('f', 'follow', None,
2998 ('f', 'follow', None,
3000 _('follow changeset history, or file history across copies and renames')),
2999 _('follow changeset history, or file history across copies and renames')),
3001 ('', 'follow-first', None,
3000 ('', 'follow-first', None,
3002 _('only follow the first parent of merge changesets')),
3001 _('only follow the first parent of merge changesets')),
3003 ('C', 'copies', None, _('show copied files')),
3002 ('C', 'copies', None, _('show copied files')),
3004 ('k', 'keyword', [], _('search for a keyword')),
3003 ('k', 'keyword', [], _('search for a keyword')),
3005 ('l', 'limit', '', _('limit number of changes displayed')),
3004 ('l', 'limit', '', _('limit number of changes displayed')),
3006 ('r', 'rev', [], _('show the specified revision or range')),
3005 ('r', 'rev', [], _('show the specified revision or range')),
3007 ('M', 'no-merges', None, _('do not show merges')),
3006 ('M', 'no-merges', None, _('do not show merges')),
3008 ('', 'style', '', _('display using template map file')),
3007 ('', 'style', '', _('display using template map file')),
3009 ('m', 'only-merges', None, _('show only merges')),
3008 ('m', 'only-merges', None, _('show only merges')),
3010 ('p', 'patch', None, _('show patch')),
3009 ('p', 'patch', None, _('show patch')),
3011 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3010 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3012 ('', 'template', '', _('display with template')),
3011 ('', 'template', '', _('display with template')),
3013 ] + walkopts,
3012 ] + walkopts,
3014 _('hg log [OPTION]... [FILE]')),
3013 _('hg log [OPTION]... [FILE]')),
3015 "manifest": (manifest, [], _('hg manifest [REV]')),
3014 "manifest": (manifest, [], _('hg manifest [REV]')),
3016 "merge":
3015 "merge":
3017 (merge,
3016 (merge,
3018 [('b', 'branch', '', _('merge with head of a specific branch (DEPRECATED)')),
3017 [('b', 'branch', '', _('merge with head of a specific branch (DEPRECATED)')),
3019 ('f', 'force', None, _('force a merge with outstanding changes'))],
3018 ('f', 'force', None, _('force a merge with outstanding changes'))],
3020 _('hg merge [-f] [REV]')),
3019 _('hg merge [-f] [REV]')),
3021 "outgoing|out": (outgoing,
3020 "outgoing|out": (outgoing,
3022 [('M', 'no-merges', None, _('do not show merges')),
3021 [('M', 'no-merges', None, _('do not show merges')),
3023 ('f', 'force', None,
3022 ('f', 'force', None,
3024 _('run even when remote repository is unrelated')),
3023 _('run even when remote repository is unrelated')),
3025 ('p', 'patch', None, _('show patch')),
3024 ('p', 'patch', None, _('show patch')),
3026 ('', 'style', '', _('display using template map file')),
3025 ('', 'style', '', _('display using template map file')),
3027 ('r', 'rev', [], _('a specific revision you would like to push')),
3026 ('r', 'rev', [], _('a specific revision you would like to push')),
3028 ('n', 'newest-first', None, _('show newest record first')),
3027 ('n', 'newest-first', None, _('show newest record first')),
3029 ('', 'template', '', _('display with template')),
3028 ('', 'template', '', _('display with template')),
3030 ] + remoteopts,
3029 ] + remoteopts,
3031 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3030 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3032 "^parents":
3031 "^parents":
3033 (parents,
3032 (parents,
3034 [('b', 'branches', None, _('show branches (DEPRECATED)')),
3033 [('b', 'branches', None, _('show branches (DEPRECATED)')),
3035 ('r', 'rev', '', _('show parents from the specified rev')),
3034 ('r', 'rev', '', _('show parents from the specified rev')),
3036 ('', 'style', '', _('display using template map file')),
3035 ('', 'style', '', _('display using template map file')),
3037 ('', 'template', '', _('display with template'))],
3036 ('', 'template', '', _('display with template'))],
3038 _('hg parents [-r REV] [FILE]')),
3037 _('hg parents [-r REV] [FILE]')),
3039 "paths": (paths, [], _('hg paths [NAME]')),
3038 "paths": (paths, [], _('hg paths [NAME]')),
3040 "^pull":
3039 "^pull":
3041 (pull,
3040 (pull,
3042 [('u', 'update', None,
3041 [('u', 'update', None,
3043 _('update to new tip if changesets were pulled')),
3042 _('update to new tip if changesets were pulled')),
3044 ('f', 'force', None,
3043 ('f', 'force', None,
3045 _('run even when remote repository is unrelated')),
3044 _('run even when remote repository is unrelated')),
3046 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
3045 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
3047 ] + remoteopts,
3046 ] + remoteopts,
3048 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
3047 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
3049 "^push":
3048 "^push":
3050 (push,
3049 (push,
3051 [('f', 'force', None, _('force push')),
3050 [('f', 'force', None, _('force push')),
3052 ('r', 'rev', [], _('a specific revision you would like to push')),
3051 ('r', 'rev', [], _('a specific revision you would like to push')),
3053 ] + remoteopts,
3052 ] + remoteopts,
3054 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
3053 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
3055 "debugrawcommit|rawcommit":
3054 "debugrawcommit|rawcommit":
3056 (rawcommit,
3055 (rawcommit,
3057 [('p', 'parent', [], _('parent')),
3056 [('p', 'parent', [], _('parent')),
3058 ('d', 'date', '', _('date code')),
3057 ('d', 'date', '', _('date code')),
3059 ('u', 'user', '', _('user')),
3058 ('u', 'user', '', _('user')),
3060 ('F', 'files', '', _('file list')),
3059 ('F', 'files', '', _('file list')),
3061 ('m', 'message', '', _('commit message')),
3060 ('m', 'message', '', _('commit message')),
3062 ('l', 'logfile', '', _('commit message file'))],
3061 ('l', 'logfile', '', _('commit message file'))],
3063 _('hg debugrawcommit [OPTION]... [FILE]...')),
3062 _('hg debugrawcommit [OPTION]... [FILE]...')),
3064 "recover": (recover, [], _('hg recover')),
3063 "recover": (recover, [], _('hg recover')),
3065 "^remove|rm":
3064 "^remove|rm":
3066 (remove,
3065 (remove,
3067 [('A', 'after', None, _('record remove that has already occurred')),
3066 [('A', 'after', None, _('record remove that has already occurred')),
3068 ('f', 'force', None, _('remove file even if modified')),
3067 ('f', 'force', None, _('remove file even if modified')),
3069 ] + walkopts,
3068 ] + walkopts,
3070 _('hg remove [OPTION]... FILE...')),
3069 _('hg remove [OPTION]... FILE...')),
3071 "rename|mv":
3070 "rename|mv":
3072 (rename,
3071 (rename,
3073 [('A', 'after', None, _('record a rename that has already occurred')),
3072 [('A', 'after', None, _('record a rename that has already occurred')),
3074 ('f', 'force', None,
3073 ('f', 'force', None,
3075 _('forcibly copy over an existing managed file')),
3074 _('forcibly copy over an existing managed file')),
3076 ] + walkopts + dryrunopts,
3075 ] + walkopts + dryrunopts,
3077 _('hg rename [OPTION]... SOURCE... DEST')),
3076 _('hg rename [OPTION]... SOURCE... DEST')),
3078 "^revert":
3077 "^revert":
3079 (revert,
3078 (revert,
3080 [('a', 'all', None, _('revert all changes when no arguments given')),
3079 [('a', 'all', None, _('revert all changes when no arguments given')),
3081 ('r', 'rev', '', _('revision to revert to')),
3080 ('r', 'rev', '', _('revision to revert to')),
3082 ('', 'no-backup', None, _('do not save backup copies of files')),
3081 ('', 'no-backup', None, _('do not save backup copies of files')),
3083 ] + walkopts + dryrunopts,
3082 ] + walkopts + dryrunopts,
3084 _('hg revert [-r REV] [NAME]...')),
3083 _('hg revert [-r REV] [NAME]...')),
3085 "rollback": (rollback, [], _('hg rollback')),
3084 "rollback": (rollback, [], _('hg rollback')),
3086 "root": (root, [], _('hg root')),
3085 "root": (root, [], _('hg root')),
3087 "showconfig|debugconfig": (showconfig, [], _('showconfig [NAME]...')),
3086 "showconfig|debugconfig": (showconfig, [], _('showconfig [NAME]...')),
3088 "^serve":
3087 "^serve":
3089 (serve,
3088 (serve,
3090 [('A', 'accesslog', '', _('name of access log file to write to')),
3089 [('A', 'accesslog', '', _('name of access log file to write to')),
3091 ('d', 'daemon', None, _('run server in background')),
3090 ('d', 'daemon', None, _('run server in background')),
3092 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3091 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3093 ('E', 'errorlog', '', _('name of error log file to write to')),
3092 ('E', 'errorlog', '', _('name of error log file to write to')),
3094 ('p', 'port', 0, _('port to use (default: 8000)')),
3093 ('p', 'port', 0, _('port to use (default: 8000)')),
3095 ('a', 'address', '', _('address to use')),
3094 ('a', 'address', '', _('address to use')),
3096 ('n', 'name', '',
3095 ('n', 'name', '',
3097 _('name to show in web pages (default: working dir)')),
3096 _('name to show in web pages (default: working dir)')),
3098 ('', 'webdir-conf', '', _('name of the webdir config file'
3097 ('', 'webdir-conf', '', _('name of the webdir config file'
3099 ' (serve more than one repo)')),
3098 ' (serve more than one repo)')),
3100 ('', 'pid-file', '', _('name of file to write process ID to')),
3099 ('', 'pid-file', '', _('name of file to write process ID to')),
3101 ('', 'stdio', None, _('for remote clients')),
3100 ('', 'stdio', None, _('for remote clients')),
3102 ('t', 'templates', '', _('web templates to use')),
3101 ('t', 'templates', '', _('web templates to use')),
3103 ('', 'style', '', _('template style to use')),
3102 ('', 'style', '', _('template style to use')),
3104 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3103 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3105 _('hg serve [OPTION]...')),
3104 _('hg serve [OPTION]...')),
3106 "^status|st":
3105 "^status|st":
3107 (status,
3106 (status,
3108 [('A', 'all', None, _('show status of all files')),
3107 [('A', 'all', None, _('show status of all files')),
3109 ('m', 'modified', None, _('show only modified files')),
3108 ('m', 'modified', None, _('show only modified files')),
3110 ('a', 'added', None, _('show only added files')),
3109 ('a', 'added', None, _('show only added files')),
3111 ('r', 'removed', None, _('show only removed files')),
3110 ('r', 'removed', None, _('show only removed files')),
3112 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3111 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3113 ('c', 'clean', None, _('show only files without changes')),
3112 ('c', 'clean', None, _('show only files without changes')),
3114 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3113 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3115 ('i', 'ignored', None, _('show ignored files')),
3114 ('i', 'ignored', None, _('show ignored files')),
3116 ('n', 'no-status', None, _('hide status prefix')),
3115 ('n', 'no-status', None, _('hide status prefix')),
3117 ('C', 'copies', None, _('show source of copied files')),
3116 ('C', 'copies', None, _('show source of copied files')),
3118 ('0', 'print0', None,
3117 ('0', 'print0', None,
3119 _('end filenames with NUL, for use with xargs')),
3118 _('end filenames with NUL, for use with xargs')),
3120 ('', 'rev', [], _('show difference from revision')),
3119 ('', 'rev', [], _('show difference from revision')),
3121 ] + walkopts,
3120 ] + walkopts,
3122 _('hg status [OPTION]... [FILE]...')),
3121 _('hg status [OPTION]... [FILE]...')),
3123 "tag":
3122 "tag":
3124 (tag,
3123 (tag,
3125 [('l', 'local', None, _('make the tag local')),
3124 [('l', 'local', None, _('make the tag local')),
3126 ('m', 'message', '', _('message for tag commit log entry')),
3125 ('m', 'message', '', _('message for tag commit log entry')),
3127 ('d', 'date', '', _('record datecode as commit date')),
3126 ('d', 'date', '', _('record datecode as commit date')),
3128 ('u', 'user', '', _('record user as commiter')),
3127 ('u', 'user', '', _('record user as commiter')),
3129 ('r', 'rev', '', _('revision to tag'))],
3128 ('r', 'rev', '', _('revision to tag'))],
3130 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3129 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3131 "tags": (tags, [], _('hg tags')),
3130 "tags": (tags, [], _('hg tags')),
3132 "tip":
3131 "tip":
3133 (tip,
3132 (tip,
3134 [('b', 'branches', None, _('show branches (DEPRECATED)')),
3133 [('b', 'branches', None, _('show branches (DEPRECATED)')),
3135 ('', 'style', '', _('display using template map file')),
3134 ('', 'style', '', _('display using template map file')),
3136 ('p', 'patch', None, _('show patch')),
3135 ('p', 'patch', None, _('show patch')),
3137 ('', 'template', '', _('display with template'))],
3136 ('', 'template', '', _('display with template'))],
3138 _('hg tip [-p]')),
3137 _('hg tip [-p]')),
3139 "unbundle":
3138 "unbundle":
3140 (unbundle,
3139 (unbundle,
3141 [('u', 'update', None,
3140 [('u', 'update', None,
3142 _('update to new tip if changesets were unbundled'))],
3141 _('update to new tip if changesets were unbundled'))],
3143 _('hg unbundle [-u] FILE')),
3142 _('hg unbundle [-u] FILE')),
3144 "^update|up|checkout|co":
3143 "^update|up|checkout|co":
3145 (update,
3144 (update,
3146 [('b', 'branch', '',
3145 [('b', 'branch', '',
3147 _('checkout the head of a specific branch (DEPRECATED)')),
3146 _('checkout the head of a specific branch (DEPRECATED)')),
3148 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3147 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3149 ('C', 'clean', None, _('overwrite locally modified files')),
3148 ('C', 'clean', None, _('overwrite locally modified files')),
3150 ('f', 'force', None, _('force a merge with outstanding changes'))],
3149 ('f', 'force', None, _('force a merge with outstanding changes'))],
3151 _('hg update [-C] [-f] [REV]')),
3150 _('hg update [-C] [-f] [REV]')),
3152 "verify": (verify, [], _('hg verify')),
3151 "verify": (verify, [], _('hg verify')),
3153 "version": (show_version, [], _('hg version')),
3152 "version": (show_version, [], _('hg version')),
3154 }
3153 }
3155
3154
3156 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3155 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3157 " debugindex debugindexdot")
3156 " debugindex debugindexdot")
3158 optionalrepo = ("paths serve showconfig")
3157 optionalrepo = ("paths serve showconfig")
3159
3158
3160 def findpossible(ui, cmd):
3159 def findpossible(ui, cmd):
3161 """
3160 """
3162 Return cmd -> (aliases, command table entry)
3161 Return cmd -> (aliases, command table entry)
3163 for each matching command.
3162 for each matching command.
3164 Return debug commands (or their aliases) only if no normal command matches.
3163 Return debug commands (or their aliases) only if no normal command matches.
3165 """
3164 """
3166 choice = {}
3165 choice = {}
3167 debugchoice = {}
3166 debugchoice = {}
3168 for e in table.keys():
3167 for e in table.keys():
3169 aliases = e.lstrip("^").split("|")
3168 aliases = e.lstrip("^").split("|")
3170 found = None
3169 found = None
3171 if cmd in aliases:
3170 if cmd in aliases:
3172 found = cmd
3171 found = cmd
3173 elif not ui.config("ui", "strict"):
3172 elif not ui.config("ui", "strict"):
3174 for a in aliases:
3173 for a in aliases:
3175 if a.startswith(cmd):
3174 if a.startswith(cmd):
3176 found = a
3175 found = a
3177 break
3176 break
3178 if found is not None:
3177 if found is not None:
3179 if aliases[0].startswith("debug") or found.startswith("debug"):
3178 if aliases[0].startswith("debug") or found.startswith("debug"):
3180 debugchoice[found] = (aliases, table[e])
3179 debugchoice[found] = (aliases, table[e])
3181 else:
3180 else:
3182 choice[found] = (aliases, table[e])
3181 choice[found] = (aliases, table[e])
3183
3182
3184 if not choice and debugchoice:
3183 if not choice and debugchoice:
3185 choice = debugchoice
3184 choice = debugchoice
3186
3185
3187 return choice
3186 return choice
3188
3187
3189 def findcmd(ui, cmd):
3188 def findcmd(ui, cmd):
3190 """Return (aliases, command table entry) for command string."""
3189 """Return (aliases, command table entry) for command string."""
3191 choice = findpossible(ui, cmd)
3190 choice = findpossible(ui, cmd)
3192
3191
3193 if choice.has_key(cmd):
3192 if choice.has_key(cmd):
3194 return choice[cmd]
3193 return choice[cmd]
3195
3194
3196 if len(choice) > 1:
3195 if len(choice) > 1:
3197 clist = choice.keys()
3196 clist = choice.keys()
3198 clist.sort()
3197 clist.sort()
3199 raise AmbiguousCommand(cmd, clist)
3198 raise AmbiguousCommand(cmd, clist)
3200
3199
3201 if choice:
3200 if choice:
3202 return choice.values()[0]
3201 return choice.values()[0]
3203
3202
3204 raise UnknownCommand(cmd)
3203 raise UnknownCommand(cmd)
3205
3204
3206 def catchterm(*args):
3205 def catchterm(*args):
3207 raise util.SignalInterrupt
3206 raise util.SignalInterrupt
3208
3207
3209 def run():
3208 def run():
3210 sys.exit(dispatch(sys.argv[1:]))
3209 sys.exit(dispatch(sys.argv[1:]))
3211
3210
3212 class ParseError(Exception):
3211 class ParseError(Exception):
3213 """Exception raised on errors in parsing the command line."""
3212 """Exception raised on errors in parsing the command line."""
3214
3213
3215 def parse(ui, args):
3214 def parse(ui, args):
3216 options = {}
3215 options = {}
3217 cmdoptions = {}
3216 cmdoptions = {}
3218
3217
3219 try:
3218 try:
3220 args = fancyopts.fancyopts(args, globalopts, options)
3219 args = fancyopts.fancyopts(args, globalopts, options)
3221 except fancyopts.getopt.GetoptError, inst:
3220 except fancyopts.getopt.GetoptError, inst:
3222 raise ParseError(None, inst)
3221 raise ParseError(None, inst)
3223
3222
3224 if args:
3223 if args:
3225 cmd, args = args[0], args[1:]
3224 cmd, args = args[0], args[1:]
3226 aliases, i = findcmd(ui, cmd)
3225 aliases, i = findcmd(ui, cmd)
3227 cmd = aliases[0]
3226 cmd = aliases[0]
3228 defaults = ui.config("defaults", cmd)
3227 defaults = ui.config("defaults", cmd)
3229 if defaults:
3228 if defaults:
3230 args = shlex.split(defaults) + args
3229 args = shlex.split(defaults) + args
3231 c = list(i[1])
3230 c = list(i[1])
3232 else:
3231 else:
3233 cmd = None
3232 cmd = None
3234 c = []
3233 c = []
3235
3234
3236 # combine global options into local
3235 # combine global options into local
3237 for o in globalopts:
3236 for o in globalopts:
3238 c.append((o[0], o[1], options[o[1]], o[3]))
3237 c.append((o[0], o[1], options[o[1]], o[3]))
3239
3238
3240 try:
3239 try:
3241 args = fancyopts.fancyopts(args, c, cmdoptions)
3240 args = fancyopts.fancyopts(args, c, cmdoptions)
3242 except fancyopts.getopt.GetoptError, inst:
3241 except fancyopts.getopt.GetoptError, inst:
3243 raise ParseError(cmd, inst)
3242 raise ParseError(cmd, inst)
3244
3243
3245 # separate global options back out
3244 # separate global options back out
3246 for o in globalopts:
3245 for o in globalopts:
3247 n = o[1]
3246 n = o[1]
3248 options[n] = cmdoptions[n]
3247 options[n] = cmdoptions[n]
3249 del cmdoptions[n]
3248 del cmdoptions[n]
3250
3249
3251 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3250 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3252
3251
3253 external = {}
3252 external = {}
3254
3253
3255 def findext(name):
3254 def findext(name):
3256 '''return module with given extension name'''
3255 '''return module with given extension name'''
3257 try:
3256 try:
3258 return sys.modules[external[name]]
3257 return sys.modules[external[name]]
3259 except KeyError:
3258 except KeyError:
3260 for k, v in external.iteritems():
3259 for k, v in external.iteritems():
3261 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3260 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3262 return sys.modules[v]
3261 return sys.modules[v]
3263 raise KeyError(name)
3262 raise KeyError(name)
3264
3263
3265 def load_extensions(ui):
3264 def load_extensions(ui):
3266 added = []
3265 added = []
3267 for ext_name, load_from_name in ui.extensions():
3266 for ext_name, load_from_name in ui.extensions():
3268 if ext_name in external:
3267 if ext_name in external:
3269 continue
3268 continue
3270 try:
3269 try:
3271 if load_from_name:
3270 if load_from_name:
3272 # the module will be loaded in sys.modules
3271 # the module will be loaded in sys.modules
3273 # choose an unique name so that it doesn't
3272 # choose an unique name so that it doesn't
3274 # conflicts with other modules
3273 # conflicts with other modules
3275 module_name = "hgext_%s" % ext_name.replace('.', '_')
3274 module_name = "hgext_%s" % ext_name.replace('.', '_')
3276 mod = imp.load_source(module_name, load_from_name)
3275 mod = imp.load_source(module_name, load_from_name)
3277 else:
3276 else:
3278 def importh(name):
3277 def importh(name):
3279 mod = __import__(name)
3278 mod = __import__(name)
3280 components = name.split('.')
3279 components = name.split('.')
3281 for comp in components[1:]:
3280 for comp in components[1:]:
3282 mod = getattr(mod, comp)
3281 mod = getattr(mod, comp)
3283 return mod
3282 return mod
3284 try:
3283 try:
3285 mod = importh("hgext.%s" % ext_name)
3284 mod = importh("hgext.%s" % ext_name)
3286 except ImportError:
3285 except ImportError:
3287 mod = importh(ext_name)
3286 mod = importh(ext_name)
3288 external[ext_name] = mod.__name__
3287 external[ext_name] = mod.__name__
3289 added.append((mod, ext_name))
3288 added.append((mod, ext_name))
3290 except (util.SignalInterrupt, KeyboardInterrupt):
3289 except (util.SignalInterrupt, KeyboardInterrupt):
3291 raise
3290 raise
3292 except Exception, inst:
3291 except Exception, inst:
3293 ui.warn(_("*** failed to import extension %s: %s\n") %
3292 ui.warn(_("*** failed to import extension %s: %s\n") %
3294 (ext_name, inst))
3293 (ext_name, inst))
3295 if ui.print_exc():
3294 if ui.print_exc():
3296 return 1
3295 return 1
3297
3296
3298 for mod, name in added:
3297 for mod, name in added:
3299 uisetup = getattr(mod, 'uisetup', None)
3298 uisetup = getattr(mod, 'uisetup', None)
3300 if uisetup:
3299 if uisetup:
3301 uisetup(ui)
3300 uisetup(ui)
3302 cmdtable = getattr(mod, 'cmdtable', {})
3301 cmdtable = getattr(mod, 'cmdtable', {})
3303 for t in cmdtable:
3302 for t in cmdtable:
3304 if t in table:
3303 if t in table:
3305 ui.warn(_("module %s overrides %s\n") % (name, t))
3304 ui.warn(_("module %s overrides %s\n") % (name, t))
3306 table.update(cmdtable)
3305 table.update(cmdtable)
3307
3306
3308 def parseconfig(config):
3307 def parseconfig(config):
3309 """parse the --config options from the command line"""
3308 """parse the --config options from the command line"""
3310 parsed = []
3309 parsed = []
3311 for cfg in config:
3310 for cfg in config:
3312 try:
3311 try:
3313 name, value = cfg.split('=', 1)
3312 name, value = cfg.split('=', 1)
3314 section, name = name.split('.', 1)
3313 section, name = name.split('.', 1)
3315 if not section or not name:
3314 if not section or not name:
3316 raise IndexError
3315 raise IndexError
3317 parsed.append((section, name, value))
3316 parsed.append((section, name, value))
3318 except (IndexError, ValueError):
3317 except (IndexError, ValueError):
3319 raise util.Abort(_('malformed --config option: %s') % cfg)
3318 raise util.Abort(_('malformed --config option: %s') % cfg)
3320 return parsed
3319 return parsed
3321
3320
3322 def dispatch(args):
3321 def dispatch(args):
3323 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3322 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3324 num = getattr(signal, name, None)
3323 num = getattr(signal, name, None)
3325 if num: signal.signal(num, catchterm)
3324 if num: signal.signal(num, catchterm)
3326
3325
3327 try:
3326 try:
3328 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3327 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3329 except util.Abort, inst:
3328 except util.Abort, inst:
3330 sys.stderr.write(_("abort: %s\n") % inst)
3329 sys.stderr.write(_("abort: %s\n") % inst)
3331 return -1
3330 return -1
3332
3331
3333 load_extensions(u)
3332 load_extensions(u)
3334 u.addreadhook(load_extensions)
3333 u.addreadhook(load_extensions)
3335
3334
3336 try:
3335 try:
3337 cmd, func, args, options, cmdoptions = parse(u, args)
3336 cmd, func, args, options, cmdoptions = parse(u, args)
3338 if options["time"]:
3337 if options["time"]:
3339 def get_times():
3338 def get_times():
3340 t = os.times()
3339 t = os.times()
3341 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3340 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3342 t = (t[0], t[1], t[2], t[3], time.clock())
3341 t = (t[0], t[1], t[2], t[3], time.clock())
3343 return t
3342 return t
3344 s = get_times()
3343 s = get_times()
3345 def print_time():
3344 def print_time():
3346 t = get_times()
3345 t = get_times()
3347 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3346 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3348 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3347 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3349 atexit.register(print_time)
3348 atexit.register(print_time)
3350
3349
3351 # enter the debugger before command execution
3350 # enter the debugger before command execution
3352 if options['debugger']:
3351 if options['debugger']:
3353 pdb.set_trace()
3352 pdb.set_trace()
3354
3353
3355 try:
3354 try:
3356 if options['cwd']:
3355 if options['cwd']:
3357 try:
3356 try:
3358 os.chdir(options['cwd'])
3357 os.chdir(options['cwd'])
3359 except OSError, inst:
3358 except OSError, inst:
3360 raise util.Abort('%s: %s' %
3359 raise util.Abort('%s: %s' %
3361 (options['cwd'], inst.strerror))
3360 (options['cwd'], inst.strerror))
3362
3361
3363 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3362 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3364 not options["noninteractive"], options["traceback"],
3363 not options["noninteractive"], options["traceback"],
3365 parseconfig(options["config"]))
3364 parseconfig(options["config"]))
3366
3365
3367 path = u.expandpath(options["repository"]) or ""
3366 path = u.expandpath(options["repository"]) or ""
3368 repo = path and hg.repository(u, path=path) or None
3367 repo = path and hg.repository(u, path=path) or None
3369 if repo and not repo.local():
3368 if repo and not repo.local():
3370 raise util.Abort(_("repository '%s' is not local") % path)
3369 raise util.Abort(_("repository '%s' is not local") % path)
3371
3370
3372 if options['help']:
3371 if options['help']:
3373 return help_(u, cmd, options['version'])
3372 return help_(u, cmd, options['version'])
3374 elif options['version']:
3373 elif options['version']:
3375 return show_version(u)
3374 return show_version(u)
3376 elif not cmd:
3375 elif not cmd:
3377 return help_(u, 'shortlist')
3376 return help_(u, 'shortlist')
3378
3377
3379 if cmd not in norepo.split():
3378 if cmd not in norepo.split():
3380 try:
3379 try:
3381 if not repo:
3380 if not repo:
3382 repo = hg.repository(u, path=path)
3381 repo = hg.repository(u, path=path)
3383 u = repo.ui
3382 u = repo.ui
3384 for name in external.itervalues():
3383 for name in external.itervalues():
3385 mod = sys.modules[name]
3384 mod = sys.modules[name]
3386 if hasattr(mod, 'reposetup'):
3385 if hasattr(mod, 'reposetup'):
3387 mod.reposetup(u, repo)
3386 mod.reposetup(u, repo)
3388 hg.repo_setup_hooks.append(mod.reposetup)
3387 hg.repo_setup_hooks.append(mod.reposetup)
3389 except hg.RepoError:
3388 except hg.RepoError:
3390 if cmd not in optionalrepo.split():
3389 if cmd not in optionalrepo.split():
3391 raise
3390 raise
3392 d = lambda: func(u, repo, *args, **cmdoptions)
3391 d = lambda: func(u, repo, *args, **cmdoptions)
3393 else:
3392 else:
3394 d = lambda: func(u, *args, **cmdoptions)
3393 d = lambda: func(u, *args, **cmdoptions)
3395
3394
3396 try:
3395 try:
3397 if options['profile']:
3396 if options['profile']:
3398 import hotshot, hotshot.stats
3397 import hotshot, hotshot.stats
3399 prof = hotshot.Profile("hg.prof")
3398 prof = hotshot.Profile("hg.prof")
3400 try:
3399 try:
3401 try:
3400 try:
3402 return prof.runcall(d)
3401 return prof.runcall(d)
3403 except:
3402 except:
3404 try:
3403 try:
3405 u.warn(_('exception raised - generating '
3404 u.warn(_('exception raised - generating '
3406 'profile anyway\n'))
3405 'profile anyway\n'))
3407 except:
3406 except:
3408 pass
3407 pass
3409 raise
3408 raise
3410 finally:
3409 finally:
3411 prof.close()
3410 prof.close()
3412 stats = hotshot.stats.load("hg.prof")
3411 stats = hotshot.stats.load("hg.prof")
3413 stats.strip_dirs()
3412 stats.strip_dirs()
3414 stats.sort_stats('time', 'calls')
3413 stats.sort_stats('time', 'calls')
3415 stats.print_stats(40)
3414 stats.print_stats(40)
3416 elif options['lsprof']:
3415 elif options['lsprof']:
3417 try:
3416 try:
3418 from mercurial import lsprof
3417 from mercurial import lsprof
3419 except ImportError:
3418 except ImportError:
3420 raise util.Abort(_(
3419 raise util.Abort(_(
3421 'lsprof not available - install from '
3420 'lsprof not available - install from '
3422 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3421 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3423 p = lsprof.Profiler()
3422 p = lsprof.Profiler()
3424 p.enable(subcalls=True)
3423 p.enable(subcalls=True)
3425 try:
3424 try:
3426 return d()
3425 return d()
3427 finally:
3426 finally:
3428 p.disable()
3427 p.disable()
3429 stats = lsprof.Stats(p.getstats())
3428 stats = lsprof.Stats(p.getstats())
3430 stats.sort()
3429 stats.sort()
3431 stats.pprint(top=10, file=sys.stderr, climit=5)
3430 stats.pprint(top=10, file=sys.stderr, climit=5)
3432 else:
3431 else:
3433 return d()
3432 return d()
3434 finally:
3433 finally:
3435 u.flush()
3434 u.flush()
3436 except:
3435 except:
3437 # enter the debugger when we hit an exception
3436 # enter the debugger when we hit an exception
3438 if options['debugger']:
3437 if options['debugger']:
3439 pdb.post_mortem(sys.exc_info()[2])
3438 pdb.post_mortem(sys.exc_info()[2])
3440 u.print_exc()
3439 u.print_exc()
3441 raise
3440 raise
3442 except ParseError, inst:
3441 except ParseError, inst:
3443 if inst.args[0]:
3442 if inst.args[0]:
3444 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3443 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3445 help_(u, inst.args[0])
3444 help_(u, inst.args[0])
3446 else:
3445 else:
3447 u.warn(_("hg: %s\n") % inst.args[1])
3446 u.warn(_("hg: %s\n") % inst.args[1])
3448 help_(u, 'shortlist')
3447 help_(u, 'shortlist')
3449 except AmbiguousCommand, inst:
3448 except AmbiguousCommand, inst:
3450 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3449 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3451 (inst.args[0], " ".join(inst.args[1])))
3450 (inst.args[0], " ".join(inst.args[1])))
3452 except UnknownCommand, inst:
3451 except UnknownCommand, inst:
3453 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3452 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3454 help_(u, 'shortlist')
3453 help_(u, 'shortlist')
3455 except hg.RepoError, inst:
3454 except hg.RepoError, inst:
3456 u.warn(_("abort: %s!\n") % inst)
3455 u.warn(_("abort: %s!\n") % inst)
3457 except lock.LockHeld, inst:
3456 except lock.LockHeld, inst:
3458 if inst.errno == errno.ETIMEDOUT:
3457 if inst.errno == errno.ETIMEDOUT:
3459 reason = _('timed out waiting for lock held by %s') % inst.locker
3458 reason = _('timed out waiting for lock held by %s') % inst.locker
3460 else:
3459 else:
3461 reason = _('lock held by %s') % inst.locker
3460 reason = _('lock held by %s') % inst.locker
3462 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3461 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3463 except lock.LockUnavailable, inst:
3462 except lock.LockUnavailable, inst:
3464 u.warn(_("abort: could not lock %s: %s\n") %
3463 u.warn(_("abort: could not lock %s: %s\n") %
3465 (inst.desc or inst.filename, inst.strerror))
3464 (inst.desc or inst.filename, inst.strerror))
3466 except revlog.RevlogError, inst:
3465 except revlog.RevlogError, inst:
3467 u.warn(_("abort: %s!\n") % inst)
3466 u.warn(_("abort: %s!\n") % inst)
3468 except util.SignalInterrupt:
3467 except util.SignalInterrupt:
3469 u.warn(_("killed!\n"))
3468 u.warn(_("killed!\n"))
3470 except KeyboardInterrupt:
3469 except KeyboardInterrupt:
3471 try:
3470 try:
3472 u.warn(_("interrupted!\n"))
3471 u.warn(_("interrupted!\n"))
3473 except IOError, inst:
3472 except IOError, inst:
3474 if inst.errno == errno.EPIPE:
3473 if inst.errno == errno.EPIPE:
3475 if u.debugflag:
3474 if u.debugflag:
3476 u.warn(_("\nbroken pipe\n"))
3475 u.warn(_("\nbroken pipe\n"))
3477 else:
3476 else:
3478 raise
3477 raise
3479 except IOError, inst:
3478 except IOError, inst:
3480 if hasattr(inst, "code"):
3479 if hasattr(inst, "code"):
3481 u.warn(_("abort: %s\n") % inst)
3480 u.warn(_("abort: %s\n") % inst)
3482 elif hasattr(inst, "reason"):
3481 elif hasattr(inst, "reason"):
3483 u.warn(_("abort: error: %s\n") % inst.reason[1])
3482 u.warn(_("abort: error: %s\n") % inst.reason[1])
3484 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3483 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3485 if u.debugflag:
3484 if u.debugflag:
3486 u.warn(_("broken pipe\n"))
3485 u.warn(_("broken pipe\n"))
3487 elif getattr(inst, "strerror", None):
3486 elif getattr(inst, "strerror", None):
3488 if getattr(inst, "filename", None):
3487 if getattr(inst, "filename", None):
3489 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3488 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3490 else:
3489 else:
3491 u.warn(_("abort: %s\n") % inst.strerror)
3490 u.warn(_("abort: %s\n") % inst.strerror)
3492 else:
3491 else:
3493 raise
3492 raise
3494 except OSError, inst:
3493 except OSError, inst:
3495 if getattr(inst, "filename", None):
3494 if getattr(inst, "filename", None):
3496 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3495 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3497 else:
3496 else:
3498 u.warn(_("abort: %s\n") % inst.strerror)
3497 u.warn(_("abort: %s\n") % inst.strerror)
3499 except util.Abort, inst:
3498 except util.Abort, inst:
3500 u.warn(_("abort: %s\n") % inst)
3499 u.warn(_("abort: %s\n") % inst)
3501 except TypeError, inst:
3500 except TypeError, inst:
3502 # was this an argument error?
3501 # was this an argument error?
3503 tb = traceback.extract_tb(sys.exc_info()[2])
3502 tb = traceback.extract_tb(sys.exc_info()[2])
3504 if len(tb) > 2: # no
3503 if len(tb) > 2: # no
3505 raise
3504 raise
3506 u.debug(inst, "\n")
3505 u.debug(inst, "\n")
3507 u.warn(_("%s: invalid arguments\n") % cmd)
3506 u.warn(_("%s: invalid arguments\n") % cmd)
3508 help_(u, cmd)
3507 help_(u, cmd)
3509 except SystemExit, inst:
3508 except SystemExit, inst:
3510 # Commands shouldn't sys.exit directly, but give a return code.
3509 # Commands shouldn't sys.exit directly, but give a return code.
3511 # Just in case catch this and and pass exit code to caller.
3510 # Just in case catch this and and pass exit code to caller.
3512 return inst.code
3511 return inst.code
3513 except:
3512 except:
3514 u.warn(_("** unknown exception encountered, details follow\n"))
3513 u.warn(_("** unknown exception encountered, details follow\n"))
3515 u.warn(_("** report bug details to "
3514 u.warn(_("** report bug details to "
3516 "http://www.selenic.com/mercurial/bts\n"))
3515 "http://www.selenic.com/mercurial/bts\n"))
3517 u.warn(_("** or mercurial@selenic.com\n"))
3516 u.warn(_("** or mercurial@selenic.com\n"))
3518 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3517 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3519 % version.get_version())
3518 % version.get_version())
3520 raise
3519 raise
3521
3520
3522 return -1
3521 return -1
General Comments 0
You need to be logged in to leave comments. Login now