##// END OF EJS Templates
patch: return list of modified files even when an exception is raised...
Brendan Cully -
r3465:2d35a8d2 default
parent child Browse files
Show More
@@ -1,2127 +1,2128 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 try:
412 try:
412 (files, fuzz) = patch.patch(patchfile, self.ui, strip=1,
413 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
413 cwd=repo.root)
414 files=files)
414 except Exception, inst:
415 except Exception, inst:
415 self.ui.note(str(inst) + '\n')
416 self.ui.note(str(inst) + '\n')
416 if not self.ui.verbose:
417 if not self.ui.verbose:
417 self.ui.warn("patch failed, unable to continue (try -v)\n")
418 self.ui.warn("patch failed, unable to continue (try -v)\n")
418 return (False, [], False)
419 return (False, files, False)
419
420
420 return (True, files, fuzz)
421 return (True, files, fuzz)
421
422
422 def apply(self, repo, series, list=False, update_status=True,
423 def apply(self, repo, series, list=False, update_status=True,
423 strict=False, patchdir=None, merge=None, wlock=None):
424 strict=False, patchdir=None, merge=None, wlock=None):
424 # TODO unify with commands.py
425 # TODO unify with commands.py
425 if not patchdir:
426 if not patchdir:
426 patchdir = self.path
427 patchdir = self.path
427 err = 0
428 err = 0
428 if not wlock:
429 if not wlock:
429 wlock = repo.wlock()
430 wlock = repo.wlock()
430 lock = repo.lock()
431 lock = repo.lock()
431 tr = repo.transaction()
432 tr = repo.transaction()
432 n = None
433 n = None
433 for patchname in series:
434 for patchname in series:
434 pushable, reason = self.pushable(patchname)
435 pushable, reason = self.pushable(patchname)
435 if not pushable:
436 if not pushable:
436 self.explain_pushable(patchname, all_patches=True)
437 self.explain_pushable(patchname, all_patches=True)
437 continue
438 continue
438 self.ui.warn("applying %s\n" % patchname)
439 self.ui.warn("applying %s\n" % patchname)
439 pf = os.path.join(patchdir, patchname)
440 pf = os.path.join(patchdir, patchname)
440
441
441 try:
442 try:
442 message, comments, user, date, patchfound = self.readheaders(patchname)
443 message, comments, user, date, patchfound = self.readheaders(patchname)
443 except:
444 except:
444 self.ui.warn("Unable to read %s\n" % patchname)
445 self.ui.warn("Unable to read %s\n" % patchname)
445 err = 1
446 err = 1
446 break
447 break
447
448
448 if not message:
449 if not message:
449 message = "imported patch %s\n" % patchname
450 message = "imported patch %s\n" % patchname
450 else:
451 else:
451 if list:
452 if list:
452 message.append("\nimported patch %s" % patchname)
453 message.append("\nimported patch %s" % patchname)
453 message = '\n'.join(message)
454 message = '\n'.join(message)
454
455
455 (patcherr, files, fuzz) = self.patch(repo, pf)
456 (patcherr, files, fuzz) = self.patch(repo, pf)
456 patcherr = not patcherr
457 patcherr = not patcherr
457
458
458 if merge and files:
459 if merge and files:
459 # Mark as merged and update dirstate parent info
460 # Mark as merged and update dirstate parent info
460 repo.dirstate.update(repo.dirstate.filterfiles(files.keys()), 'm')
461 repo.dirstate.update(repo.dirstate.filterfiles(files.keys()), 'm')
461 p1, p2 = repo.dirstate.parents()
462 p1, p2 = repo.dirstate.parents()
462 repo.dirstate.setparents(p1, merge)
463 repo.dirstate.setparents(p1, merge)
463 files = patch.updatedir(self.ui, repo, files, wlock=wlock)
464 files = patch.updatedir(self.ui, repo, files, wlock=wlock)
464 n = repo.commit(files, message, user, date, force=1, lock=lock,
465 n = repo.commit(files, message, user, date, force=1, lock=lock,
465 wlock=wlock)
466 wlock=wlock)
466
467
467 if n == None:
468 if n == None:
468 raise util.Abort(_("repo commit failed"))
469 raise util.Abort(_("repo commit failed"))
469
470
470 if update_status:
471 if update_status:
471 self.applied.append(statusentry(revlog.hex(n), patchname))
472 self.applied.append(statusentry(revlog.hex(n), patchname))
472
473
473 if patcherr:
474 if patcherr:
474 if not patchfound:
475 if not patchfound:
475 self.ui.warn("patch %s is empty\n" % patchname)
476 self.ui.warn("patch %s is empty\n" % patchname)
476 err = 0
477 err = 0
477 else:
478 else:
478 self.ui.warn("patch failed, rejects left in working dir\n")
479 self.ui.warn("patch failed, rejects left in working dir\n")
479 err = 1
480 err = 1
480 break
481 break
481
482
482 if fuzz and strict:
483 if fuzz and strict:
483 self.ui.warn("fuzz found when applying patch, stopping\n")
484 self.ui.warn("fuzz found when applying patch, stopping\n")
484 err = 1
485 err = 1
485 break
486 break
486 tr.close()
487 tr.close()
487 return (err, n)
488 return (err, n)
488
489
489 def delete(self, repo, patches, opts):
490 def delete(self, repo, patches, opts):
490 realpatches = []
491 realpatches = []
491 for patch in patches:
492 for patch in patches:
492 patch = self.lookup(patch, strict=True)
493 patch = self.lookup(patch, strict=True)
493 info = self.isapplied(patch)
494 info = self.isapplied(patch)
494 if info:
495 if info:
495 raise util.Abort(_("cannot delete applied patch %s") % patch)
496 raise util.Abort(_("cannot delete applied patch %s") % patch)
496 if patch not in self.series:
497 if patch not in self.series:
497 raise util.Abort(_("patch %s not in series file") % patch)
498 raise util.Abort(_("patch %s not in series file") % patch)
498 realpatches.append(patch)
499 realpatches.append(patch)
499
500
500 appliedbase = 0
501 appliedbase = 0
501 if opts.get('rev'):
502 if opts.get('rev'):
502 if not self.applied:
503 if not self.applied:
503 raise util.Abort(_('no patches applied'))
504 raise util.Abort(_('no patches applied'))
504 revs = [int(r) for r in cmdutil.revrange(ui, repo, opts['rev'])]
505 revs = [int(r) for r in cmdutil.revrange(ui, repo, opts['rev'])]
505 if len(revs) > 1 and revs[0] > revs[1]:
506 if len(revs) > 1 and revs[0] > revs[1]:
506 revs.reverse()
507 revs.reverse()
507 for rev in revs:
508 for rev in revs:
508 if appliedbase >= len(self.applied):
509 if appliedbase >= len(self.applied):
509 raise util.Abort(_("revision %d is not managed") % rev)
510 raise util.Abort(_("revision %d is not managed") % rev)
510
511
511 base = revlog.bin(self.applied[appliedbase].rev)
512 base = revlog.bin(self.applied[appliedbase].rev)
512 node = repo.changelog.node(rev)
513 node = repo.changelog.node(rev)
513 if node != base:
514 if node != base:
514 raise util.Abort(_("cannot delete revision %d above "
515 raise util.Abort(_("cannot delete revision %d above "
515 "applied patches") % rev)
516 "applied patches") % rev)
516 realpatches.append(self.applied[appliedbase].name)
517 realpatches.append(self.applied[appliedbase].name)
517 appliedbase += 1
518 appliedbase += 1
518
519
519 if not opts.get('keep'):
520 if not opts.get('keep'):
520 r = self.qrepo()
521 r = self.qrepo()
521 if r:
522 if r:
522 r.remove(realpatches, True)
523 r.remove(realpatches, True)
523 else:
524 else:
524 for p in realpatches:
525 for p in realpatches:
525 os.unlink(self.join(p))
526 os.unlink(self.join(p))
526
527
527 if appliedbase:
528 if appliedbase:
528 del self.applied[:appliedbase]
529 del self.applied[:appliedbase]
529 self.applied_dirty = 1
530 self.applied_dirty = 1
530 indices = [self.find_series(p) for p in realpatches]
531 indices = [self.find_series(p) for p in realpatches]
531 indices.sort()
532 indices.sort()
532 for i in indices[-1::-1]:
533 for i in indices[-1::-1]:
533 del self.full_series[i]
534 del self.full_series[i]
534 self.parse_series()
535 self.parse_series()
535 self.series_dirty = 1
536 self.series_dirty = 1
536
537
537 def check_toppatch(self, repo):
538 def check_toppatch(self, repo):
538 if len(self.applied) > 0:
539 if len(self.applied) > 0:
539 top = revlog.bin(self.applied[-1].rev)
540 top = revlog.bin(self.applied[-1].rev)
540 pp = repo.dirstate.parents()
541 pp = repo.dirstate.parents()
541 if top not in pp:
542 if top not in pp:
542 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"))
543 return top
544 return top
544 return None
545 return None
545 def check_localchanges(self, repo, force=False, refresh=True):
546 def check_localchanges(self, repo, force=False, refresh=True):
546 m, a, r, d = repo.status()[:4]
547 m, a, r, d = repo.status()[:4]
547 if m or a or r or d:
548 if m or a or r or d:
548 if not force:
549 if not force:
549 if refresh:
550 if refresh:
550 raise util.Abort(_("local changes found, refresh first"))
551 raise util.Abort(_("local changes found, refresh first"))
551 else:
552 else:
552 raise util.Abort(_("local changes found"))
553 raise util.Abort(_("local changes found"))
553 return m, a, r, d
554 return m, a, r, d
554 def new(self, repo, patch, msg=None, force=None):
555 def new(self, repo, patch, msg=None, force=None):
555 if os.path.exists(self.join(patch)):
556 if os.path.exists(self.join(patch)):
556 raise util.Abort(_('patch "%s" already exists') % patch)
557 raise util.Abort(_('patch "%s" already exists') % patch)
557 m, a, r, d = self.check_localchanges(repo, force)
558 m, a, r, d = self.check_localchanges(repo, force)
558 commitfiles = m + a + r
559 commitfiles = m + a + r
559 self.check_toppatch(repo)
560 self.check_toppatch(repo)
560 wlock = repo.wlock()
561 wlock = repo.wlock()
561 insert = self.full_series_end()
562 insert = self.full_series_end()
562 if msg:
563 if msg:
563 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
564 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
564 wlock=wlock)
565 wlock=wlock)
565 else:
566 else:
566 n = repo.commit(commitfiles,
567 n = repo.commit(commitfiles,
567 "New patch: %s" % patch, force=True, wlock=wlock)
568 "New patch: %s" % patch, force=True, wlock=wlock)
568 if n == None:
569 if n == None:
569 raise util.Abort(_("repo commit failed"))
570 raise util.Abort(_("repo commit failed"))
570 self.full_series[insert:insert] = [patch]
571 self.full_series[insert:insert] = [patch]
571 self.applied.append(statusentry(revlog.hex(n), patch))
572 self.applied.append(statusentry(revlog.hex(n), patch))
572 self.parse_series()
573 self.parse_series()
573 self.series_dirty = 1
574 self.series_dirty = 1
574 self.applied_dirty = 1
575 self.applied_dirty = 1
575 p = self.opener(patch, "w")
576 p = self.opener(patch, "w")
576 if msg:
577 if msg:
577 msg = msg + "\n"
578 msg = msg + "\n"
578 p.write(msg)
579 p.write(msg)
579 p.close()
580 p.close()
580 wlock = None
581 wlock = None
581 r = self.qrepo()
582 r = self.qrepo()
582 if r: r.add([patch])
583 if r: r.add([patch])
583 if commitfiles:
584 if commitfiles:
584 self.refresh(repo, short=True)
585 self.refresh(repo, short=True)
585
586
586 def strip(self, repo, rev, update=True, backup="all", wlock=None):
587 def strip(self, repo, rev, update=True, backup="all", wlock=None):
587 def limitheads(chlog, stop):
588 def limitheads(chlog, stop):
588 """return the list of all nodes that have no children"""
589 """return the list of all nodes that have no children"""
589 p = {}
590 p = {}
590 h = []
591 h = []
591 stoprev = 0
592 stoprev = 0
592 if stop in chlog.nodemap:
593 if stop in chlog.nodemap:
593 stoprev = chlog.rev(stop)
594 stoprev = chlog.rev(stop)
594
595
595 for r in range(chlog.count() - 1, -1, -1):
596 for r in range(chlog.count() - 1, -1, -1):
596 n = chlog.node(r)
597 n = chlog.node(r)
597 if n not in p:
598 if n not in p:
598 h.append(n)
599 h.append(n)
599 if n == stop:
600 if n == stop:
600 break
601 break
601 if r < stoprev:
602 if r < stoprev:
602 break
603 break
603 for pn in chlog.parents(n):
604 for pn in chlog.parents(n):
604 p[pn] = 1
605 p[pn] = 1
605 return h
606 return h
606
607
607 def bundle(cg):
608 def bundle(cg):
608 backupdir = repo.join("strip-backup")
609 backupdir = repo.join("strip-backup")
609 if not os.path.isdir(backupdir):
610 if not os.path.isdir(backupdir):
610 os.mkdir(backupdir)
611 os.mkdir(backupdir)
611 name = os.path.join(backupdir, "%s" % revlog.short(rev))
612 name = os.path.join(backupdir, "%s" % revlog.short(rev))
612 name = savename(name)
613 name = savename(name)
613 self.ui.warn("saving bundle to %s\n" % name)
614 self.ui.warn("saving bundle to %s\n" % name)
614 # TODO, exclusive open
615 # TODO, exclusive open
615 f = open(name, "wb")
616 f = open(name, "wb")
616 try:
617 try:
617 f.write("HG10")
618 f.write("HG10")
618 z = bz2.BZ2Compressor(9)
619 z = bz2.BZ2Compressor(9)
619 while 1:
620 while 1:
620 chunk = cg.read(4096)
621 chunk = cg.read(4096)
621 if not chunk:
622 if not chunk:
622 break
623 break
623 f.write(z.compress(chunk))
624 f.write(z.compress(chunk))
624 f.write(z.flush())
625 f.write(z.flush())
625 except:
626 except:
626 os.unlink(name)
627 os.unlink(name)
627 raise
628 raise
628 f.close()
629 f.close()
629 return name
630 return name
630
631
631 def stripall(rev, revnum):
632 def stripall(rev, revnum):
632 cl = repo.changelog
633 cl = repo.changelog
633 c = cl.read(rev)
634 c = cl.read(rev)
634 mm = repo.manifest.read(c[0])
635 mm = repo.manifest.read(c[0])
635 seen = {}
636 seen = {}
636
637
637 for x in xrange(revnum, cl.count()):
638 for x in xrange(revnum, cl.count()):
638 c = cl.read(cl.node(x))
639 c = cl.read(cl.node(x))
639 for f in c[3]:
640 for f in c[3]:
640 if f in seen:
641 if f in seen:
641 continue
642 continue
642 seen[f] = 1
643 seen[f] = 1
643 if f in mm:
644 if f in mm:
644 filerev = mm[f]
645 filerev = mm[f]
645 else:
646 else:
646 filerev = 0
647 filerev = 0
647 seen[f] = filerev
648 seen[f] = filerev
648 # 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
649 # sensible order. When stripping many files, this helps keep
650 # sensible order. When stripping many files, this helps keep
650 # our disk access patterns under control.
651 # our disk access patterns under control.
651 seen_list = seen.keys()
652 seen_list = seen.keys()
652 seen_list.sort()
653 seen_list.sort()
653 for f in seen_list:
654 for f in seen_list:
654 ff = repo.file(f)
655 ff = repo.file(f)
655 filerev = seen[f]
656 filerev = seen[f]
656 if filerev != 0:
657 if filerev != 0:
657 if filerev in ff.nodemap:
658 if filerev in ff.nodemap:
658 filerev = ff.rev(filerev)
659 filerev = ff.rev(filerev)
659 else:
660 else:
660 filerev = 0
661 filerev = 0
661 ff.strip(filerev, revnum)
662 ff.strip(filerev, revnum)
662
663
663 if not wlock:
664 if not wlock:
664 wlock = repo.wlock()
665 wlock = repo.wlock()
665 lock = repo.lock()
666 lock = repo.lock()
666 chlog = repo.changelog
667 chlog = repo.changelog
667 # TODO delete the undo files, and handle undo of merge sets
668 # TODO delete the undo files, and handle undo of merge sets
668 pp = chlog.parents(rev)
669 pp = chlog.parents(rev)
669 revnum = chlog.rev(rev)
670 revnum = chlog.rev(rev)
670
671
671 if update:
672 if update:
672 self.check_localchanges(repo, refresh=False)
673 self.check_localchanges(repo, refresh=False)
673 urev = self.qparents(repo, rev)
674 urev = self.qparents(repo, rev)
674 hg.clean(repo, urev, wlock=wlock)
675 hg.clean(repo, urev, wlock=wlock)
675 repo.dirstate.write()
676 repo.dirstate.write()
676
677
677 # 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
678 # that we actually want to keep. changegroup will be used
679 # that we actually want to keep. changegroup will be used
679 # to preserve them and add them back after the truncate
680 # to preserve them and add them back after the truncate
680 saveheads = []
681 saveheads = []
681 savebases = {}
682 savebases = {}
682
683
683 heads = limitheads(chlog, rev)
684 heads = limitheads(chlog, rev)
684 seen = {}
685 seen = {}
685
686
686 # search through all the heads, finding those where the revision
687 # search through all the heads, finding those where the revision
687 # 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
688 # that might be turned into new heads by the strip.
689 # that might be turned into new heads by the strip.
689 while heads:
690 while heads:
690 h = heads.pop()
691 h = heads.pop()
691 n = h
692 n = h
692 while True:
693 while True:
693 seen[n] = 1
694 seen[n] = 1
694 pp = chlog.parents(n)
695 pp = chlog.parents(n)
695 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
696 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
696 if pp[1] not in seen:
697 if pp[1] not in seen:
697 heads.append(pp[1])
698 heads.append(pp[1])
698 if pp[0] == revlog.nullid:
699 if pp[0] == revlog.nullid:
699 break
700 break
700 if chlog.rev(pp[0]) < revnum:
701 if chlog.rev(pp[0]) < revnum:
701 break
702 break
702 n = pp[0]
703 n = pp[0]
703 if n == rev:
704 if n == rev:
704 break
705 break
705 r = chlog.reachable(h, rev)
706 r = chlog.reachable(h, rev)
706 if rev not in r:
707 if rev not in r:
707 saveheads.append(h)
708 saveheads.append(h)
708 for x in r:
709 for x in r:
709 if chlog.rev(x) > revnum:
710 if chlog.rev(x) > revnum:
710 savebases[x] = 1
711 savebases[x] = 1
711
712
712 # create a changegroup for all the branches we need to keep
713 # create a changegroup for all the branches we need to keep
713 if backup == "all":
714 if backup == "all":
714 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
715 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
715 bundle(backupch)
716 bundle(backupch)
716 if saveheads:
717 if saveheads:
717 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
718 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
718 chgrpfile = bundle(backupch)
719 chgrpfile = bundle(backupch)
719
720
720 stripall(rev, revnum)
721 stripall(rev, revnum)
721
722
722 change = chlog.read(rev)
723 change = chlog.read(rev)
723 chlog.strip(revnum, revnum)
724 chlog.strip(revnum, revnum)
724 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
725 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
725 if saveheads:
726 if saveheads:
726 self.ui.status("adding branch\n")
727 self.ui.status("adding branch\n")
727 commands.unbundle(self.ui, repo, chgrpfile, update=False)
728 commands.unbundle(self.ui, repo, chgrpfile, update=False)
728 if backup != "strip":
729 if backup != "strip":
729 os.unlink(chgrpfile)
730 os.unlink(chgrpfile)
730
731
731 def isapplied(self, patch):
732 def isapplied(self, patch):
732 """returns (index, rev, patch)"""
733 """returns (index, rev, patch)"""
733 for i in xrange(len(self.applied)):
734 for i in xrange(len(self.applied)):
734 a = self.applied[i]
735 a = self.applied[i]
735 if a.name == patch:
736 if a.name == patch:
736 return (i, a.rev, a.name)
737 return (i, a.rev, a.name)
737 return None
738 return None
738
739
739 # 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
740 # variations. If strict is passed, we try only #1
741 # variations. If strict is passed, we try only #1
741 #
742 #
742 # 1) a number to indicate an offset in the series file
743 # 1) a number to indicate an offset in the series file
743 # 2) a unique substring of the patch name was given
744 # 2) a unique substring of the patch name was given
744 # 3) patchname[-+]num to indicate an offset in the series file
745 # 3) patchname[-+]num to indicate an offset in the series file
745 def lookup(self, patch, strict=False):
746 def lookup(self, patch, strict=False):
746 patch = patch and str(patch)
747 patch = patch and str(patch)
747
748
748 def partial_name(s):
749 def partial_name(s):
749 if s in self.series:
750 if s in self.series:
750 return s
751 return s
751 matches = [x for x in self.series if s in x]
752 matches = [x for x in self.series if s in x]
752 if len(matches) > 1:
753 if len(matches) > 1:
753 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
754 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
754 for m in matches:
755 for m in matches:
755 self.ui.warn(' %s\n' % m)
756 self.ui.warn(' %s\n' % m)
756 return None
757 return None
757 if matches:
758 if matches:
758 return matches[0]
759 return matches[0]
759 if len(self.series) > 0 and len(self.applied) > 0:
760 if len(self.series) > 0 and len(self.applied) > 0:
760 if s == 'qtip':
761 if s == 'qtip':
761 return self.series[self.series_end()-1]
762 return self.series[self.series_end()-1]
762 if s == 'qbase':
763 if s == 'qbase':
763 return self.series[0]
764 return self.series[0]
764 return None
765 return None
765 if patch == None:
766 if patch == None:
766 return None
767 return None
767
768
768 # 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
769 # sure the file name passed in does not exist (checked below)
770 # sure the file name passed in does not exist (checked below)
770 res = partial_name(patch)
771 res = partial_name(patch)
771 if res and res == patch:
772 if res and res == patch:
772 return res
773 return res
773
774
774 if not os.path.isfile(self.join(patch)):
775 if not os.path.isfile(self.join(patch)):
775 try:
776 try:
776 sno = int(patch)
777 sno = int(patch)
777 except(ValueError, OverflowError):
778 except(ValueError, OverflowError):
778 pass
779 pass
779 else:
780 else:
780 if sno < len(self.series):
781 if sno < len(self.series):
781 return self.series[sno]
782 return self.series[sno]
782 if not strict:
783 if not strict:
783 # return any partial match made above
784 # return any partial match made above
784 if res:
785 if res:
785 return res
786 return res
786 minus = patch.rfind('-')
787 minus = patch.rfind('-')
787 if minus >= 0:
788 if minus >= 0:
788 res = partial_name(patch[:minus])
789 res = partial_name(patch[:minus])
789 if res:
790 if res:
790 i = self.series.index(res)
791 i = self.series.index(res)
791 try:
792 try:
792 off = int(patch[minus+1:] or 1)
793 off = int(patch[minus+1:] or 1)
793 except(ValueError, OverflowError):
794 except(ValueError, OverflowError):
794 pass
795 pass
795 else:
796 else:
796 if i - off >= 0:
797 if i - off >= 0:
797 return self.series[i - off]
798 return self.series[i - off]
798 plus = patch.rfind('+')
799 plus = patch.rfind('+')
799 if plus >= 0:
800 if plus >= 0:
800 res = partial_name(patch[:plus])
801 res = partial_name(patch[:plus])
801 if res:
802 if res:
802 i = self.series.index(res)
803 i = self.series.index(res)
803 try:
804 try:
804 off = int(patch[plus+1:] or 1)
805 off = int(patch[plus+1:] or 1)
805 except(ValueError, OverflowError):
806 except(ValueError, OverflowError):
806 pass
807 pass
807 else:
808 else:
808 if i + off < len(self.series):
809 if i + off < len(self.series):
809 return self.series[i + off]
810 return self.series[i + off]
810 raise util.Abort(_("patch %s not in series") % patch)
811 raise util.Abort(_("patch %s not in series") % patch)
811
812
812 def push(self, repo, patch=None, force=False, list=False,
813 def push(self, repo, patch=None, force=False, list=False,
813 mergeq=None, wlock=None):
814 mergeq=None, wlock=None):
814 if not wlock:
815 if not wlock:
815 wlock = repo.wlock()
816 wlock = repo.wlock()
816 patch = self.lookup(patch)
817 patch = self.lookup(patch)
817 if patch and self.isapplied(patch):
818 if patch and self.isapplied(patch):
818 raise util.Abort(_("patch %s is already applied") % patch)
819 raise util.Abort(_("patch %s is already applied") % patch)
819 if self.series_end() == len(self.series):
820 if self.series_end() == len(self.series):
820 raise util.Abort(_("patch series fully applied"))
821 raise util.Abort(_("patch series fully applied"))
821 if not force:
822 if not force:
822 self.check_localchanges(repo)
823 self.check_localchanges(repo)
823
824
824 self.applied_dirty = 1;
825 self.applied_dirty = 1;
825 start = self.series_end()
826 start = self.series_end()
826 if start > 0:
827 if start > 0:
827 self.check_toppatch(repo)
828 self.check_toppatch(repo)
828 if not patch:
829 if not patch:
829 patch = self.series[start]
830 patch = self.series[start]
830 end = start + 1
831 end = start + 1
831 else:
832 else:
832 end = self.series.index(patch, start) + 1
833 end = self.series.index(patch, start) + 1
833 s = self.series[start:end]
834 s = self.series[start:end]
834 if mergeq:
835 if mergeq:
835 ret = self.mergepatch(repo, mergeq, s, wlock)
836 ret = self.mergepatch(repo, mergeq, s, wlock)
836 else:
837 else:
837 ret = self.apply(repo, s, list, wlock=wlock)
838 ret = self.apply(repo, s, list, wlock=wlock)
838 top = self.applied[-1].name
839 top = self.applied[-1].name
839 if ret[0]:
840 if ret[0]:
840 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" %
841 top)
842 top)
842 else:
843 else:
843 self.ui.write("Now at: %s\n" % top)
844 self.ui.write("Now at: %s\n" % top)
844 return ret[0]
845 return ret[0]
845
846
846 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,
847 wlock=None):
848 wlock=None):
848 def getfile(f, rev):
849 def getfile(f, rev):
849 t = repo.file(f).read(rev)
850 t = repo.file(f).read(rev)
850 try:
851 try:
851 repo.wfile(f, "w").write(t)
852 repo.wfile(f, "w").write(t)
852 except IOError:
853 except IOError:
853 try:
854 try:
854 os.makedirs(os.path.dirname(repo.wjoin(f)))
855 os.makedirs(os.path.dirname(repo.wjoin(f)))
855 except OSError, err:
856 except OSError, err:
856 if err.errno != errno.EEXIST: raise
857 if err.errno != errno.EEXIST: raise
857 repo.wfile(f, "w").write(t)
858 repo.wfile(f, "w").write(t)
858
859
859 if not wlock:
860 if not wlock:
860 wlock = repo.wlock()
861 wlock = repo.wlock()
861 if patch:
862 if patch:
862 # index, rev, patch
863 # index, rev, patch
863 info = self.isapplied(patch)
864 info = self.isapplied(patch)
864 if not info:
865 if not info:
865 patch = self.lookup(patch)
866 patch = self.lookup(patch)
866 info = self.isapplied(patch)
867 info = self.isapplied(patch)
867 if not info:
868 if not info:
868 raise util.Abort(_("patch %s is not applied") % patch)
869 raise util.Abort(_("patch %s is not applied") % patch)
869 if len(self.applied) == 0:
870 if len(self.applied) == 0:
870 raise util.Abort(_("no patches applied"))
871 raise util.Abort(_("no patches applied"))
871
872
872 if not update:
873 if not update:
873 parents = repo.dirstate.parents()
874 parents = repo.dirstate.parents()
874 rr = [ revlog.bin(x.rev) for x in self.applied ]
875 rr = [ revlog.bin(x.rev) for x in self.applied ]
875 for p in parents:
876 for p in parents:
876 if p in rr:
877 if p in rr:
877 self.ui.warn("qpop: forcing dirstate update\n")
878 self.ui.warn("qpop: forcing dirstate update\n")
878 update = True
879 update = True
879
880
880 if not force and update:
881 if not force and update:
881 self.check_localchanges(repo)
882 self.check_localchanges(repo)
882
883
883 self.applied_dirty = 1;
884 self.applied_dirty = 1;
884 end = len(self.applied)
885 end = len(self.applied)
885 if not patch:
886 if not patch:
886 if all:
887 if all:
887 popi = 0
888 popi = 0
888 else:
889 else:
889 popi = len(self.applied) - 1
890 popi = len(self.applied) - 1
890 else:
891 else:
891 popi = info[0] + 1
892 popi = info[0] + 1
892 if popi >= end:
893 if popi >= end:
893 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)
894 return
895 return
895 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
896 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
896
897
897 start = info[0]
898 start = info[0]
898 rev = revlog.bin(info[1])
899 rev = revlog.bin(info[1])
899
900
900 # 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
901 # form of hg.update.
902 # form of hg.update.
902 if update:
903 if update:
903 top = self.check_toppatch(repo)
904 top = self.check_toppatch(repo)
904 qp = self.qparents(repo, rev)
905 qp = self.qparents(repo, rev)
905 changes = repo.changelog.read(qp)
906 changes = repo.changelog.read(qp)
906 mmap = repo.manifest.read(changes[0])
907 mmap = repo.manifest.read(changes[0])
907 m, a, r, d, u = repo.status(qp, top)[:5]
908 m, a, r, d, u = repo.status(qp, top)[:5]
908 if d:
909 if d:
909 raise util.Abort("deletions found between repo revs")
910 raise util.Abort("deletions found between repo revs")
910 for f in m:
911 for f in m:
911 getfile(f, mmap[f])
912 getfile(f, mmap[f])
912 for f in r:
913 for f in r:
913 getfile(f, mmap[f])
914 getfile(f, mmap[f])
914 util.set_exec(repo.wjoin(f), mmap.execf(f))
915 util.set_exec(repo.wjoin(f), mmap.execf(f))
915 repo.dirstate.update(m + r, 'n')
916 repo.dirstate.update(m + r, 'n')
916 for f in a:
917 for f in a:
917 try: os.unlink(repo.wjoin(f))
918 try: os.unlink(repo.wjoin(f))
918 except: raise
919 except: raise
919 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
920 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
920 except: pass
921 except: pass
921 if a:
922 if a:
922 repo.dirstate.forget(a)
923 repo.dirstate.forget(a)
923 repo.dirstate.setparents(qp, revlog.nullid)
924 repo.dirstate.setparents(qp, revlog.nullid)
924 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
925 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
925 del self.applied[start:end]
926 del self.applied[start:end]
926 if len(self.applied):
927 if len(self.applied):
927 self.ui.write("Now at: %s\n" % self.applied[-1].name)
928 self.ui.write("Now at: %s\n" % self.applied[-1].name)
928 else:
929 else:
929 self.ui.write("Patch queue now empty\n")
930 self.ui.write("Patch queue now empty\n")
930
931
931 def diff(self, repo, pats, opts):
932 def diff(self, repo, pats, opts):
932 top = self.check_toppatch(repo)
933 top = self.check_toppatch(repo)
933 if not top:
934 if not top:
934 self.ui.write("No patches applied\n")
935 self.ui.write("No patches applied\n")
935 return
936 return
936 qp = self.qparents(repo, top)
937 qp = self.qparents(repo, top)
937 self.printdiff(repo, qp, files=pats, opts=opts)
938 self.printdiff(repo, qp, files=pats, opts=opts)
938
939
939 def refresh(self, repo, pats=None, **opts):
940 def refresh(self, repo, pats=None, **opts):
940 if len(self.applied) == 0:
941 if len(self.applied) == 0:
941 self.ui.write("No patches applied\n")
942 self.ui.write("No patches applied\n")
942 return 1
943 return 1
943 wlock = repo.wlock()
944 wlock = repo.wlock()
944 self.check_toppatch(repo)
945 self.check_toppatch(repo)
945 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
946 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
946 top = revlog.bin(top)
947 top = revlog.bin(top)
947 cparents = repo.changelog.parents(top)
948 cparents = repo.changelog.parents(top)
948 patchparent = self.qparents(repo, top)
949 patchparent = self.qparents(repo, top)
949 message, comments, user, date, patchfound = self.readheaders(patchfn)
950 message, comments, user, date, patchfound = self.readheaders(patchfn)
950
951
951 patchf = self.opener(patchfn, "w")
952 patchf = self.opener(patchfn, "w")
952 msg = opts.get('msg', '').rstrip()
953 msg = opts.get('msg', '').rstrip()
953 if msg:
954 if msg:
954 if comments:
955 if comments:
955 # Remove existing message.
956 # Remove existing message.
956 ci = 0
957 ci = 0
957 for mi in range(len(message)):
958 for mi in range(len(message)):
958 while message[mi] != comments[ci]:
959 while message[mi] != comments[ci]:
959 ci += 1
960 ci += 1
960 del comments[ci]
961 del comments[ci]
961 comments.append(msg)
962 comments.append(msg)
962 if comments:
963 if comments:
963 comments = "\n".join(comments) + '\n\n'
964 comments = "\n".join(comments) + '\n\n'
964 patchf.write(comments)
965 patchf.write(comments)
965
966
966 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
967 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
967 tip = repo.changelog.tip()
968 tip = repo.changelog.tip()
968 if top == tip:
969 if top == tip:
969 # 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
970 # optimization here. We update the dirstate in place and strip
971 # optimization here. We update the dirstate in place and strip
971 # off the tip commit. Then just commit the current directory
972 # off the tip commit. Then just commit the current directory
972 # tree. We can also send repo.commit the list of files
973 # tree. We can also send repo.commit the list of files
973 # changed to speed up the diff
974 # changed to speed up the diff
974 #
975 #
975 # in short mode, we only diff the files included in the
976 # in short mode, we only diff the files included in the
976 # patch already
977 # patch already
977 #
978 #
978 # this should really read:
979 # this should really read:
979 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
980 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
980 # but we do it backwards to take advantage of manifest/chlog
981 # but we do it backwards to take advantage of manifest/chlog
981 # caching against the next repo.status call
982 # caching against the next repo.status call
982 #
983 #
983 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
984 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
984 if opts.get('short'):
985 if opts.get('short'):
985 filelist = mm + aa + dd
986 filelist = mm + aa + dd
986 else:
987 else:
987 filelist = None
988 filelist = None
988 m, a, r, d, u = repo.status(files=filelist)[:5]
989 m, a, r, d, u = repo.status(files=filelist)[:5]
989
990
990 # 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
991 # the dirstate parent, but then changed in the local dirstate.
992 # the dirstate parent, but then changed in the local dirstate.
992 # 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
993 for x in m:
994 for x in m:
994 if x not in aa:
995 if x not in aa:
995 mm.append(x)
996 mm.append(x)
996 # 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
997 # were deleted by the patch. In this case, they should only
998 # were deleted by the patch. In this case, they should only
998 # show up in the changed section.
999 # show up in the changed section.
999 for x in a:
1000 for x in a:
1000 if x in dd:
1001 if x in dd:
1001 del dd[dd.index(x)]
1002 del dd[dd.index(x)]
1002 mm.append(x)
1003 mm.append(x)
1003 else:
1004 else:
1004 aa.append(x)
1005 aa.append(x)
1005 # make sure any files deleted in the local dirstate
1006 # make sure any files deleted in the local dirstate
1006 # are not in the add or change column of the patch
1007 # are not in the add or change column of the patch
1007 forget = []
1008 forget = []
1008 for x in d + r:
1009 for x in d + r:
1009 if x in aa:
1010 if x in aa:
1010 del aa[aa.index(x)]
1011 del aa[aa.index(x)]
1011 forget.append(x)
1012 forget.append(x)
1012 continue
1013 continue
1013 elif x in mm:
1014 elif x in mm:
1014 del mm[mm.index(x)]
1015 del mm[mm.index(x)]
1015 dd.append(x)
1016 dd.append(x)
1016
1017
1017 m = list(util.unique(mm))
1018 m = list(util.unique(mm))
1018 r = list(util.unique(dd))
1019 r = list(util.unique(dd))
1019 a = list(util.unique(aa))
1020 a = list(util.unique(aa))
1020 filelist = filter(matchfn, util.unique(m + r + a))
1021 filelist = filter(matchfn, util.unique(m + r + a))
1021 if opts.get('git'):
1022 if opts.get('git'):
1022 self.diffopts().git = True
1023 self.diffopts().git = True
1023 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1024 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1024 fp=patchf, changes=(m, a, r, [], u),
1025 fp=patchf, changes=(m, a, r, [], u),
1025 opts=self.diffopts())
1026 opts=self.diffopts())
1026 patchf.close()
1027 patchf.close()
1027
1028
1028 changes = repo.changelog.read(tip)
1029 changes = repo.changelog.read(tip)
1029 repo.dirstate.setparents(*cparents)
1030 repo.dirstate.setparents(*cparents)
1030 copies = [(f, repo.dirstate.copied(f)) for f in a]
1031 copies = [(f, repo.dirstate.copied(f)) for f in a]
1031 repo.dirstate.update(a, 'a')
1032 repo.dirstate.update(a, 'a')
1032 for dst, src in copies:
1033 for dst, src in copies:
1033 repo.dirstate.copy(src, dst)
1034 repo.dirstate.copy(src, dst)
1034 repo.dirstate.update(r, 'r')
1035 repo.dirstate.update(r, 'r')
1035 # 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
1036 # so status can see it.
1037 # so status can see it.
1037 mm = []
1038 mm = []
1038 for i in range(len(m)-1, -1, -1):
1039 for i in range(len(m)-1, -1, -1):
1039 if not matchfn(m[i]):
1040 if not matchfn(m[i]):
1040 mm.append(m[i])
1041 mm.append(m[i])
1041 del m[i]
1042 del m[i]
1042 repo.dirstate.update(m, 'n')
1043 repo.dirstate.update(m, 'n')
1043 repo.dirstate.update(mm, 'n', st_mtime=0)
1044 repo.dirstate.update(mm, 'n', st_mtime=0)
1044 repo.dirstate.forget(forget)
1045 repo.dirstate.forget(forget)
1045
1046
1046 if not msg:
1047 if not msg:
1047 if not message:
1048 if not message:
1048 message = "patch queue: %s\n" % patchfn
1049 message = "patch queue: %s\n" % patchfn
1049 else:
1050 else:
1050 message = "\n".join(message)
1051 message = "\n".join(message)
1051 else:
1052 else:
1052 message = msg
1053 message = msg
1053
1054
1054 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1055 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1055 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
1056 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
1056 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1057 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1057 self.applied_dirty = 1
1058 self.applied_dirty = 1
1058 else:
1059 else:
1059 self.printdiff(repo, patchparent, fp=patchf)
1060 self.printdiff(repo, patchparent, fp=patchf)
1060 patchf.close()
1061 patchf.close()
1061 self.pop(repo, force=True, wlock=wlock)
1062 self.pop(repo, force=True, wlock=wlock)
1062 self.push(repo, force=True, wlock=wlock)
1063 self.push(repo, force=True, wlock=wlock)
1063
1064
1064 def init(self, repo, create=False):
1065 def init(self, repo, create=False):
1065 if os.path.isdir(self.path):
1066 if os.path.isdir(self.path):
1066 raise util.Abort(_("patch queue directory already exists"))
1067 raise util.Abort(_("patch queue directory already exists"))
1067 os.mkdir(self.path)
1068 os.mkdir(self.path)
1068 if create:
1069 if create:
1069 return self.qrepo(create=True)
1070 return self.qrepo(create=True)
1070
1071
1071 def unapplied(self, repo, patch=None):
1072 def unapplied(self, repo, patch=None):
1072 if patch and patch not in self.series:
1073 if patch and patch not in self.series:
1073 raise util.Abort(_("patch %s is not in series file") % patch)
1074 raise util.Abort(_("patch %s is not in series file") % patch)
1074 if not patch:
1075 if not patch:
1075 start = self.series_end()
1076 start = self.series_end()
1076 else:
1077 else:
1077 start = self.series.index(patch) + 1
1078 start = self.series.index(patch) + 1
1078 unapplied = []
1079 unapplied = []
1079 for i in xrange(start, len(self.series)):
1080 for i in xrange(start, len(self.series)):
1080 pushable, reason = self.pushable(i)
1081 pushable, reason = self.pushable(i)
1081 if pushable:
1082 if pushable:
1082 unapplied.append((i, self.series[i]))
1083 unapplied.append((i, self.series[i]))
1083 self.explain_pushable(i)
1084 self.explain_pushable(i)
1084 return unapplied
1085 return unapplied
1085
1086
1086 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,
1087 summary=False):
1088 summary=False):
1088 def displayname(patchname):
1089 def displayname(patchname):
1089 if summary:
1090 if summary:
1090 msg = self.readheaders(patchname)[0]
1091 msg = self.readheaders(patchname)[0]
1091 msg = msg and ': ' + msg[0] or ': '
1092 msg = msg and ': ' + msg[0] or ': '
1092 else:
1093 else:
1093 msg = ''
1094 msg = ''
1094 return '%s%s' % (patchname, msg)
1095 return '%s%s' % (patchname, msg)
1095
1096
1096 def pname(i):
1097 def pname(i):
1097 if status == 'A':
1098 if status == 'A':
1098 return self.applied[i].name
1099 return self.applied[i].name
1099 else:
1100 else:
1100 return self.series[i]
1101 return self.series[i]
1101
1102
1102 unapplied = self.series_end(all_patches=True)
1103 unapplied = self.series_end(all_patches=True)
1103 if not length:
1104 if not length:
1104 length = len(self.series) - start
1105 length = len(self.series) - start
1105 if not missing:
1106 if not missing:
1106 for i in range(start, start+length):
1107 for i in range(start, start+length):
1107 pfx = ''
1108 pfx = ''
1108 patch = pname(i)
1109 patch = pname(i)
1109 if self.ui.verbose:
1110 if self.ui.verbose:
1110 if i < unapplied:
1111 if i < unapplied:
1111 status = 'A'
1112 status = 'A'
1112 elif self.pushable(i)[0]:
1113 elif self.pushable(i)[0]:
1113 status = 'U'
1114 status = 'U'
1114 else:
1115 else:
1115 status = 'G'
1116 status = 'G'
1116 pfx = '%d %s ' % (i, status)
1117 pfx = '%d %s ' % (i, status)
1117 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1118 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1118 else:
1119 else:
1119 msng_list = []
1120 msng_list = []
1120 for root, dirs, files in os.walk(self.path):
1121 for root, dirs, files in os.walk(self.path):
1121 d = root[len(self.path) + 1:]
1122 d = root[len(self.path) + 1:]
1122 for f in files:
1123 for f in files:
1123 fl = os.path.join(d, f)
1124 fl = os.path.join(d, f)
1124 if (fl not in self.series and
1125 if (fl not in self.series and
1125 fl not in (self.status_path, self.series_path)
1126 fl not in (self.status_path, self.series_path)
1126 and not fl.startswith('.')):
1127 and not fl.startswith('.')):
1127 msng_list.append(fl)
1128 msng_list.append(fl)
1128 msng_list.sort()
1129 msng_list.sort()
1129 for x in msng_list:
1130 for x in msng_list:
1130 pfx = self.ui.verbose and ('D ') or ''
1131 pfx = self.ui.verbose and ('D ') or ''
1131 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1132 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1132
1133
1133 def issaveline(self, l):
1134 def issaveline(self, l):
1134 if l.name == '.hg.patches.save.line':
1135 if l.name == '.hg.patches.save.line':
1135 return True
1136 return True
1136
1137
1137 def qrepo(self, create=False):
1138 def qrepo(self, create=False):
1138 if create or os.path.isdir(self.join(".hg")):
1139 if create or os.path.isdir(self.join(".hg")):
1139 return hg.repository(self.ui, path=self.path, create=create)
1140 return hg.repository(self.ui, path=self.path, create=create)
1140
1141
1141 def restore(self, repo, rev, delete=None, qupdate=None):
1142 def restore(self, repo, rev, delete=None, qupdate=None):
1142 c = repo.changelog.read(rev)
1143 c = repo.changelog.read(rev)
1143 desc = c[4].strip()
1144 desc = c[4].strip()
1144 lines = desc.splitlines()
1145 lines = desc.splitlines()
1145 i = 0
1146 i = 0
1146 datastart = None
1147 datastart = None
1147 series = []
1148 series = []
1148 applied = []
1149 applied = []
1149 qpp = None
1150 qpp = None
1150 for i in xrange(0, len(lines)):
1151 for i in xrange(0, len(lines)):
1151 if lines[i] == 'Patch Data:':
1152 if lines[i] == 'Patch Data:':
1152 datastart = i + 1
1153 datastart = i + 1
1153 elif lines[i].startswith('Dirstate:'):
1154 elif lines[i].startswith('Dirstate:'):
1154 l = lines[i].rstrip()
1155 l = lines[i].rstrip()
1155 l = l[10:].split(' ')
1156 l = l[10:].split(' ')
1156 qpp = [ hg.bin(x) for x in l ]
1157 qpp = [ hg.bin(x) for x in l ]
1157 elif datastart != None:
1158 elif datastart != None:
1158 l = lines[i].rstrip()
1159 l = lines[i].rstrip()
1159 se = statusentry(l)
1160 se = statusentry(l)
1160 file_ = se.name
1161 file_ = se.name
1161 if se.rev:
1162 if se.rev:
1162 applied.append(se)
1163 applied.append(se)
1163 else:
1164 else:
1164 series.append(file_)
1165 series.append(file_)
1165 if datastart == None:
1166 if datastart == None:
1166 self.ui.warn("No saved patch data found\n")
1167 self.ui.warn("No saved patch data found\n")
1167 return 1
1168 return 1
1168 self.ui.warn("restoring status: %s\n" % lines[0])
1169 self.ui.warn("restoring status: %s\n" % lines[0])
1169 self.full_series = series
1170 self.full_series = series
1170 self.applied = applied
1171 self.applied = applied
1171 self.parse_series()
1172 self.parse_series()
1172 self.series_dirty = 1
1173 self.series_dirty = 1
1173 self.applied_dirty = 1
1174 self.applied_dirty = 1
1174 heads = repo.changelog.heads()
1175 heads = repo.changelog.heads()
1175 if delete:
1176 if delete:
1176 if rev not in heads:
1177 if rev not in heads:
1177 self.ui.warn("save entry has children, leaving it alone\n")
1178 self.ui.warn("save entry has children, leaving it alone\n")
1178 else:
1179 else:
1179 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1180 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1180 pp = repo.dirstate.parents()
1181 pp = repo.dirstate.parents()
1181 if rev in pp:
1182 if rev in pp:
1182 update = True
1183 update = True
1183 else:
1184 else:
1184 update = False
1185 update = False
1185 self.strip(repo, rev, update=update, backup='strip')
1186 self.strip(repo, rev, update=update, backup='strip')
1186 if qpp:
1187 if qpp:
1187 self.ui.warn("saved queue repository parents: %s %s\n" %
1188 self.ui.warn("saved queue repository parents: %s %s\n" %
1188 (hg.short(qpp[0]), hg.short(qpp[1])))
1189 (hg.short(qpp[0]), hg.short(qpp[1])))
1189 if qupdate:
1190 if qupdate:
1190 print "queue directory updating"
1191 print "queue directory updating"
1191 r = self.qrepo()
1192 r = self.qrepo()
1192 if not r:
1193 if not r:
1193 self.ui.warn("Unable to load queue repository\n")
1194 self.ui.warn("Unable to load queue repository\n")
1194 return 1
1195 return 1
1195 hg.clean(r, qpp[0])
1196 hg.clean(r, qpp[0])
1196
1197
1197 def save(self, repo, msg=None):
1198 def save(self, repo, msg=None):
1198 if len(self.applied) == 0:
1199 if len(self.applied) == 0:
1199 self.ui.warn("save: no patches applied, exiting\n")
1200 self.ui.warn("save: no patches applied, exiting\n")
1200 return 1
1201 return 1
1201 if self.issaveline(self.applied[-1]):
1202 if self.issaveline(self.applied[-1]):
1202 self.ui.warn("status is already saved\n")
1203 self.ui.warn("status is already saved\n")
1203 return 1
1204 return 1
1204
1205
1205 ar = [ ':' + x for x in self.full_series ]
1206 ar = [ ':' + x for x in self.full_series ]
1206 if not msg:
1207 if not msg:
1207 msg = "hg patches saved state"
1208 msg = "hg patches saved state"
1208 else:
1209 else:
1209 msg = "hg patches: " + msg.rstrip('\r\n')
1210 msg = "hg patches: " + msg.rstrip('\r\n')
1210 r = self.qrepo()
1211 r = self.qrepo()
1211 if r:
1212 if r:
1212 pp = r.dirstate.parents()
1213 pp = r.dirstate.parents()
1213 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]))
1214 msg += "\n\nPatch Data:\n"
1215 msg += "\n\nPatch Data:\n"
1215 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
1216 "\n".join(ar) + '\n' or "")
1217 "\n".join(ar) + '\n' or "")
1217 n = repo.commit(None, text, user=None, force=1)
1218 n = repo.commit(None, text, user=None, force=1)
1218 if not n:
1219 if not n:
1219 self.ui.warn("repo commit failed\n")
1220 self.ui.warn("repo commit failed\n")
1220 return 1
1221 return 1
1221 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1222 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1222 self.applied_dirty = 1
1223 self.applied_dirty = 1
1223
1224
1224 def full_series_end(self):
1225 def full_series_end(self):
1225 if len(self.applied) > 0:
1226 if len(self.applied) > 0:
1226 p = self.applied[-1].name
1227 p = self.applied[-1].name
1227 end = self.find_series(p)
1228 end = self.find_series(p)
1228 if end == None:
1229 if end == None:
1229 return len(self.full_series)
1230 return len(self.full_series)
1230 return end + 1
1231 return end + 1
1231 return 0
1232 return 0
1232
1233
1233 def series_end(self, all_patches=False):
1234 def series_end(self, all_patches=False):
1234 end = 0
1235 end = 0
1235 def next(start):
1236 def next(start):
1236 if all_patches:
1237 if all_patches:
1237 return start
1238 return start
1238 i = start
1239 i = start
1239 while i < len(self.series):
1240 while i < len(self.series):
1240 p, reason = self.pushable(i)
1241 p, reason = self.pushable(i)
1241 if p:
1242 if p:
1242 break
1243 break
1243 self.explain_pushable(i)
1244 self.explain_pushable(i)
1244 i += 1
1245 i += 1
1245 return i
1246 return i
1246 if len(self.applied) > 0:
1247 if len(self.applied) > 0:
1247 p = self.applied[-1].name
1248 p = self.applied[-1].name
1248 try:
1249 try:
1249 end = self.series.index(p)
1250 end = self.series.index(p)
1250 except ValueError:
1251 except ValueError:
1251 return 0
1252 return 0
1252 return next(end + 1)
1253 return next(end + 1)
1253 return next(end)
1254 return next(end)
1254
1255
1255 def appliedname(self, index):
1256 def appliedname(self, index):
1256 pname = self.applied[index].name
1257 pname = self.applied[index].name
1257 if not self.ui.verbose:
1258 if not self.ui.verbose:
1258 p = pname
1259 p = pname
1259 else:
1260 else:
1260 p = str(self.series.index(pname)) + " " + pname
1261 p = str(self.series.index(pname)) + " " + pname
1261 return p
1262 return p
1262
1263
1263 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1264 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1264 force=None):
1265 force=None):
1265 def checkseries(patchname):
1266 def checkseries(patchname):
1266 if patchname in self.series:
1267 if patchname in self.series:
1267 raise util.Abort(_('patch %s is already in the series file')
1268 raise util.Abort(_('patch %s is already in the series file')
1268 % patchname)
1269 % patchname)
1269 def checkfile(patchname):
1270 def checkfile(patchname):
1270 if not force and os.path.exists(self.join(patchname)):
1271 if not force and os.path.exists(self.join(patchname)):
1271 raise util.Abort(_('patch "%s" already exists')
1272 raise util.Abort(_('patch "%s" already exists')
1272 % patchname)
1273 % patchname)
1273
1274
1274 if rev:
1275 if rev:
1275 if files:
1276 if files:
1276 raise util.Abort(_('option "-r" not valid when importing '
1277 raise util.Abort(_('option "-r" not valid when importing '
1277 'files'))
1278 'files'))
1278 rev = [int(r) for r in cmdutil.revrange(self.ui, repo, rev)]
1279 rev = [int(r) for r in cmdutil.revrange(self.ui, repo, rev)]
1279 rev.sort(lambda x, y: cmp(y, x))
1280 rev.sort(lambda x, y: cmp(y, x))
1280 if (len(files) > 1 or len(rev) > 1) and patchname:
1281 if (len(files) > 1 or len(rev) > 1) and patchname:
1281 raise util.Abort(_('option "-n" not valid when importing multiple '
1282 raise util.Abort(_('option "-n" not valid when importing multiple '
1282 'patches'))
1283 'patches'))
1283 i = 0
1284 i = 0
1284 added = []
1285 added = []
1285 if rev:
1286 if rev:
1286 # If mq patches are applied, we can only import revisions
1287 # If mq patches are applied, we can only import revisions
1287 # that form a linear path to qbase.
1288 # that form a linear path to qbase.
1288 # Otherwise, they should form a linear path to a head.
1289 # Otherwise, they should form a linear path to a head.
1289 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1290 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1290 if len(heads) > 1:
1291 if len(heads) > 1:
1291 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 '
1292 'branch') % rev[-1])
1293 'branch') % rev[-1])
1293 if self.applied:
1294 if self.applied:
1294 base = revlog.hex(repo.changelog.node(rev[0]))
1295 base = revlog.hex(repo.changelog.node(rev[0]))
1295 if base in [n.rev for n in self.applied]:
1296 if base in [n.rev for n in self.applied]:
1296 raise util.Abort(_('revision %d is already managed')
1297 raise util.Abort(_('revision %d is already managed')
1297 % rev[0])
1298 % rev[0])
1298 if heads != [revlog.bin(self.applied[-1].rev)]:
1299 if heads != [revlog.bin(self.applied[-1].rev)]:
1299 raise util.Abort(_('revision %d is not the parent of '
1300 raise util.Abort(_('revision %d is not the parent of '
1300 'the queue') % rev[0])
1301 'the queue') % rev[0])
1301 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1302 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1302 lastparent = repo.changelog.parentrevs(base)[0]
1303 lastparent = repo.changelog.parentrevs(base)[0]
1303 else:
1304 else:
1304 if heads != [repo.changelog.node(rev[0])]:
1305 if heads != [repo.changelog.node(rev[0])]:
1305 raise util.Abort(_('revision %d has unmanaged children')
1306 raise util.Abort(_('revision %d has unmanaged children')
1306 % rev[0])
1307 % rev[0])
1307 lastparent = None
1308 lastparent = None
1308
1309
1309 for r in rev:
1310 for r in rev:
1310 p1, p2 = repo.changelog.parentrevs(r)
1311 p1, p2 = repo.changelog.parentrevs(r)
1311 n = repo.changelog.node(r)
1312 n = repo.changelog.node(r)
1312 if p2 != -1:
1313 if p2 != -1:
1313 raise util.Abort(_('cannot import merge revision %d') % r)
1314 raise util.Abort(_('cannot import merge revision %d') % r)
1314 if lastparent and lastparent != r:
1315 if lastparent and lastparent != r:
1315 raise util.Abort(_('revision %d is not the parent of %d')
1316 raise util.Abort(_('revision %d is not the parent of %d')
1316 % (r, lastparent))
1317 % (r, lastparent))
1317 lastparent = p1
1318 lastparent = p1
1318
1319
1319 if not patchname:
1320 if not patchname:
1320 patchname = '%d.diff' % r
1321 patchname = '%d.diff' % r
1321 checkseries(patchname)
1322 checkseries(patchname)
1322 checkfile(patchname)
1323 checkfile(patchname)
1323 self.full_series.insert(0, patchname)
1324 self.full_series.insert(0, patchname)
1324
1325
1325 patchf = self.opener(patchname, "w")
1326 patchf = self.opener(patchname, "w")
1326 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1327 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1327 patchf.close()
1328 patchf.close()
1328
1329
1329 se = statusentry(revlog.hex(n), patchname)
1330 se = statusentry(revlog.hex(n), patchname)
1330 self.applied.insert(0, se)
1331 self.applied.insert(0, se)
1331
1332
1332 added.append(patchname)
1333 added.append(patchname)
1333 patchname = None
1334 patchname = None
1334 self.parse_series()
1335 self.parse_series()
1335 self.applied_dirty = 1
1336 self.applied_dirty = 1
1336
1337
1337 for filename in files:
1338 for filename in files:
1338 if existing:
1339 if existing:
1339 if not patchname:
1340 if not patchname:
1340 patchname = filename
1341 patchname = filename
1341 if not os.path.isfile(self.join(patchname)):
1342 if not os.path.isfile(self.join(patchname)):
1342 raise util.Abort(_("patch %s does not exist") % patchname)
1343 raise util.Abort(_("patch %s does not exist") % patchname)
1343 else:
1344 else:
1344 try:
1345 try:
1345 text = file(filename).read()
1346 text = file(filename).read()
1346 except IOError:
1347 except IOError:
1347 raise util.Abort(_("unable to read %s") % patchname)
1348 raise util.Abort(_("unable to read %s") % patchname)
1348 if not patchname:
1349 if not patchname:
1349 patchname = os.path.basename(filename)
1350 patchname = os.path.basename(filename)
1350 checkfile(patchname)
1351 checkfile(patchname)
1351 patchf = self.opener(patchname, "w")
1352 patchf = self.opener(patchname, "w")
1352 patchf.write(text)
1353 patchf.write(text)
1353 checkseries(patchname)
1354 checkseries(patchname)
1354 index = self.full_series_end() + i
1355 index = self.full_series_end() + i
1355 self.full_series[index:index] = [patchname]
1356 self.full_series[index:index] = [patchname]
1356 self.parse_series()
1357 self.parse_series()
1357 self.ui.warn("adding %s to series file\n" % patchname)
1358 self.ui.warn("adding %s to series file\n" % patchname)
1358 i += 1
1359 i += 1
1359 added.append(patchname)
1360 added.append(patchname)
1360 patchname = None
1361 patchname = None
1361 self.series_dirty = 1
1362 self.series_dirty = 1
1362 qrepo = self.qrepo()
1363 qrepo = self.qrepo()
1363 if qrepo:
1364 if qrepo:
1364 qrepo.add(added)
1365 qrepo.add(added)
1365
1366
1366 def delete(ui, repo, *patches, **opts):
1367 def delete(ui, repo, *patches, **opts):
1367 """remove patches from queue
1368 """remove patches from queue
1368
1369
1369 With --rev, mq will stop managing the named revisions. The
1370 With --rev, mq will stop managing the named revisions. The
1370 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
1371 is useful when the patches have been applied upstream.
1372 is useful when the patches have been applied upstream.
1372
1373
1373 Otherwise, the patches must not be applied.
1374 Otherwise, the patches must not be applied.
1374
1375
1375 With --keep, the patch files are preserved in the patch directory."""
1376 With --keep, the patch files are preserved in the patch directory."""
1376 q = repo.mq
1377 q = repo.mq
1377 q.delete(repo, patches, opts)
1378 q.delete(repo, patches, opts)
1378 q.save_dirty()
1379 q.save_dirty()
1379 return 0
1380 return 0
1380
1381
1381 def applied(ui, repo, patch=None, **opts):
1382 def applied(ui, repo, patch=None, **opts):
1382 """print the patches already applied"""
1383 """print the patches already applied"""
1383 q = repo.mq
1384 q = repo.mq
1384 if patch:
1385 if patch:
1385 if patch not in q.series:
1386 if patch not in q.series:
1386 raise util.Abort(_("patch %s is not in series file") % patch)
1387 raise util.Abort(_("patch %s is not in series file") % patch)
1387 end = q.series.index(patch) + 1
1388 end = q.series.index(patch) + 1
1388 else:
1389 else:
1389 end = len(q.applied)
1390 end = len(q.applied)
1390 if not end:
1391 if not end:
1391 return
1392 return
1392
1393
1393 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'))
1394
1395
1395 def unapplied(ui, repo, patch=None, **opts):
1396 def unapplied(ui, repo, patch=None, **opts):
1396 """print the patches not yet applied"""
1397 """print the patches not yet applied"""
1397 q = repo.mq
1398 q = repo.mq
1398 if patch:
1399 if patch:
1399 if patch not in q.series:
1400 if patch not in q.series:
1400 raise util.Abort(_("patch %s is not in series file") % patch)
1401 raise util.Abort(_("patch %s is not in series file") % patch)
1401 start = q.series.index(patch) + 1
1402 start = q.series.index(patch) + 1
1402 else:
1403 else:
1403 start = q.series_end()
1404 start = q.series_end()
1404 q.qseries(repo, start=start, summary=opts.get('summary'))
1405 q.qseries(repo, start=start, summary=opts.get('summary'))
1405
1406
1406 def qimport(ui, repo, *filename, **opts):
1407 def qimport(ui, repo, *filename, **opts):
1407 """import a patch
1408 """import a patch
1408
1409
1409 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
1410 give it a new one with --name.
1411 give it a new one with --name.
1411
1412
1412 You can register an existing patch inside the patch directory
1413 You can register an existing patch inside the patch directory
1413 with the --existing flag.
1414 with the --existing flag.
1414
1415
1415 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.
1416
1417
1417 An existing changeset may be placed under mq control with --rev
1418 An existing changeset may be placed under mq control with --rev
1418 (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).
1419 """
1420 """
1420 q = repo.mq
1421 q = repo.mq
1421 q.qimport(repo, filename, patchname=opts['name'],
1422 q.qimport(repo, filename, patchname=opts['name'],
1422 existing=opts['existing'], force=opts['force'], rev=opts['rev'])
1423 existing=opts['existing'], force=opts['force'], rev=opts['rev'])
1423 q.save_dirty()
1424 q.save_dirty()
1424 return 0
1425 return 0
1425
1426
1426 def init(ui, repo, **opts):
1427 def init(ui, repo, **opts):
1427 """init a new queue repository
1428 """init a new queue repository
1428
1429
1429 The queue repository is unversioned by default. If -c is
1430 The queue repository is unversioned by default. If -c is
1430 specified, qinit will create a separate nested repository
1431 specified, qinit will create a separate nested repository
1431 for patches. Use qcommit to commit changes to this queue
1432 for patches. Use qcommit to commit changes to this queue
1432 repository."""
1433 repository."""
1433 q = repo.mq
1434 q = repo.mq
1434 r = q.init(repo, create=opts['create_repo'])
1435 r = q.init(repo, create=opts['create_repo'])
1435 q.save_dirty()
1436 q.save_dirty()
1436 if r:
1437 if r:
1437 fp = r.wopener('.hgignore', 'w')
1438 fp = r.wopener('.hgignore', 'w')
1438 print >> fp, 'syntax: glob'
1439 print >> fp, 'syntax: glob'
1439 print >> fp, 'status'
1440 print >> fp, 'status'
1440 fp.close()
1441 fp.close()
1441 r.wopener('series', 'w').close()
1442 r.wopener('series', 'w').close()
1442 r.add(['.hgignore', 'series'])
1443 r.add(['.hgignore', 'series'])
1443 return 0
1444 return 0
1444
1445
1445 def clone(ui, source, dest=None, **opts):
1446 def clone(ui, source, dest=None, **opts):
1446 '''clone main and patch repository at same time
1447 '''clone main and patch repository at same time
1447
1448
1448 If source is local, destination will have no patches applied. If
1449 If source is local, destination will have no patches applied. If
1449 source is remote, this command can not check if patches are
1450 source is remote, this command can not check if patches are
1450 applied in source, so cannot guarantee that patches are not
1451 applied in source, so cannot guarantee that patches are not
1451 applied in destination. If you clone remote repository, be sure
1452 applied in destination. If you clone remote repository, be sure
1452 before that it has no patches applied.
1453 before that it has no patches applied.
1453
1454
1454 Source patch repository is looked for in <src>/.hg/patches by
1455 Source patch repository is looked for in <src>/.hg/patches by
1455 default. Use -p <url> to change.
1456 default. Use -p <url> to change.
1456 '''
1457 '''
1457 commands.setremoteconfig(ui, opts)
1458 commands.setremoteconfig(ui, opts)
1458 if dest is None:
1459 if dest is None:
1459 dest = hg.defaultdest(source)
1460 dest = hg.defaultdest(source)
1460 sr = hg.repository(ui, ui.expandpath(source))
1461 sr = hg.repository(ui, ui.expandpath(source))
1461 qbase, destrev = None, None
1462 qbase, destrev = None, None
1462 if sr.local():
1463 if sr.local():
1463 reposetup(ui, sr)
1464 reposetup(ui, sr)
1464 if sr.mq.applied:
1465 if sr.mq.applied:
1465 qbase = revlog.bin(sr.mq.applied[0].rev)
1466 qbase = revlog.bin(sr.mq.applied[0].rev)
1466 if not hg.islocal(dest):
1467 if not hg.islocal(dest):
1467 destrev = sr.parents(qbase)[0]
1468 destrev = sr.parents(qbase)[0]
1468 ui.note(_('cloning main repo\n'))
1469 ui.note(_('cloning main repo\n'))
1469 sr, dr = hg.clone(ui, sr, dest,
1470 sr, dr = hg.clone(ui, sr, dest,
1470 pull=opts['pull'],
1471 pull=opts['pull'],
1471 rev=destrev,
1472 rev=destrev,
1472 update=False,
1473 update=False,
1473 stream=opts['uncompressed'])
1474 stream=opts['uncompressed'])
1474 ui.note(_('cloning patch repo\n'))
1475 ui.note(_('cloning patch repo\n'))
1475 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'),
1476 dr.url() + '/.hg/patches',
1477 dr.url() + '/.hg/patches',
1477 pull=opts['pull'],
1478 pull=opts['pull'],
1478 update=not opts['noupdate'],
1479 update=not opts['noupdate'],
1479 stream=opts['uncompressed'])
1480 stream=opts['uncompressed'])
1480 if dr.local():
1481 if dr.local():
1481 if qbase:
1482 if qbase:
1482 ui.note(_('stripping applied patches from destination repo\n'))
1483 ui.note(_('stripping applied patches from destination repo\n'))
1483 reposetup(ui, dr)
1484 reposetup(ui, dr)
1484 dr.mq.strip(dr, qbase, update=False, backup=None)
1485 dr.mq.strip(dr, qbase, update=False, backup=None)
1485 if not opts['noupdate']:
1486 if not opts['noupdate']:
1486 ui.note(_('updating destination repo\n'))
1487 ui.note(_('updating destination repo\n'))
1487 hg.update(dr, dr.changelog.tip())
1488 hg.update(dr, dr.changelog.tip())
1488
1489
1489 def commit(ui, repo, *pats, **opts):
1490 def commit(ui, repo, *pats, **opts):
1490 """commit changes in the queue repository"""
1491 """commit changes in the queue repository"""
1491 q = repo.mq
1492 q = repo.mq
1492 r = q.qrepo()
1493 r = q.qrepo()
1493 if not r: raise util.Abort('no queue repository')
1494 if not r: raise util.Abort('no queue repository')
1494 commands.commit(r.ui, r, *pats, **opts)
1495 commands.commit(r.ui, r, *pats, **opts)
1495
1496
1496 def series(ui, repo, **opts):
1497 def series(ui, repo, **opts):
1497 """print the entire series file"""
1498 """print the entire series file"""
1498 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1499 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1499 return 0
1500 return 0
1500
1501
1501 def top(ui, repo, **opts):
1502 def top(ui, repo, **opts):
1502 """print the name of the current patch"""
1503 """print the name of the current patch"""
1503 q = repo.mq
1504 q = repo.mq
1504 t = len(q.applied)
1505 t = len(q.applied)
1505 if t:
1506 if t:
1506 return q.qseries(repo, start=t-1, length=1, status='A',
1507 return q.qseries(repo, start=t-1, length=1, status='A',
1507 summary=opts.get('summary'))
1508 summary=opts.get('summary'))
1508 else:
1509 else:
1509 ui.write("No patches applied\n")
1510 ui.write("No patches applied\n")
1510 return 1
1511 return 1
1511
1512
1512 def next(ui, repo, **opts):
1513 def next(ui, repo, **opts):
1513 """print the name of the next patch"""
1514 """print the name of the next patch"""
1514 q = repo.mq
1515 q = repo.mq
1515 end = q.series_end()
1516 end = q.series_end()
1516 if end == len(q.series):
1517 if end == len(q.series):
1517 ui.write("All patches applied\n")
1518 ui.write("All patches applied\n")
1518 return 1
1519 return 1
1519 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'))
1520
1521
1521 def prev(ui, repo, **opts):
1522 def prev(ui, repo, **opts):
1522 """print the name of the previous patch"""
1523 """print the name of the previous patch"""
1523 q = repo.mq
1524 q = repo.mq
1524 l = len(q.applied)
1525 l = len(q.applied)
1525 if l == 1:
1526 if l == 1:
1526 ui.write("Only one patch applied\n")
1527 ui.write("Only one patch applied\n")
1527 return 1
1528 return 1
1528 if not l:
1529 if not l:
1529 ui.write("No patches applied\n")
1530 ui.write("No patches applied\n")
1530 return 1
1531 return 1
1531 return q.qseries(repo, start=l-2, length=1, status='A',
1532 return q.qseries(repo, start=l-2, length=1, status='A',
1532 summary=opts.get('summary'))
1533 summary=opts.get('summary'))
1533
1534
1534 def new(ui, repo, patch, **opts):
1535 def new(ui, repo, patch, **opts):
1535 """create a new patch
1536 """create a new patch
1536
1537
1537 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
1538 (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
1539 changes unless -f is specified, in which case the patch will
1540 changes unless -f is specified, in which case the patch will
1540 be initialised with them.
1541 be initialised with them.
1541
1542
1542 -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.
1543 If none is specified, the patch header is empty and the
1544 If none is specified, the patch header is empty and the
1544 commit message is 'New patch: PATCH'"""
1545 commit message is 'New patch: PATCH'"""
1545 q = repo.mq
1546 q = repo.mq
1546 message = commands.logmessage(opts)
1547 message = commands.logmessage(opts)
1547 if opts['edit']:
1548 if opts['edit']:
1548 message = ui.edit(message, ui.username())
1549 message = ui.edit(message, ui.username())
1549 q.new(repo, patch, msg=message, force=opts['force'])
1550 q.new(repo, patch, msg=message, force=opts['force'])
1550 q.save_dirty()
1551 q.save_dirty()
1551 return 0
1552 return 0
1552
1553
1553 def refresh(ui, repo, *pats, **opts):
1554 def refresh(ui, repo, *pats, **opts):
1554 """update the current patch
1555 """update the current patch
1555
1556
1556 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
1557 the modifications that match those patterns; the remaining modifications
1558 the modifications that match those patterns; the remaining modifications
1558 will remain in the working directory.
1559 will remain in the working directory.
1559 """
1560 """
1560 q = repo.mq
1561 q = repo.mq
1561 message = commands.logmessage(opts)
1562 message = commands.logmessage(opts)
1562 if opts['edit']:
1563 if opts['edit']:
1563 if message:
1564 if message:
1564 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1565 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1565 patch = q.applied[-1].name
1566 patch = q.applied[-1].name
1566 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1567 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1567 message = ui.edit('\n'.join(message), user or ui.username())
1568 message = ui.edit('\n'.join(message), user or ui.username())
1568 ret = q.refresh(repo, pats, msg=message, **opts)
1569 ret = q.refresh(repo, pats, msg=message, **opts)
1569 q.save_dirty()
1570 q.save_dirty()
1570 return ret
1571 return ret
1571
1572
1572 def diff(ui, repo, *pats, **opts):
1573 def diff(ui, repo, *pats, **opts):
1573 """diff of the current patch"""
1574 """diff of the current patch"""
1574 repo.mq.diff(repo, pats, opts)
1575 repo.mq.diff(repo, pats, opts)
1575 return 0
1576 return 0
1576
1577
1577 def fold(ui, repo, *files, **opts):
1578 def fold(ui, repo, *files, **opts):
1578 """fold the named patches into the current patch
1579 """fold the named patches into the current patch
1579
1580
1580 Patches must not yet be applied. Each patch will be successively
1581 Patches must not yet be applied. Each patch will be successively
1581 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
1582 patches apply successfully, the current patch will be refreshed
1583 patches apply successfully, the current patch will be refreshed
1583 with the new cumulative patch, and the folded patches will
1584 with the new cumulative patch, and the folded patches will
1584 be deleted. With -k/--keep, the folded patch files will not
1585 be deleted. With -k/--keep, the folded patch files will not
1585 be removed afterwards.
1586 be removed afterwards.
1586
1587
1587 The header for each folded patch will be concatenated with
1588 The header for each folded patch will be concatenated with
1588 the current patch header, separated by a line of '* * *'."""
1589 the current patch header, separated by a line of '* * *'."""
1589
1590
1590 q = repo.mq
1591 q = repo.mq
1591
1592
1592 if not files:
1593 if not files:
1593 raise util.Abort(_('qfold requires at least one patch name'))
1594 raise util.Abort(_('qfold requires at least one patch name'))
1594 if not q.check_toppatch(repo):
1595 if not q.check_toppatch(repo):
1595 raise util.Abort(_('No patches applied'))
1596 raise util.Abort(_('No patches applied'))
1596
1597
1597 message = commands.logmessage(opts)
1598 message = commands.logmessage(opts)
1598 if opts['edit']:
1599 if opts['edit']:
1599 if message:
1600 if message:
1600 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1601 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1601
1602
1602 parent = q.lookup('qtip')
1603 parent = q.lookup('qtip')
1603 patches = []
1604 patches = []
1604 messages = []
1605 messages = []
1605 for f in files:
1606 for f in files:
1606 p = q.lookup(f)
1607 p = q.lookup(f)
1607 if p in patches or p == parent:
1608 if p in patches or p == parent:
1608 ui.warn(_('Skipping already folded patch %s') % p)
1609 ui.warn(_('Skipping already folded patch %s') % p)
1609 if q.isapplied(p):
1610 if q.isapplied(p):
1610 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1611 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1611 patches.append(p)
1612 patches.append(p)
1612
1613
1613 for p in patches:
1614 for p in patches:
1614 if not message:
1615 if not message:
1615 messages.append(q.readheaders(p)[0])
1616 messages.append(q.readheaders(p)[0])
1616 pf = q.join(p)
1617 pf = q.join(p)
1617 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1618 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1618 if not patchsuccess:
1619 if not patchsuccess:
1619 raise util.Abort(_('Error folding patch %s') % p)
1620 raise util.Abort(_('Error folding patch %s') % p)
1620 patch.updatedir(ui, repo, files)
1621 patch.updatedir(ui, repo, files)
1621
1622
1622 if not message:
1623 if not message:
1623 message, comments, user = q.readheaders(parent)[0:3]
1624 message, comments, user = q.readheaders(parent)[0:3]
1624 for msg in messages:
1625 for msg in messages:
1625 message.append('* * *')
1626 message.append('* * *')
1626 message.extend(msg)
1627 message.extend(msg)
1627 message = '\n'.join(message)
1628 message = '\n'.join(message)
1628
1629
1629 if opts['edit']:
1630 if opts['edit']:
1630 message = ui.edit(message, user or ui.username())
1631 message = ui.edit(message, user or ui.username())
1631
1632
1632 q.refresh(repo, msg=message)
1633 q.refresh(repo, msg=message)
1633 q.delete(repo, patches, opts)
1634 q.delete(repo, patches, opts)
1634 q.save_dirty()
1635 q.save_dirty()
1635
1636
1636 def guard(ui, repo, *args, **opts):
1637 def guard(ui, repo, *args, **opts):
1637 '''set or print guards for a patch
1638 '''set or print guards for a patch
1638
1639
1639 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
1640 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
1641 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
1642 a negative guard ("-foo") is never pushed if the qselect command
1643 a negative guard ("-foo") is never pushed if the qselect command
1643 has activated it.
1644 has activated it.
1644
1645
1645 With no arguments, print the currently active guards.
1646 With no arguments, print the currently active guards.
1646 With arguments, set guards for the named patch.
1647 With arguments, set guards for the named patch.
1647
1648
1648 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
1649 hg will not interpret "-foo" as an option):
1650 hg will not interpret "-foo" as an option):
1650 hg qguard -- -foo
1651 hg qguard -- -foo
1651
1652
1652 To set guards on another patch:
1653 To set guards on another patch:
1653 hg qguard other.patch +2.6.17 -stable
1654 hg qguard other.patch +2.6.17 -stable
1654 '''
1655 '''
1655 def status(idx):
1656 def status(idx):
1656 guards = q.series_guards[idx] or ['unguarded']
1657 guards = q.series_guards[idx] or ['unguarded']
1657 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1658 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1658 q = repo.mq
1659 q = repo.mq
1659 patch = None
1660 patch = None
1660 args = list(args)
1661 args = list(args)
1661 if opts['list']:
1662 if opts['list']:
1662 if args or opts['none']:
1663 if args or opts['none']:
1663 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1664 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1664 for i in xrange(len(q.series)):
1665 for i in xrange(len(q.series)):
1665 status(i)
1666 status(i)
1666 return
1667 return
1667 if not args or args[0][0:1] in '-+':
1668 if not args or args[0][0:1] in '-+':
1668 if not q.applied:
1669 if not q.applied:
1669 raise util.Abort(_('no patches applied'))
1670 raise util.Abort(_('no patches applied'))
1670 patch = q.applied[-1].name
1671 patch = q.applied[-1].name
1671 if patch is None and args[0][0:1] not in '-+':
1672 if patch is None and args[0][0:1] not in '-+':
1672 patch = args.pop(0)
1673 patch = args.pop(0)
1673 if patch is None:
1674 if patch is None:
1674 raise util.Abort(_('no patch to work with'))
1675 raise util.Abort(_('no patch to work with'))
1675 if args or opts['none']:
1676 if args or opts['none']:
1676 q.set_guards(q.find_series(patch), args)
1677 q.set_guards(q.find_series(patch), args)
1677 q.save_dirty()
1678 q.save_dirty()
1678 else:
1679 else:
1679 status(q.series.index(q.lookup(patch)))
1680 status(q.series.index(q.lookup(patch)))
1680
1681
1681 def header(ui, repo, patch=None):
1682 def header(ui, repo, patch=None):
1682 """Print the header of the topmost or specified patch"""
1683 """Print the header of the topmost or specified patch"""
1683 q = repo.mq
1684 q = repo.mq
1684
1685
1685 if patch:
1686 if patch:
1686 patch = q.lookup(patch)
1687 patch = q.lookup(patch)
1687 else:
1688 else:
1688 if not q.applied:
1689 if not q.applied:
1689 ui.write('No patches applied\n')
1690 ui.write('No patches applied\n')
1690 return 1
1691 return 1
1691 patch = q.lookup('qtip')
1692 patch = q.lookup('qtip')
1692 message = repo.mq.readheaders(patch)[0]
1693 message = repo.mq.readheaders(patch)[0]
1693
1694
1694 ui.write('\n'.join(message) + '\n')
1695 ui.write('\n'.join(message) + '\n')
1695
1696
1696 def lastsavename(path):
1697 def lastsavename(path):
1697 (directory, base) = os.path.split(path)
1698 (directory, base) = os.path.split(path)
1698 names = os.listdir(directory)
1699 names = os.listdir(directory)
1699 namere = re.compile("%s.([0-9]+)" % base)
1700 namere = re.compile("%s.([0-9]+)" % base)
1700 maxindex = None
1701 maxindex = None
1701 maxname = None
1702 maxname = None
1702 for f in names:
1703 for f in names:
1703 m = namere.match(f)
1704 m = namere.match(f)
1704 if m:
1705 if m:
1705 index = int(m.group(1))
1706 index = int(m.group(1))
1706 if maxindex == None or index > maxindex:
1707 if maxindex == None or index > maxindex:
1707 maxindex = index
1708 maxindex = index
1708 maxname = f
1709 maxname = f
1709 if maxname:
1710 if maxname:
1710 return (os.path.join(directory, maxname), maxindex)
1711 return (os.path.join(directory, maxname), maxindex)
1711 return (None, None)
1712 return (None, None)
1712
1713
1713 def savename(path):
1714 def savename(path):
1714 (last, index) = lastsavename(path)
1715 (last, index) = lastsavename(path)
1715 if last is None:
1716 if last is None:
1716 index = 0
1717 index = 0
1717 newpath = path + ".%d" % (index + 1)
1718 newpath = path + ".%d" % (index + 1)
1718 return newpath
1719 return newpath
1719
1720
1720 def push(ui, repo, patch=None, **opts):
1721 def push(ui, repo, patch=None, **opts):
1721 """push the next patch onto the stack"""
1722 """push the next patch onto the stack"""
1722 q = repo.mq
1723 q = repo.mq
1723 mergeq = None
1724 mergeq = None
1724
1725
1725 if opts['all']:
1726 if opts['all']:
1726 patch = q.series[-1]
1727 patch = q.series[-1]
1727 if opts['merge']:
1728 if opts['merge']:
1728 if opts['name']:
1729 if opts['name']:
1729 newpath = opts['name']
1730 newpath = opts['name']
1730 else:
1731 else:
1731 newpath, i = lastsavename(q.path)
1732 newpath, i = lastsavename(q.path)
1732 if not newpath:
1733 if not newpath:
1733 ui.warn("no saved queues found, please use -n\n")
1734 ui.warn("no saved queues found, please use -n\n")
1734 return 1
1735 return 1
1735 mergeq = queue(ui, repo.join(""), newpath)
1736 mergeq = queue(ui, repo.join(""), newpath)
1736 ui.warn("merging with queue at: %s\n" % mergeq.path)
1737 ui.warn("merging with queue at: %s\n" % mergeq.path)
1737 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1738 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1738 mergeq=mergeq)
1739 mergeq=mergeq)
1739 q.save_dirty()
1740 q.save_dirty()
1740 return ret
1741 return ret
1741
1742
1742 def pop(ui, repo, patch=None, **opts):
1743 def pop(ui, repo, patch=None, **opts):
1743 """pop the current patch off the stack"""
1744 """pop the current patch off the stack"""
1744 localupdate = True
1745 localupdate = True
1745 if opts['name']:
1746 if opts['name']:
1746 q = queue(ui, repo.join(""), repo.join(opts['name']))
1747 q = queue(ui, repo.join(""), repo.join(opts['name']))
1747 ui.warn('using patch queue: %s\n' % q.path)
1748 ui.warn('using patch queue: %s\n' % q.path)
1748 localupdate = False
1749 localupdate = False
1749 else:
1750 else:
1750 q = repo.mq
1751 q = repo.mq
1751 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'])
1752 q.save_dirty()
1753 q.save_dirty()
1753 return 0
1754 return 0
1754
1755
1755 def rename(ui, repo, patch, name=None, **opts):
1756 def rename(ui, repo, patch, name=None, **opts):
1756 """rename a patch
1757 """rename a patch
1757
1758
1758 With one argument, renames the current patch to PATCH1.
1759 With one argument, renames the current patch to PATCH1.
1759 With two arguments, renames PATCH1 to PATCH2."""
1760 With two arguments, renames PATCH1 to PATCH2."""
1760
1761
1761 q = repo.mq
1762 q = repo.mq
1762
1763
1763 if not name:
1764 if not name:
1764 name = patch
1765 name = patch
1765 patch = None
1766 patch = None
1766
1767
1767 if patch:
1768 if patch:
1768 patch = q.lookup(patch)
1769 patch = q.lookup(patch)
1769 else:
1770 else:
1770 if not q.applied:
1771 if not q.applied:
1771 ui.write(_('No patches applied\n'))
1772 ui.write(_('No patches applied\n'))
1772 return
1773 return
1773 patch = q.lookup('qtip')
1774 patch = q.lookup('qtip')
1774 absdest = q.join(name)
1775 absdest = q.join(name)
1775 if os.path.isdir(absdest):
1776 if os.path.isdir(absdest):
1776 name = os.path.join(name, os.path.basename(patch))
1777 name = os.path.join(name, os.path.basename(patch))
1777 absdest = q.join(name)
1778 absdest = q.join(name)
1778 if os.path.exists(absdest):
1779 if os.path.exists(absdest):
1779 raise util.Abort(_('%s already exists') % absdest)
1780 raise util.Abort(_('%s already exists') % absdest)
1780
1781
1781 if name in q.series:
1782 if name in q.series:
1782 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)
1783
1784
1784 if ui.verbose:
1785 if ui.verbose:
1785 ui.write('Renaming %s to %s\n' % (patch, name))
1786 ui.write('Renaming %s to %s\n' % (patch, name))
1786 i = q.find_series(patch)
1787 i = q.find_series(patch)
1787 q.full_series[i] = name
1788 q.full_series[i] = name
1788 q.parse_series()
1789 q.parse_series()
1789 q.series_dirty = 1
1790 q.series_dirty = 1
1790
1791
1791 info = q.isapplied(patch)
1792 info = q.isapplied(patch)
1792 if info:
1793 if info:
1793 q.applied[info[0]] = statusentry(info[1], name)
1794 q.applied[info[0]] = statusentry(info[1], name)
1794 q.applied_dirty = 1
1795 q.applied_dirty = 1
1795
1796
1796 util.rename(q.join(patch), absdest)
1797 util.rename(q.join(patch), absdest)
1797 r = q.qrepo()
1798 r = q.qrepo()
1798 if r:
1799 if r:
1799 wlock = r.wlock()
1800 wlock = r.wlock()
1800 if r.dirstate.state(name) == 'r':
1801 if r.dirstate.state(name) == 'r':
1801 r.undelete([name], wlock)
1802 r.undelete([name], wlock)
1802 r.copy(patch, name, wlock)
1803 r.copy(patch, name, wlock)
1803 r.remove([patch], False, wlock)
1804 r.remove([patch], False, wlock)
1804
1805
1805 q.save_dirty()
1806 q.save_dirty()
1806
1807
1807 def restore(ui, repo, rev, **opts):
1808 def restore(ui, repo, rev, **opts):
1808 """restore the queue state saved by a rev"""
1809 """restore the queue state saved by a rev"""
1809 rev = repo.lookup(rev)
1810 rev = repo.lookup(rev)
1810 q = repo.mq
1811 q = repo.mq
1811 q.restore(repo, rev, delete=opts['delete'],
1812 q.restore(repo, rev, delete=opts['delete'],
1812 qupdate=opts['update'])
1813 qupdate=opts['update'])
1813 q.save_dirty()
1814 q.save_dirty()
1814 return 0
1815 return 0
1815
1816
1816 def save(ui, repo, **opts):
1817 def save(ui, repo, **opts):
1817 """save current queue state"""
1818 """save current queue state"""
1818 q = repo.mq
1819 q = repo.mq
1819 message = commands.logmessage(opts)
1820 message = commands.logmessage(opts)
1820 ret = q.save(repo, msg=message)
1821 ret = q.save(repo, msg=message)
1821 if ret:
1822 if ret:
1822 return ret
1823 return ret
1823 q.save_dirty()
1824 q.save_dirty()
1824 if opts['copy']:
1825 if opts['copy']:
1825 path = q.path
1826 path = q.path
1826 if opts['name']:
1827 if opts['name']:
1827 newpath = os.path.join(q.basepath, opts['name'])
1828 newpath = os.path.join(q.basepath, opts['name'])
1828 if os.path.exists(newpath):
1829 if os.path.exists(newpath):
1829 if not os.path.isdir(newpath):
1830 if not os.path.isdir(newpath):
1830 raise util.Abort(_('destination %s exists and is not '
1831 raise util.Abort(_('destination %s exists and is not '
1831 'a directory') % newpath)
1832 'a directory') % newpath)
1832 if not opts['force']:
1833 if not opts['force']:
1833 raise util.Abort(_('destination %s exists, '
1834 raise util.Abort(_('destination %s exists, '
1834 'use -f to force') % newpath)
1835 'use -f to force') % newpath)
1835 else:
1836 else:
1836 newpath = savename(path)
1837 newpath = savename(path)
1837 ui.warn("copy %s to %s\n" % (path, newpath))
1838 ui.warn("copy %s to %s\n" % (path, newpath))
1838 util.copyfiles(path, newpath)
1839 util.copyfiles(path, newpath)
1839 if opts['empty']:
1840 if opts['empty']:
1840 try:
1841 try:
1841 os.unlink(q.join(q.status_path))
1842 os.unlink(q.join(q.status_path))
1842 except:
1843 except:
1843 pass
1844 pass
1844 return 0
1845 return 0
1845
1846
1846 def strip(ui, repo, rev, **opts):
1847 def strip(ui, repo, rev, **opts):
1847 """strip a revision and all later revs on the same branch"""
1848 """strip a revision and all later revs on the same branch"""
1848 rev = repo.lookup(rev)
1849 rev = repo.lookup(rev)
1849 backup = 'all'
1850 backup = 'all'
1850 if opts['backup']:
1851 if opts['backup']:
1851 backup = 'strip'
1852 backup = 'strip'
1852 elif opts['nobackup']:
1853 elif opts['nobackup']:
1853 backup = 'none'
1854 backup = 'none'
1854 update = repo.dirstate.parents()[0] != revlog.nullid
1855 update = repo.dirstate.parents()[0] != revlog.nullid
1855 repo.mq.strip(repo, rev, backup=backup, update=update)
1856 repo.mq.strip(repo, rev, backup=backup, update=update)
1856 return 0
1857 return 0
1857
1858
1858 def select(ui, repo, *args, **opts):
1859 def select(ui, repo, *args, **opts):
1859 '''set or print guarded patches to push
1860 '''set or print guarded patches to push
1860
1861
1861 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
1862 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
1863 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,
1864 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.
1865 For example:
1866 For example:
1866
1867
1867 qguard foo.patch -stable (negative guard)
1868 qguard foo.patch -stable (negative guard)
1868 qguard bar.patch +stable (positive guard)
1869 qguard bar.patch +stable (positive guard)
1869 qselect stable
1870 qselect stable
1870
1871
1871 This activates the "stable" guard. mq will skip foo.patch (because
1872 This activates the "stable" guard. mq will skip foo.patch (because
1872 it has a negative match) but push bar.patch (because it
1873 it has a negative match) but push bar.patch (because it
1873 has a positive match).
1874 has a positive match).
1874
1875
1875 With no arguments, prints the currently active guards.
1876 With no arguments, prints the currently active guards.
1876 With one argument, sets the active guard.
1877 With one argument, sets the active guard.
1877
1878
1878 Use -n/--none to deactivate guards (no other arguments needed).
1879 Use -n/--none to deactivate guards (no other arguments needed).
1879 When no guards are active, patches with positive guards are skipped
1880 When no guards are active, patches with positive guards are skipped
1880 and patches with negative guards are pushed.
1881 and patches with negative guards are pushed.
1881
1882
1882 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
1883 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
1884 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
1885 back to the current patch afterwards, but skip guarded patches.
1886 back to the current patch afterwards, but skip guarded patches.
1886
1887
1887 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
1888 other arguments needed). Use -v for more information.'''
1889 other arguments needed). Use -v for more information.'''
1889
1890
1890 q = repo.mq
1891 q = repo.mq
1891 guards = q.active()
1892 guards = q.active()
1892 if args or opts['none']:
1893 if args or opts['none']:
1893 old_unapplied = q.unapplied(repo)
1894 old_unapplied = q.unapplied(repo)
1894 old_guarded = [i for i in xrange(len(q.applied)) if
1895 old_guarded = [i for i in xrange(len(q.applied)) if
1895 not q.pushable(i)[0]]
1896 not q.pushable(i)[0]]
1896 q.set_active(args)
1897 q.set_active(args)
1897 q.save_dirty()
1898 q.save_dirty()
1898 if not args:
1899 if not args:
1899 ui.status(_('guards deactivated\n'))
1900 ui.status(_('guards deactivated\n'))
1900 if not opts['pop'] and not opts['reapply']:
1901 if not opts['pop'] and not opts['reapply']:
1901 unapplied = q.unapplied(repo)
1902 unapplied = q.unapplied(repo)
1902 guarded = [i for i in xrange(len(q.applied))
1903 guarded = [i for i in xrange(len(q.applied))
1903 if not q.pushable(i)[0]]
1904 if not q.pushable(i)[0]]
1904 if len(unapplied) != len(old_unapplied):
1905 if len(unapplied) != len(old_unapplied):
1905 ui.status(_('number of unguarded, unapplied patches has '
1906 ui.status(_('number of unguarded, unapplied patches has '
1906 'changed from %d to %d\n') %
1907 'changed from %d to %d\n') %
1907 (len(old_unapplied), len(unapplied)))
1908 (len(old_unapplied), len(unapplied)))
1908 if len(guarded) != len(old_guarded):
1909 if len(guarded) != len(old_guarded):
1909 ui.status(_('number of guarded, applied patches has changed '
1910 ui.status(_('number of guarded, applied patches has changed '
1910 'from %d to %d\n') %
1911 'from %d to %d\n') %
1911 (len(old_guarded), len(guarded)))
1912 (len(old_guarded), len(guarded)))
1912 elif opts['series']:
1913 elif opts['series']:
1913 guards = {}
1914 guards = {}
1914 noguards = 0
1915 noguards = 0
1915 for gs in q.series_guards:
1916 for gs in q.series_guards:
1916 if not gs:
1917 if not gs:
1917 noguards += 1
1918 noguards += 1
1918 for g in gs:
1919 for g in gs:
1919 guards.setdefault(g, 0)
1920 guards.setdefault(g, 0)
1920 guards[g] += 1
1921 guards[g] += 1
1921 if ui.verbose:
1922 if ui.verbose:
1922 guards['NONE'] = noguards
1923 guards['NONE'] = noguards
1923 guards = guards.items()
1924 guards = guards.items()
1924 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:]))
1925 if guards:
1926 if guards:
1926 ui.note(_('guards in series file:\n'))
1927 ui.note(_('guards in series file:\n'))
1927 for guard, count in guards:
1928 for guard, count in guards:
1928 ui.note('%2d ' % count)
1929 ui.note('%2d ' % count)
1929 ui.write(guard, '\n')
1930 ui.write(guard, '\n')
1930 else:
1931 else:
1931 ui.note(_('no guards in series file\n'))
1932 ui.note(_('no guards in series file\n'))
1932 else:
1933 else:
1933 if guards:
1934 if guards:
1934 ui.note(_('active guards:\n'))
1935 ui.note(_('active guards:\n'))
1935 for g in guards:
1936 for g in guards:
1936 ui.write(g, '\n')
1937 ui.write(g, '\n')
1937 else:
1938 else:
1938 ui.write(_('no active guards\n'))
1939 ui.write(_('no active guards\n'))
1939 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
1940 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
1940 popped = False
1941 popped = False
1941 if opts['pop'] or opts['reapply']:
1942 if opts['pop'] or opts['reapply']:
1942 for i in xrange(len(q.applied)):
1943 for i in xrange(len(q.applied)):
1943 pushable, reason = q.pushable(i)
1944 pushable, reason = q.pushable(i)
1944 if not pushable:
1945 if not pushable:
1945 ui.status(_('popping guarded patches\n'))
1946 ui.status(_('popping guarded patches\n'))
1946 popped = True
1947 popped = True
1947 if i == 0:
1948 if i == 0:
1948 q.pop(repo, all=True)
1949 q.pop(repo, all=True)
1949 else:
1950 else:
1950 q.pop(repo, i-1)
1951 q.pop(repo, i-1)
1951 break
1952 break
1952 if popped:
1953 if popped:
1953 try:
1954 try:
1954 if reapply:
1955 if reapply:
1955 ui.status(_('reapplying unguarded patches\n'))
1956 ui.status(_('reapplying unguarded patches\n'))
1956 q.push(repo, reapply)
1957 q.push(repo, reapply)
1957 finally:
1958 finally:
1958 q.save_dirty()
1959 q.save_dirty()
1959
1960
1960 def reposetup(ui, repo):
1961 def reposetup(ui, repo):
1961 class mqrepo(repo.__class__):
1962 class mqrepo(repo.__class__):
1962 def abort_if_wdir_patched(self, errmsg, force=False):
1963 def abort_if_wdir_patched(self, errmsg, force=False):
1963 if self.mq.applied and not force:
1964 if self.mq.applied and not force:
1964 parent = revlog.hex(self.dirstate.parents()[0])
1965 parent = revlog.hex(self.dirstate.parents()[0])
1965 if parent in [s.rev for s in self.mq.applied]:
1966 if parent in [s.rev for s in self.mq.applied]:
1966 raise util.Abort(errmsg)
1967 raise util.Abort(errmsg)
1967
1968
1968 def commit(self, *args, **opts):
1969 def commit(self, *args, **opts):
1969 if len(args) >= 6:
1970 if len(args) >= 6:
1970 force = args[5]
1971 force = args[5]
1971 else:
1972 else:
1972 force = opts.get('force')
1973 force = opts.get('force')
1973 self.abort_if_wdir_patched(
1974 self.abort_if_wdir_patched(
1974 _('cannot commit over an applied mq patch'),
1975 _('cannot commit over an applied mq patch'),
1975 force)
1976 force)
1976
1977
1977 return super(mqrepo, self).commit(*args, **opts)
1978 return super(mqrepo, self).commit(*args, **opts)
1978
1979
1979 def push(self, remote, force=False, revs=None):
1980 def push(self, remote, force=False, revs=None):
1980 if self.mq.applied and not force:
1981 if self.mq.applied and not force:
1981 raise util.Abort(_('source has mq patches applied'))
1982 raise util.Abort(_('source has mq patches applied'))
1982 return super(mqrepo, self).push(remote, force, revs)
1983 return super(mqrepo, self).push(remote, force, revs)
1983
1984
1984 def tags(self):
1985 def tags(self):
1985 if self.tagscache:
1986 if self.tagscache:
1986 return self.tagscache
1987 return self.tagscache
1987
1988
1988 tagscache = super(mqrepo, self).tags()
1989 tagscache = super(mqrepo, self).tags()
1989
1990
1990 q = self.mq
1991 q = self.mq
1991 if not q.applied:
1992 if not q.applied:
1992 return tagscache
1993 return tagscache
1993
1994
1994 mqtags = [(patch.rev, patch.name) for patch in q.applied]
1995 mqtags = [(patch.rev, patch.name) for patch in q.applied]
1995 mqtags.append((mqtags[-1][0], 'qtip'))
1996 mqtags.append((mqtags[-1][0], 'qtip'))
1996 mqtags.append((mqtags[0][0], 'qbase'))
1997 mqtags.append((mqtags[0][0], 'qbase'))
1997 for patch in mqtags:
1998 for patch in mqtags:
1998 if patch[1] in tagscache:
1999 if patch[1] in tagscache:
1999 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])
2000 else:
2001 else:
2001 tagscache[patch[1]] = revlog.bin(patch[0])
2002 tagscache[patch[1]] = revlog.bin(patch[0])
2002
2003
2003 return tagscache
2004 return tagscache
2004
2005
2005 if repo.local():
2006 if repo.local():
2006 repo.__class__ = mqrepo
2007 repo.__class__ = mqrepo
2007 repo.mq = queue(ui, repo.join(""))
2008 repo.mq = queue(ui, repo.join(""))
2008
2009
2009 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2010 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2010
2011
2011 cmdtable = {
2012 cmdtable = {
2012 "qapplied": (applied, [] + seriesopts, 'hg qapplied [-s] [PATCH]'),
2013 "qapplied": (applied, [] + seriesopts, 'hg qapplied [-s] [PATCH]'),
2013 "qclone": (clone,
2014 "qclone": (clone,
2014 [('', 'pull', None, _('use pull protocol to copy metadata')),
2015 [('', 'pull', None, _('use pull protocol to copy metadata')),
2015 ('U', 'noupdate', None, _('do not update the new working directories')),
2016 ('U', 'noupdate', None, _('do not update the new working directories')),
2016 ('', 'uncompressed', None,
2017 ('', 'uncompressed', None,
2017 _('use uncompressed transfer (fast over LAN)')),
2018 _('use uncompressed transfer (fast over LAN)')),
2018 ('e', 'ssh', '', _('specify ssh command to use')),
2019 ('e', 'ssh', '', _('specify ssh command to use')),
2019 ('p', 'patches', '', _('location of source patch repo')),
2020 ('p', 'patches', '', _('location of source patch repo')),
2020 ('', 'remotecmd', '',
2021 ('', 'remotecmd', '',
2021 _('specify hg command to run on the remote side'))],
2022 _('specify hg command to run on the remote side'))],
2022 'hg qclone [OPTION]... SOURCE [DEST]'),
2023 'hg qclone [OPTION]... SOURCE [DEST]'),
2023 "qcommit|qci":
2024 "qcommit|qci":
2024 (commit,
2025 (commit,
2025 commands.table["^commit|ci"][1],
2026 commands.table["^commit|ci"][1],
2026 'hg qcommit [OPTION]... [FILE]...'),
2027 'hg qcommit [OPTION]... [FILE]...'),
2027 "^qdiff": (diff,
2028 "^qdiff": (diff,
2028 [('I', 'include', [], _('include names matching the given patterns')),
2029 [('I', 'include', [], _('include names matching the given patterns')),
2029 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2030 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2030 'hg qdiff [-I] [-X] [FILE]...'),
2031 'hg qdiff [-I] [-X] [FILE]...'),
2031 "qdelete|qremove|qrm":
2032 "qdelete|qremove|qrm":
2032 (delete,
2033 (delete,
2033 [('k', 'keep', None, _('keep patch file')),
2034 [('k', 'keep', None, _('keep patch file')),
2034 ('r', 'rev', [], _('stop managing a revision'))],
2035 ('r', 'rev', [], _('stop managing a revision'))],
2035 'hg qdelete [-k] [-r REV]... PATCH...'),
2036 'hg qdelete [-k] [-r REV]... PATCH...'),
2036 'qfold':
2037 'qfold':
2037 (fold,
2038 (fold,
2038 [('e', 'edit', None, _('edit patch header')),
2039 [('e', 'edit', None, _('edit patch header')),
2039 ('k', 'keep', None, _('keep folded patch files')),
2040 ('k', 'keep', None, _('keep folded patch files')),
2040 ('m', 'message', '', _('set patch header to <text>')),
2041 ('m', 'message', '', _('set patch header to <text>')),
2041 ('l', 'logfile', '', _('set patch header to contents of <file>'))],
2042 ('l', 'logfile', '', _('set patch header to contents of <file>'))],
2042 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
2043 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
2043 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
2044 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
2044 ('n', 'none', None, _('drop all guards'))],
2045 ('n', 'none', None, _('drop all guards'))],
2045 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
2046 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
2046 'qheader': (header, [],
2047 'qheader': (header, [],
2047 _('hg qheader [PATCH]')),
2048 _('hg qheader [PATCH]')),
2048 "^qimport":
2049 "^qimport":
2049 (qimport,
2050 (qimport,
2050 [('e', 'existing', None, 'import file in patch dir'),
2051 [('e', 'existing', None, 'import file in patch dir'),
2051 ('n', 'name', '', 'patch file name'),
2052 ('n', 'name', '', 'patch file name'),
2052 ('f', 'force', None, 'overwrite existing files'),
2053 ('f', 'force', None, 'overwrite existing files'),
2053 ('r', 'rev', [], 'place existing revisions under mq control')],
2054 ('r', 'rev', [], 'place existing revisions under mq control')],
2054 'hg qimport [-e] [-n NAME] [-f] [-r REV]... FILE...'),
2055 'hg qimport [-e] [-n NAME] [-f] [-r REV]... FILE...'),
2055 "^qinit":
2056 "^qinit":
2056 (init,
2057 (init,
2057 [('c', 'create-repo', None, 'create queue repository')],
2058 [('c', 'create-repo', None, 'create queue repository')],
2058 'hg qinit [-c]'),
2059 'hg qinit [-c]'),
2059 "qnew":
2060 "qnew":
2060 (new,
2061 (new,
2061 [('e', 'edit', None, _('edit commit message')),
2062 [('e', 'edit', None, _('edit commit message')),
2062 ('m', 'message', '', _('use <text> as commit message')),
2063 ('m', 'message', '', _('use <text> as commit message')),
2063 ('l', 'logfile', '', _('read the commit message from <file>')),
2064 ('l', 'logfile', '', _('read the commit message from <file>')),
2064 ('f', 'force', None, _('import uncommitted changes into patch'))],
2065 ('f', 'force', None, _('import uncommitted changes into patch'))],
2065 'hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH'),
2066 'hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH'),
2066 "qnext": (next, [] + seriesopts, 'hg qnext [-s]'),
2067 "qnext": (next, [] + seriesopts, 'hg qnext [-s]'),
2067 "qprev": (prev, [] + seriesopts, 'hg qprev [-s]'),
2068 "qprev": (prev, [] + seriesopts, 'hg qprev [-s]'),
2068 "^qpop":
2069 "^qpop":
2069 (pop,
2070 (pop,
2070 [('a', 'all', None, 'pop all patches'),
2071 [('a', 'all', None, 'pop all patches'),
2071 ('n', 'name', '', 'queue name to pop'),
2072 ('n', 'name', '', 'queue name to pop'),
2072 ('f', 'force', None, 'forget any local changes')],
2073 ('f', 'force', None, 'forget any local changes')],
2073 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
2074 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
2074 "^qpush":
2075 "^qpush":
2075 (push,
2076 (push,
2076 [('f', 'force', None, 'apply if the patch has rejects'),
2077 [('f', 'force', None, 'apply if the patch has rejects'),
2077 ('l', 'list', None, 'list patch name in commit text'),
2078 ('l', 'list', None, 'list patch name in commit text'),
2078 ('a', 'all', None, 'apply all patches'),
2079 ('a', 'all', None, 'apply all patches'),
2079 ('m', 'merge', None, 'merge from another queue'),
2080 ('m', 'merge', None, 'merge from another queue'),
2080 ('n', 'name', '', 'merge queue name')],
2081 ('n', 'name', '', 'merge queue name')],
2081 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
2082 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
2082 "^qrefresh":
2083 "^qrefresh":
2083 (refresh,
2084 (refresh,
2084 [('e', 'edit', None, _('edit commit message')),
2085 [('e', 'edit', None, _('edit commit message')),
2085 ('m', 'message', '', _('change commit message with <text>')),
2086 ('m', 'message', '', _('change commit message with <text>')),
2086 ('l', 'logfile', '', _('change commit message with <file> content')),
2087 ('l', 'logfile', '', _('change commit message with <file> content')),
2087 ('g', 'git', None, _('use git extended diff format')),
2088 ('g', 'git', None, _('use git extended diff format')),
2088 ('s', 'short', None, 'short refresh'),
2089 ('s', 'short', None, 'short refresh'),
2089 ('I', 'include', [], _('include names matching the given patterns')),
2090 ('I', 'include', [], _('include names matching the given patterns')),
2090 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2091 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2091 'hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] FILES...'),
2092 'hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] FILES...'),
2092 'qrename|qmv':
2093 'qrename|qmv':
2093 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
2094 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
2094 "qrestore":
2095 "qrestore":
2095 (restore,
2096 (restore,
2096 [('d', 'delete', None, 'delete save entry'),
2097 [('d', 'delete', None, 'delete save entry'),
2097 ('u', 'update', None, 'update queue working dir')],
2098 ('u', 'update', None, 'update queue working dir')],
2098 'hg qrestore [-d] [-u] REV'),
2099 'hg qrestore [-d] [-u] REV'),
2099 "qsave":
2100 "qsave":
2100 (save,
2101 (save,
2101 [('m', 'message', '', _('use <text> as commit message')),
2102 [('m', 'message', '', _('use <text> as commit message')),
2102 ('l', 'logfile', '', _('read the commit message from <file>')),
2103 ('l', 'logfile', '', _('read the commit message from <file>')),
2103 ('c', 'copy', None, 'copy patch directory'),
2104 ('c', 'copy', None, 'copy patch directory'),
2104 ('n', 'name', '', 'copy directory name'),
2105 ('n', 'name', '', 'copy directory name'),
2105 ('e', 'empty', None, 'clear queue status file'),
2106 ('e', 'empty', None, 'clear queue status file'),
2106 ('f', 'force', None, 'force copy')],
2107 ('f', 'force', None, 'force copy')],
2107 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
2108 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
2108 "qselect": (select,
2109 "qselect": (select,
2109 [('n', 'none', None, _('disable all guards')),
2110 [('n', 'none', None, _('disable all guards')),
2110 ('s', 'series', None, _('list all guards in series file')),
2111 ('s', 'series', None, _('list all guards in series file')),
2111 ('', 'pop', None,
2112 ('', 'pop', None,
2112 _('pop to before first guarded applied patch')),
2113 _('pop to before first guarded applied patch')),
2113 ('', 'reapply', None, _('pop, then reapply patches'))],
2114 ('', 'reapply', None, _('pop, then reapply patches'))],
2114 'hg qselect [OPTION...] [GUARD...]'),
2115 'hg qselect [OPTION...] [GUARD...]'),
2115 "qseries":
2116 "qseries":
2116 (series,
2117 (series,
2117 [('m', 'missing', None, 'print patches not in series')] + seriesopts,
2118 [('m', 'missing', None, 'print patches not in series')] + seriesopts,
2118 'hg qseries [-ms]'),
2119 'hg qseries [-ms]'),
2119 "^strip":
2120 "^strip":
2120 (strip,
2121 (strip,
2121 [('f', 'force', None, 'force multi-head removal'),
2122 [('f', 'force', None, 'force multi-head removal'),
2122 ('b', 'backup', None, 'bundle unrelated changesets'),
2123 ('b', 'backup', None, 'bundle unrelated changesets'),
2123 ('n', 'nobackup', None, 'no backups')],
2124 ('n', 'nobackup', None, 'no backups')],
2124 'hg strip [-f] [-b] [-n] REV'),
2125 'hg strip [-f] [-b] [-n] REV'),
2125 "qtop": (top, [] + seriesopts, 'hg qtop [-s]'),
2126 "qtop": (top, [] + seriesopts, 'hg qtop [-s]'),
2126 "qunapplied": (unapplied, [] + seriesopts, 'hg qunapplied [-s] [PATCH]'),
2127 "qunapplied": (unapplied, [] + seriesopts, 'hg qunapplied [-s] [PATCH]'),
2127 }
2128 }
@@ -1,3513 +1,3517 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 = map(int, 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 range(rev, stop-1, -1):
222 for x in range(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 def trimuser(ui, name, rev, revcache):
301 def trimuser(ui, name, rev, revcache):
302 """trim the name of the user who committed a change"""
302 """trim the name of the user who committed a change"""
303 user = revcache.get(rev)
303 user = revcache.get(rev)
304 if user is None:
304 if user is None:
305 user = revcache[rev] = ui.shortuser(name)
305 user = revcache[rev] = ui.shortuser(name)
306 return user
306 return user
307
307
308 class changeset_printer(object):
308 class changeset_printer(object):
309 '''show changeset information when templating not requested.'''
309 '''show changeset information when templating not requested.'''
310
310
311 def __init__(self, ui, repo):
311 def __init__(self, ui, repo):
312 self.ui = ui
312 self.ui = ui
313 self.repo = repo
313 self.repo = repo
314
314
315 def show(self, rev=0, changenode=None, brinfo=None, copies=None):
315 def show(self, rev=0, changenode=None, brinfo=None, copies=None):
316 '''show a single changeset or file revision'''
316 '''show a single changeset or file revision'''
317 log = self.repo.changelog
317 log = self.repo.changelog
318 if changenode is None:
318 if changenode is None:
319 changenode = log.node(rev)
319 changenode = log.node(rev)
320 elif not rev:
320 elif not rev:
321 rev = log.rev(changenode)
321 rev = log.rev(changenode)
322
322
323 if self.ui.quiet:
323 if self.ui.quiet:
324 self.ui.write("%d:%s\n" % (rev, short(changenode)))
324 self.ui.write("%d:%s\n" % (rev, short(changenode)))
325 return
325 return
326
326
327 changes = log.read(changenode)
327 changes = log.read(changenode)
328 date = util.datestr(changes[2])
328 date = util.datestr(changes[2])
329 extra = changes[5]
329 extra = changes[5]
330 branch = extra.get("branch")
330 branch = extra.get("branch")
331
331
332 hexfunc = self.ui.debugflag and hex or short
332 hexfunc = self.ui.debugflag and hex or short
333
333
334 parents = [(log.rev(p), hexfunc(p)) for p in log.parents(changenode)
334 parents = [(log.rev(p), hexfunc(p)) for p in log.parents(changenode)
335 if self.ui.debugflag or p != nullid]
335 if self.ui.debugflag or p != nullid]
336 if (not self.ui.debugflag and len(parents) == 1 and
336 if (not self.ui.debugflag and len(parents) == 1 and
337 parents[0][0] == rev-1):
337 parents[0][0] == rev-1):
338 parents = []
338 parents = []
339
339
340 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
340 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
341
341
342 if branch:
342 if branch:
343 self.ui.status(_("branch: %s\n") % branch)
343 self.ui.status(_("branch: %s\n") % branch)
344 for tag in self.repo.nodetags(changenode):
344 for tag in self.repo.nodetags(changenode):
345 self.ui.status(_("tag: %s\n") % tag)
345 self.ui.status(_("tag: %s\n") % tag)
346 for parent in parents:
346 for parent in parents:
347 self.ui.write(_("parent: %d:%s\n") % parent)
347 self.ui.write(_("parent: %d:%s\n") % parent)
348
348
349 if brinfo and changenode in brinfo:
349 if brinfo and changenode in brinfo:
350 br = brinfo[changenode]
350 br = brinfo[changenode]
351 self.ui.write(_("branch: %s\n") % " ".join(br))
351 self.ui.write(_("branch: %s\n") % " ".join(br))
352
352
353 self.ui.debug(_("manifest: %d:%s\n") %
353 self.ui.debug(_("manifest: %d:%s\n") %
354 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
354 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
355 self.ui.status(_("user: %s\n") % changes[1])
355 self.ui.status(_("user: %s\n") % changes[1])
356 self.ui.status(_("date: %s\n") % date)
356 self.ui.status(_("date: %s\n") % date)
357
357
358 if self.ui.debugflag:
358 if self.ui.debugflag:
359 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
359 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
360 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
360 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
361 files):
361 files):
362 if value:
362 if value:
363 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
363 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
364 elif changes[3]:
364 elif changes[3]:
365 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
365 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
366 if copies:
366 if copies:
367 copies = ['%s (%s)' % c for c in copies]
367 copies = ['%s (%s)' % c for c in copies]
368 self.ui.note(_("copies: %s\n") % ' '.join(copies))
368 self.ui.note(_("copies: %s\n") % ' '.join(copies))
369
369
370 if extra and self.ui.debugflag:
370 if extra and self.ui.debugflag:
371 extraitems = extra.items()
371 extraitems = extra.items()
372 extraitems.sort()
372 extraitems.sort()
373 for key, value in extraitems:
373 for key, value in extraitems:
374 self.ui.debug(_("extra: %s=%s\n")
374 self.ui.debug(_("extra: %s=%s\n")
375 % (key, value.encode('string_escape')))
375 % (key, value.encode('string_escape')))
376
376
377 description = changes[4].strip()
377 description = changes[4].strip()
378 if description:
378 if description:
379 if self.ui.verbose:
379 if self.ui.verbose:
380 self.ui.status(_("description:\n"))
380 self.ui.status(_("description:\n"))
381 self.ui.status(description)
381 self.ui.status(description)
382 self.ui.status("\n\n")
382 self.ui.status("\n\n")
383 else:
383 else:
384 self.ui.status(_("summary: %s\n") %
384 self.ui.status(_("summary: %s\n") %
385 description.splitlines()[0])
385 description.splitlines()[0])
386 self.ui.status("\n")
386 self.ui.status("\n")
387
387
388 def show_changeset(ui, repo, opts):
388 def show_changeset(ui, repo, opts):
389 """show one changeset using template or regular display.
389 """show one changeset using template or regular display.
390
390
391 Display format will be the first non-empty hit of:
391 Display format will be the first non-empty hit of:
392 1. option 'template'
392 1. option 'template'
393 2. option 'style'
393 2. option 'style'
394 3. [ui] setting 'logtemplate'
394 3. [ui] setting 'logtemplate'
395 4. [ui] setting 'style'
395 4. [ui] setting 'style'
396 If all of these values are either the unset or the empty string,
396 If all of these values are either the unset or the empty string,
397 regular display via changeset_printer() is done.
397 regular display via changeset_printer() is done.
398 """
398 """
399 # options
399 # options
400 tmpl = opts.get('template')
400 tmpl = opts.get('template')
401 mapfile = None
401 mapfile = None
402 if tmpl:
402 if tmpl:
403 tmpl = templater.parsestring(tmpl, quoted=False)
403 tmpl = templater.parsestring(tmpl, quoted=False)
404 else:
404 else:
405 mapfile = opts.get('style')
405 mapfile = opts.get('style')
406 # ui settings
406 # ui settings
407 if not mapfile:
407 if not mapfile:
408 tmpl = ui.config('ui', 'logtemplate')
408 tmpl = ui.config('ui', 'logtemplate')
409 if tmpl:
409 if tmpl:
410 tmpl = templater.parsestring(tmpl)
410 tmpl = templater.parsestring(tmpl)
411 else:
411 else:
412 mapfile = ui.config('ui', 'style')
412 mapfile = ui.config('ui', 'style')
413
413
414 if tmpl or mapfile:
414 if tmpl or mapfile:
415 if mapfile:
415 if mapfile:
416 if not os.path.split(mapfile)[0]:
416 if not os.path.split(mapfile)[0]:
417 mapname = (templater.templatepath('map-cmdline.' + mapfile)
417 mapname = (templater.templatepath('map-cmdline.' + mapfile)
418 or templater.templatepath(mapfile))
418 or templater.templatepath(mapfile))
419 if mapname: mapfile = mapname
419 if mapname: mapfile = mapname
420 try:
420 try:
421 t = templater.changeset_templater(ui, repo, mapfile)
421 t = templater.changeset_templater(ui, repo, mapfile)
422 except SyntaxError, inst:
422 except SyntaxError, inst:
423 raise util.Abort(inst.args[0])
423 raise util.Abort(inst.args[0])
424 if tmpl: t.use_template(tmpl)
424 if tmpl: t.use_template(tmpl)
425 return t
425 return t
426 return changeset_printer(ui, repo)
426 return changeset_printer(ui, repo)
427
427
428 def setremoteconfig(ui, opts):
428 def setremoteconfig(ui, opts):
429 "copy remote options to ui tree"
429 "copy remote options to ui tree"
430 if opts.get('ssh'):
430 if opts.get('ssh'):
431 ui.setconfig("ui", "ssh", opts['ssh'])
431 ui.setconfig("ui", "ssh", opts['ssh'])
432 if opts.get('remotecmd'):
432 if opts.get('remotecmd'):
433 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
433 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
434
434
435 def show_version(ui):
435 def show_version(ui):
436 """output version and copyright information"""
436 """output version and copyright information"""
437 ui.write(_("Mercurial Distributed SCM (version %s)\n")
437 ui.write(_("Mercurial Distributed SCM (version %s)\n")
438 % version.get_version())
438 % version.get_version())
439 ui.status(_(
439 ui.status(_(
440 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
440 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
441 "This is free software; see the source for copying conditions. "
441 "This is free software; see the source for copying conditions. "
442 "There is NO\nwarranty; "
442 "There is NO\nwarranty; "
443 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
443 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
444 ))
444 ))
445
445
446 def help_(ui, name=None, with_version=False):
446 def help_(ui, name=None, with_version=False):
447 """show help for a command, extension, or list of commands
447 """show help for a command, extension, or list of commands
448
448
449 With no arguments, print a list of commands and short help.
449 With no arguments, print a list of commands and short help.
450
450
451 Given a command name, print help for that command.
451 Given a command name, print help for that command.
452
452
453 Given an extension name, print help for that extension, and the
453 Given an extension name, print help for that extension, and the
454 commands it provides."""
454 commands it provides."""
455 option_lists = []
455 option_lists = []
456
456
457 def helpcmd(name):
457 def helpcmd(name):
458 if with_version:
458 if with_version:
459 show_version(ui)
459 show_version(ui)
460 ui.write('\n')
460 ui.write('\n')
461 aliases, i = findcmd(ui, name)
461 aliases, i = findcmd(ui, name)
462 # synopsis
462 # synopsis
463 ui.write("%s\n\n" % i[2])
463 ui.write("%s\n\n" % i[2])
464
464
465 # description
465 # description
466 doc = i[0].__doc__
466 doc = i[0].__doc__
467 if not doc:
467 if not doc:
468 doc = _("(No help text available)")
468 doc = _("(No help text available)")
469 if ui.quiet:
469 if ui.quiet:
470 doc = doc.splitlines(0)[0]
470 doc = doc.splitlines(0)[0]
471 ui.write("%s\n" % doc.rstrip())
471 ui.write("%s\n" % doc.rstrip())
472
472
473 if not ui.quiet:
473 if not ui.quiet:
474 # aliases
474 # aliases
475 if len(aliases) > 1:
475 if len(aliases) > 1:
476 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
476 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
477
477
478 # options
478 # options
479 if i[1]:
479 if i[1]:
480 option_lists.append(("options", i[1]))
480 option_lists.append(("options", i[1]))
481
481
482 def helplist(select=None):
482 def helplist(select=None):
483 h = {}
483 h = {}
484 cmds = {}
484 cmds = {}
485 for c, e in table.items():
485 for c, e in table.items():
486 f = c.split("|", 1)[0]
486 f = c.split("|", 1)[0]
487 if select and not select(f):
487 if select and not select(f):
488 continue
488 continue
489 if name == "shortlist" and not f.startswith("^"):
489 if name == "shortlist" and not f.startswith("^"):
490 continue
490 continue
491 f = f.lstrip("^")
491 f = f.lstrip("^")
492 if not ui.debugflag and f.startswith("debug"):
492 if not ui.debugflag and f.startswith("debug"):
493 continue
493 continue
494 doc = e[0].__doc__
494 doc = e[0].__doc__
495 if not doc:
495 if not doc:
496 doc = _("(No help text available)")
496 doc = _("(No help text available)")
497 h[f] = doc.splitlines(0)[0].rstrip()
497 h[f] = doc.splitlines(0)[0].rstrip()
498 cmds[f] = c.lstrip("^")
498 cmds[f] = c.lstrip("^")
499
499
500 fns = h.keys()
500 fns = h.keys()
501 fns.sort()
501 fns.sort()
502 m = max(map(len, fns))
502 m = max(map(len, fns))
503 for f in fns:
503 for f in fns:
504 if ui.verbose:
504 if ui.verbose:
505 commands = cmds[f].replace("|",", ")
505 commands = cmds[f].replace("|",", ")
506 ui.write(" %s:\n %s\n"%(commands, h[f]))
506 ui.write(" %s:\n %s\n"%(commands, h[f]))
507 else:
507 else:
508 ui.write(' %-*s %s\n' % (m, f, h[f]))
508 ui.write(' %-*s %s\n' % (m, f, h[f]))
509
509
510 def helpext(name):
510 def helpext(name):
511 try:
511 try:
512 mod = findext(name)
512 mod = findext(name)
513 except KeyError:
513 except KeyError:
514 raise UnknownCommand(name)
514 raise UnknownCommand(name)
515
515
516 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
516 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
517 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
517 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
518 for d in doc[1:]:
518 for d in doc[1:]:
519 ui.write(d, '\n')
519 ui.write(d, '\n')
520
520
521 ui.status('\n')
521 ui.status('\n')
522 if ui.verbose:
522 if ui.verbose:
523 ui.status(_('list of commands:\n\n'))
523 ui.status(_('list of commands:\n\n'))
524 else:
524 else:
525 ui.status(_('list of commands (use "hg help -v %s" '
525 ui.status(_('list of commands (use "hg help -v %s" '
526 'to show aliases and global options):\n\n') % name)
526 'to show aliases and global options):\n\n') % name)
527
527
528 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
528 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
529 helplist(modcmds.has_key)
529 helplist(modcmds.has_key)
530
530
531 if name and name != 'shortlist':
531 if name and name != 'shortlist':
532 try:
532 try:
533 helpcmd(name)
533 helpcmd(name)
534 except UnknownCommand:
534 except UnknownCommand:
535 helpext(name)
535 helpext(name)
536
536
537 else:
537 else:
538 # program name
538 # program name
539 if ui.verbose or with_version:
539 if ui.verbose or with_version:
540 show_version(ui)
540 show_version(ui)
541 else:
541 else:
542 ui.status(_("Mercurial Distributed SCM\n"))
542 ui.status(_("Mercurial Distributed SCM\n"))
543 ui.status('\n')
543 ui.status('\n')
544
544
545 # list of commands
545 # list of commands
546 if name == "shortlist":
546 if name == "shortlist":
547 ui.status(_('basic commands (use "hg help" '
547 ui.status(_('basic commands (use "hg help" '
548 'for the full list or option "-v" for details):\n\n'))
548 'for the full list or option "-v" for details):\n\n'))
549 elif ui.verbose:
549 elif ui.verbose:
550 ui.status(_('list of commands:\n\n'))
550 ui.status(_('list of commands:\n\n'))
551 else:
551 else:
552 ui.status(_('list of commands (use "hg help -v" '
552 ui.status(_('list of commands (use "hg help -v" '
553 'to show aliases and global options):\n\n'))
553 'to show aliases and global options):\n\n'))
554
554
555 helplist()
555 helplist()
556
556
557 # global options
557 # global options
558 if ui.verbose:
558 if ui.verbose:
559 option_lists.append(("global options", globalopts))
559 option_lists.append(("global options", globalopts))
560
560
561 # list all option lists
561 # list all option lists
562 opt_output = []
562 opt_output = []
563 for title, options in option_lists:
563 for title, options in option_lists:
564 opt_output.append(("\n%s:\n" % title, None))
564 opt_output.append(("\n%s:\n" % title, None))
565 for shortopt, longopt, default, desc in options:
565 for shortopt, longopt, default, desc in options:
566 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
566 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
567 longopt and " --%s" % longopt),
567 longopt and " --%s" % longopt),
568 "%s%s" % (desc,
568 "%s%s" % (desc,
569 default
569 default
570 and _(" (default: %s)") % default
570 and _(" (default: %s)") % default
571 or "")))
571 or "")))
572
572
573 if opt_output:
573 if opt_output:
574 opts_len = max([len(line[0]) for line in opt_output if line[1]])
574 opts_len = max([len(line[0]) for line in opt_output if line[1]])
575 for first, second in opt_output:
575 for first, second in opt_output:
576 if second:
576 if second:
577 ui.write(" %-*s %s\n" % (opts_len, first, second))
577 ui.write(" %-*s %s\n" % (opts_len, first, second))
578 else:
578 else:
579 ui.write("%s\n" % first)
579 ui.write("%s\n" % first)
580
580
581 # Commands start here, listed alphabetically
581 # Commands start here, listed alphabetically
582
582
583 def add(ui, repo, *pats, **opts):
583 def add(ui, repo, *pats, **opts):
584 """add the specified files on the next commit
584 """add the specified files on the next commit
585
585
586 Schedule files to be version controlled and added to the repository.
586 Schedule files to be version controlled and added to the repository.
587
587
588 The files will be added to the repository at the next commit.
588 The files will be added to the repository at the next commit.
589
589
590 If no names are given, add all files in the repository.
590 If no names are given, add all files in the repository.
591 """
591 """
592
592
593 names = []
593 names = []
594 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
594 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
595 if exact:
595 if exact:
596 if ui.verbose:
596 if ui.verbose:
597 ui.status(_('adding %s\n') % rel)
597 ui.status(_('adding %s\n') % rel)
598 names.append(abs)
598 names.append(abs)
599 elif repo.dirstate.state(abs) == '?':
599 elif repo.dirstate.state(abs) == '?':
600 ui.status(_('adding %s\n') % rel)
600 ui.status(_('adding %s\n') % rel)
601 names.append(abs)
601 names.append(abs)
602 if not opts.get('dry_run'):
602 if not opts.get('dry_run'):
603 repo.add(names)
603 repo.add(names)
604
604
605 def addremove(ui, repo, *pats, **opts):
605 def addremove(ui, repo, *pats, **opts):
606 """add all new files, delete all missing files
606 """add all new files, delete all missing files
607
607
608 Add all new files and remove all missing files from the repository.
608 Add all new files and remove all missing files from the repository.
609
609
610 New files are ignored if they match any of the patterns in .hgignore. As
610 New files are ignored if they match any of the patterns in .hgignore. As
611 with add, these changes take effect at the next commit.
611 with add, these changes take effect at the next commit.
612
612
613 Use the -s option to detect renamed files. With a parameter > 0,
613 Use the -s option to detect renamed files. With a parameter > 0,
614 this compares every removed file with every added file and records
614 this compares every removed file with every added file and records
615 those similar enough as renames. This option takes a percentage
615 those similar enough as renames. This option takes a percentage
616 between 0 (disabled) and 100 (files must be identical) as its
616 between 0 (disabled) and 100 (files must be identical) as its
617 parameter. Detecting renamed files this way can be expensive.
617 parameter. Detecting renamed files this way can be expensive.
618 """
618 """
619 sim = float(opts.get('similarity') or 0)
619 sim = float(opts.get('similarity') or 0)
620 if sim < 0 or sim > 100:
620 if sim < 0 or sim > 100:
621 raise util.Abort(_('similarity must be between 0 and 100'))
621 raise util.Abort(_('similarity must be between 0 and 100'))
622 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
622 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
623
623
624 def annotate(ui, repo, *pats, **opts):
624 def annotate(ui, repo, *pats, **opts):
625 """show changeset information per file line
625 """show changeset information per file line
626
626
627 List changes in files, showing the revision id responsible for each line
627 List changes in files, showing the revision id responsible for each line
628
628
629 This command is useful to discover who did a change or when a change took
629 This command is useful to discover who did a change or when a change took
630 place.
630 place.
631
631
632 Without the -a option, annotate will avoid processing files it
632 Without the -a option, annotate will avoid processing files it
633 detects as binary. With -a, annotate will generate an annotation
633 detects as binary. With -a, annotate will generate an annotation
634 anyway, probably with undesirable results.
634 anyway, probably with undesirable results.
635 """
635 """
636 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
636 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
637
637
638 if not pats:
638 if not pats:
639 raise util.Abort(_('at least one file name or pattern required'))
639 raise util.Abort(_('at least one file name or pattern required'))
640
640
641 opmap = [['user', lambda x: ui.shortuser(x.user())],
641 opmap = [['user', lambda x: ui.shortuser(x.user())],
642 ['number', lambda x: str(x.rev())],
642 ['number', lambda x: str(x.rev())],
643 ['changeset', lambda x: short(x.node())],
643 ['changeset', lambda x: short(x.node())],
644 ['date', getdate], ['follow', lambda x: x.path()]]
644 ['date', getdate], ['follow', lambda x: x.path()]]
645 if (not opts['user'] and not opts['changeset'] and not opts['date']
645 if (not opts['user'] and not opts['changeset'] and not opts['date']
646 and not opts['follow']):
646 and not opts['follow']):
647 opts['number'] = 1
647 opts['number'] = 1
648
648
649 ctx = repo.changectx(opts['rev'])
649 ctx = repo.changectx(opts['rev'])
650
650
651 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
651 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
652 node=ctx.node()):
652 node=ctx.node()):
653 fctx = ctx.filectx(abs)
653 fctx = ctx.filectx(abs)
654 if not opts['text'] and util.binary(fctx.data()):
654 if not opts['text'] and util.binary(fctx.data()):
655 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
655 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
656 continue
656 continue
657
657
658 lines = fctx.annotate(follow=opts.get('follow'))
658 lines = fctx.annotate(follow=opts.get('follow'))
659 pieces = []
659 pieces = []
660
660
661 for o, f in opmap:
661 for o, f in opmap:
662 if opts[o]:
662 if opts[o]:
663 l = [f(n) for n, dummy in lines]
663 l = [f(n) for n, dummy in lines]
664 if l:
664 if l:
665 m = max(map(len, l))
665 m = max(map(len, l))
666 pieces.append(["%*s" % (m, x) for x in l])
666 pieces.append(["%*s" % (m, x) for x in l])
667
667
668 if pieces:
668 if pieces:
669 for p, l in zip(zip(*pieces), lines):
669 for p, l in zip(zip(*pieces), lines):
670 ui.write("%s: %s" % (" ".join(p), l[1]))
670 ui.write("%s: %s" % (" ".join(p), l[1]))
671
671
672 def archive(ui, repo, dest, **opts):
672 def archive(ui, repo, dest, **opts):
673 '''create unversioned archive of a repository revision
673 '''create unversioned archive of a repository revision
674
674
675 By default, the revision used is the parent of the working
675 By default, the revision used is the parent of the working
676 directory; use "-r" to specify a different revision.
676 directory; use "-r" to specify a different revision.
677
677
678 To specify the type of archive to create, use "-t". Valid
678 To specify the type of archive to create, use "-t". Valid
679 types are:
679 types are:
680
680
681 "files" (default): a directory full of files
681 "files" (default): a directory full of files
682 "tar": tar archive, uncompressed
682 "tar": tar archive, uncompressed
683 "tbz2": tar archive, compressed using bzip2
683 "tbz2": tar archive, compressed using bzip2
684 "tgz": tar archive, compressed using gzip
684 "tgz": tar archive, compressed using gzip
685 "uzip": zip archive, uncompressed
685 "uzip": zip archive, uncompressed
686 "zip": zip archive, compressed using deflate
686 "zip": zip archive, compressed using deflate
687
687
688 The exact name of the destination archive or directory is given
688 The exact name of the destination archive or directory is given
689 using a format string; see "hg help export" for details.
689 using a format string; see "hg help export" for details.
690
690
691 Each member added to an archive file has a directory prefix
691 Each member added to an archive file has a directory prefix
692 prepended. Use "-p" to specify a format string for the prefix.
692 prepended. Use "-p" to specify a format string for the prefix.
693 The default is the basename of the archive, with suffixes removed.
693 The default is the basename of the archive, with suffixes removed.
694 '''
694 '''
695
695
696 node = repo.changectx(opts['rev']).node()
696 node = repo.changectx(opts['rev']).node()
697 dest = cmdutil.make_filename(repo, dest, node)
697 dest = cmdutil.make_filename(repo, dest, node)
698 if os.path.realpath(dest) == repo.root:
698 if os.path.realpath(dest) == repo.root:
699 raise util.Abort(_('repository root cannot be destination'))
699 raise util.Abort(_('repository root cannot be destination'))
700 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
700 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
701 kind = opts.get('type') or 'files'
701 kind = opts.get('type') or 'files'
702 prefix = opts['prefix']
702 prefix = opts['prefix']
703 if dest == '-':
703 if dest == '-':
704 if kind == 'files':
704 if kind == 'files':
705 raise util.Abort(_('cannot archive plain files to stdout'))
705 raise util.Abort(_('cannot archive plain files to stdout'))
706 dest = sys.stdout
706 dest = sys.stdout
707 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
707 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
708 prefix = cmdutil.make_filename(repo, prefix, node)
708 prefix = cmdutil.make_filename(repo, prefix, node)
709 archival.archive(repo, dest, node, kind, not opts['no_decode'],
709 archival.archive(repo, dest, node, kind, not opts['no_decode'],
710 matchfn, prefix)
710 matchfn, prefix)
711
711
712 def backout(ui, repo, rev, **opts):
712 def backout(ui, repo, rev, **opts):
713 '''reverse effect of earlier changeset
713 '''reverse effect of earlier changeset
714
714
715 Commit the backed out changes as a new changeset. The new
715 Commit the backed out changes as a new changeset. The new
716 changeset is a child of the backed out changeset.
716 changeset is a child of the backed out changeset.
717
717
718 If you back out a changeset other than the tip, a new head is
718 If you back out a changeset other than the tip, a new head is
719 created. This head is the parent of the working directory. If
719 created. This head is the parent of the working directory. If
720 you back out an old changeset, your working directory will appear
720 you back out an old changeset, your working directory will appear
721 old after the backout. You should merge the backout changeset
721 old after the backout. You should merge the backout changeset
722 with another head.
722 with another head.
723
723
724 The --merge option remembers the parent of the working directory
724 The --merge option remembers the parent of the working directory
725 before starting the backout, then merges the new head with that
725 before starting the backout, then merges the new head with that
726 changeset afterwards. This saves you from doing the merge by
726 changeset afterwards. This saves you from doing the merge by
727 hand. The result of this merge is not committed, as for a normal
727 hand. The result of this merge is not committed, as for a normal
728 merge.'''
728 merge.'''
729
729
730 bail_if_changed(repo)
730 bail_if_changed(repo)
731 op1, op2 = repo.dirstate.parents()
731 op1, op2 = repo.dirstate.parents()
732 if op2 != nullid:
732 if op2 != nullid:
733 raise util.Abort(_('outstanding uncommitted merge'))
733 raise util.Abort(_('outstanding uncommitted merge'))
734 node = repo.lookup(rev)
734 node = repo.lookup(rev)
735 p1, p2 = repo.changelog.parents(node)
735 p1, p2 = repo.changelog.parents(node)
736 if p1 == nullid:
736 if p1 == nullid:
737 raise util.Abort(_('cannot back out a change with no parents'))
737 raise util.Abort(_('cannot back out a change with no parents'))
738 if p2 != nullid:
738 if p2 != nullid:
739 if not opts['parent']:
739 if not opts['parent']:
740 raise util.Abort(_('cannot back out a merge changeset without '
740 raise util.Abort(_('cannot back out a merge changeset without '
741 '--parent'))
741 '--parent'))
742 p = repo.lookup(opts['parent'])
742 p = repo.lookup(opts['parent'])
743 if p not in (p1, p2):
743 if p not in (p1, p2):
744 raise util.Abort(_('%s is not a parent of %s' %
744 raise util.Abort(_('%s is not a parent of %s' %
745 (short(p), short(node))))
745 (short(p), short(node))))
746 parent = p
746 parent = p
747 else:
747 else:
748 if opts['parent']:
748 if opts['parent']:
749 raise util.Abort(_('cannot use --parent on non-merge changeset'))
749 raise util.Abort(_('cannot use --parent on non-merge changeset'))
750 parent = p1
750 parent = p1
751 hg.clean(repo, node, show_stats=False)
751 hg.clean(repo, node, show_stats=False)
752 revert_opts = opts.copy()
752 revert_opts = opts.copy()
753 revert_opts['all'] = True
753 revert_opts['all'] = True
754 revert_opts['rev'] = hex(parent)
754 revert_opts['rev'] = hex(parent)
755 revert(ui, repo, **revert_opts)
755 revert(ui, repo, **revert_opts)
756 commit_opts = opts.copy()
756 commit_opts = opts.copy()
757 commit_opts['addremove'] = False
757 commit_opts['addremove'] = False
758 if not commit_opts['message'] and not commit_opts['logfile']:
758 if not commit_opts['message'] and not commit_opts['logfile']:
759 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
759 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
760 commit_opts['force_editor'] = True
760 commit_opts['force_editor'] = True
761 commit(ui, repo, **commit_opts)
761 commit(ui, repo, **commit_opts)
762 def nice(node):
762 def nice(node):
763 return '%d:%s' % (repo.changelog.rev(node), short(node))
763 return '%d:%s' % (repo.changelog.rev(node), short(node))
764 ui.status(_('changeset %s backs out changeset %s\n') %
764 ui.status(_('changeset %s backs out changeset %s\n') %
765 (nice(repo.changelog.tip()), nice(node)))
765 (nice(repo.changelog.tip()), nice(node)))
766 if op1 != node:
766 if op1 != node:
767 if opts['merge']:
767 if opts['merge']:
768 ui.status(_('merging with changeset %s\n') % nice(op1))
768 ui.status(_('merging with changeset %s\n') % nice(op1))
769 n = _lookup(repo, hex(op1))
769 n = _lookup(repo, hex(op1))
770 hg.merge(repo, n)
770 hg.merge(repo, n)
771 else:
771 else:
772 ui.status(_('the backout changeset is a new head - '
772 ui.status(_('the backout changeset is a new head - '
773 'do not forget to merge\n'))
773 'do not forget to merge\n'))
774 ui.status(_('(use "backout --merge" '
774 ui.status(_('(use "backout --merge" '
775 'if you want to auto-merge)\n'))
775 'if you want to auto-merge)\n'))
776
776
777 def bundle(ui, repo, fname, dest=None, **opts):
777 def bundle(ui, repo, fname, dest=None, **opts):
778 """create a changegroup file
778 """create a changegroup file
779
779
780 Generate a compressed changegroup file collecting changesets.
780 Generate a compressed changegroup file collecting changesets.
781 not found in the other repository.
781 not found in the other repository.
782
782
783 If no destination repository is specified the destination is
783 If no destination repository is specified the destination is
784 assumed to have all the node specified by --base.
784 assumed to have all the node specified by --base.
785
785
786 This file can then be transferred using conventional means and
786 This file can then be transferred using conventional means and
787 applied to another repository with the unbundle command. This is
787 applied to another repository with the unbundle command. This is
788 useful when native push and pull are not available or when
788 useful when native push and pull are not available or when
789 exporting an entire repository is undesirable. The standard file
789 exporting an entire repository is undesirable. The standard file
790 extension is ".hg".
790 extension is ".hg".
791
791
792 Unlike import/export, this exactly preserves all changeset
792 Unlike import/export, this exactly preserves all changeset
793 contents including permissions, rename data, and revision history.
793 contents including permissions, rename data, and revision history.
794 """
794 """
795 revs = opts.get('rev') or None
795 revs = opts.get('rev') or None
796 if revs:
796 if revs:
797 revs = [repo.lookup(rev) for rev in revs]
797 revs = [repo.lookup(rev) for rev in revs]
798 base = opts.get('base')
798 base = opts.get('base')
799 if base:
799 if base:
800 if dest:
800 if dest:
801 raise util.Abort(_("--base is incompatible with specifiying "
801 raise util.Abort(_("--base is incompatible with specifiying "
802 "a destination"))
802 "a destination"))
803 base = [repo.lookup(rev) for rev in base]
803 base = [repo.lookup(rev) for rev in base]
804 # create the right base
804 # create the right base
805 # XXX: nodesbetween / changegroup* should be "fixed" instead
805 # XXX: nodesbetween / changegroup* should be "fixed" instead
806 o = []
806 o = []
807 has_set = sets.Set(base)
807 has_set = sets.Set(base)
808 for n in base:
808 for n in base:
809 has_set.update(repo.changelog.reachable(n))
809 has_set.update(repo.changelog.reachable(n))
810 if revs:
810 if revs:
811 visit = list(revs)
811 visit = list(revs)
812 else:
812 else:
813 visit = repo.changelog.heads()
813 visit = repo.changelog.heads()
814 while visit:
814 while visit:
815 n = visit.pop(0)
815 n = visit.pop(0)
816 parents = [p for p in repo.changelog.parents(n)
816 parents = [p for p in repo.changelog.parents(n)
817 if p != nullid and p not in has_set]
817 if p != nullid and p not in has_set]
818 if len(parents) == 0:
818 if len(parents) == 0:
819 o.insert(0, n)
819 o.insert(0, n)
820 else:
820 else:
821 visit.extend(parents)
821 visit.extend(parents)
822 else:
822 else:
823 setremoteconfig(ui, opts)
823 setremoteconfig(ui, opts)
824 dest = ui.expandpath(dest or 'default-push', dest or 'default')
824 dest = ui.expandpath(dest or 'default-push', dest or 'default')
825 other = hg.repository(ui, dest)
825 other = hg.repository(ui, dest)
826 o = repo.findoutgoing(other, force=opts['force'])
826 o = repo.findoutgoing(other, force=opts['force'])
827
827
828 if revs:
828 if revs:
829 cg = repo.changegroupsubset(o, revs, 'bundle')
829 cg = repo.changegroupsubset(o, revs, 'bundle')
830 else:
830 else:
831 cg = repo.changegroup(o, 'bundle')
831 cg = repo.changegroup(o, 'bundle')
832 write_bundle(cg, fname)
832 write_bundle(cg, fname)
833
833
834 def cat(ui, repo, file1, *pats, **opts):
834 def cat(ui, repo, file1, *pats, **opts):
835 """output the latest or given revisions of files
835 """output the latest or given revisions of files
836
836
837 Print the specified files as they were at the given revision.
837 Print the specified files as they were at the given revision.
838 If no revision is given then working dir parent is used, or tip
838 If no revision is given then working dir parent is used, or tip
839 if no revision is checked out.
839 if no revision is checked out.
840
840
841 Output may be to a file, in which case the name of the file is
841 Output may be to a file, in which case the name of the file is
842 given using a format string. The formatting rules are the same as
842 given using a format string. The formatting rules are the same as
843 for the export command, with the following additions:
843 for the export command, with the following additions:
844
844
845 %s basename of file being printed
845 %s basename of file being printed
846 %d dirname of file being printed, or '.' if in repo root
846 %d dirname of file being printed, or '.' if in repo root
847 %p root-relative path name of file being printed
847 %p root-relative path name of file being printed
848 """
848 """
849 ctx = repo.changectx(opts['rev'])
849 ctx = repo.changectx(opts['rev'])
850 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
850 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
851 ctx.node()):
851 ctx.node()):
852 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
852 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
853 fp.write(ctx.filectx(abs).data())
853 fp.write(ctx.filectx(abs).data())
854
854
855 def clone(ui, source, dest=None, **opts):
855 def clone(ui, source, dest=None, **opts):
856 """make a copy of an existing repository
856 """make a copy of an existing repository
857
857
858 Create a copy of an existing repository in a new directory.
858 Create a copy of an existing repository in a new directory.
859
859
860 If no destination directory name is specified, it defaults to the
860 If no destination directory name is specified, it defaults to the
861 basename of the source.
861 basename of the source.
862
862
863 The location of the source is added to the new repository's
863 The location of the source is added to the new repository's
864 .hg/hgrc file, as the default to be used for future pulls.
864 .hg/hgrc file, as the default to be used for future pulls.
865
865
866 For efficiency, hardlinks are used for cloning whenever the source
866 For efficiency, hardlinks are used for cloning whenever the source
867 and destination are on the same filesystem (note this applies only
867 and destination are on the same filesystem (note this applies only
868 to the repository data, not to the checked out files). Some
868 to the repository data, not to the checked out files). Some
869 filesystems, such as AFS, implement hardlinking incorrectly, but
869 filesystems, such as AFS, implement hardlinking incorrectly, but
870 do not report errors. In these cases, use the --pull option to
870 do not report errors. In these cases, use the --pull option to
871 avoid hardlinking.
871 avoid hardlinking.
872
872
873 You can safely clone repositories and checked out files using full
873 You can safely clone repositories and checked out files using full
874 hardlinks with
874 hardlinks with
875
875
876 $ cp -al REPO REPOCLONE
876 $ cp -al REPO REPOCLONE
877
877
878 which is the fastest way to clone. However, the operation is not
878 which is the fastest way to clone. However, the operation is not
879 atomic (making sure REPO is not modified during the operation is
879 atomic (making sure REPO is not modified during the operation is
880 up to you) and you have to make sure your editor breaks hardlinks
880 up to you) and you have to make sure your editor breaks hardlinks
881 (Emacs and most Linux Kernel tools do so).
881 (Emacs and most Linux Kernel tools do so).
882
882
883 If you use the -r option to clone up to a specific revision, no
883 If you use the -r option to clone up to a specific revision, no
884 subsequent revisions will be present in the cloned repository.
884 subsequent revisions will be present in the cloned repository.
885 This option implies --pull, even on local repositories.
885 This option implies --pull, even on local repositories.
886
886
887 See pull for valid source format details.
887 See pull for valid source format details.
888
888
889 It is possible to specify an ssh:// URL as the destination, but no
889 It is possible to specify an ssh:// URL as the destination, but no
890 .hg/hgrc will be created on the remote side. Look at the help text
890 .hg/hgrc will be created on the remote side. Look at the help text
891 for the pull command for important details about ssh:// URLs.
891 for the pull command for important details about ssh:// URLs.
892 """
892 """
893 setremoteconfig(ui, opts)
893 setremoteconfig(ui, opts)
894 hg.clone(ui, ui.expandpath(source), dest,
894 hg.clone(ui, ui.expandpath(source), dest,
895 pull=opts['pull'],
895 pull=opts['pull'],
896 stream=opts['uncompressed'],
896 stream=opts['uncompressed'],
897 rev=opts['rev'],
897 rev=opts['rev'],
898 update=not opts['noupdate'])
898 update=not opts['noupdate'])
899
899
900 def commit(ui, repo, *pats, **opts):
900 def commit(ui, repo, *pats, **opts):
901 """commit the specified files or all outstanding changes
901 """commit the specified files or all outstanding changes
902
902
903 Commit changes to the given files into the repository.
903 Commit changes to the given files into the repository.
904
904
905 If a list of files is omitted, all changes reported by "hg status"
905 If a list of files is omitted, all changes reported by "hg status"
906 will be committed.
906 will be committed.
907
907
908 If no commit message is specified, the editor configured in your hgrc
908 If no commit message is specified, the editor configured in your hgrc
909 or in the EDITOR environment variable is started to enter a message.
909 or in the EDITOR environment variable is started to enter a message.
910 """
910 """
911 message = logmessage(opts)
911 message = logmessage(opts)
912
912
913 if opts['addremove']:
913 if opts['addremove']:
914 cmdutil.addremove(repo, pats, opts)
914 cmdutil.addremove(repo, pats, opts)
915 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
915 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
916 if pats:
916 if pats:
917 modified, added, removed = repo.status(files=fns, match=match)[:3]
917 modified, added, removed = repo.status(files=fns, match=match)[:3]
918 files = modified + added + removed
918 files = modified + added + removed
919 else:
919 else:
920 files = []
920 files = []
921 try:
921 try:
922 repo.commit(files, message, opts['user'], opts['date'], match,
922 repo.commit(files, message, opts['user'], opts['date'], match,
923 force_editor=opts.get('force_editor'))
923 force_editor=opts.get('force_editor'))
924 except ValueError, inst:
924 except ValueError, inst:
925 raise util.Abort(str(inst))
925 raise util.Abort(str(inst))
926
926
927 def docopy(ui, repo, pats, opts, wlock):
927 def docopy(ui, repo, pats, opts, wlock):
928 # called with the repo lock held
928 # called with the repo lock held
929 cwd = repo.getcwd()
929 cwd = repo.getcwd()
930 errors = 0
930 errors = 0
931 copied = []
931 copied = []
932 targets = {}
932 targets = {}
933
933
934 def okaytocopy(abs, rel, exact):
934 def okaytocopy(abs, rel, exact):
935 reasons = {'?': _('is not managed'),
935 reasons = {'?': _('is not managed'),
936 'a': _('has been marked for add'),
936 'a': _('has been marked for add'),
937 'r': _('has been marked for remove')}
937 'r': _('has been marked for remove')}
938 state = repo.dirstate.state(abs)
938 state = repo.dirstate.state(abs)
939 reason = reasons.get(state)
939 reason = reasons.get(state)
940 if reason:
940 if reason:
941 if state == 'a':
941 if state == 'a':
942 origsrc = repo.dirstate.copied(abs)
942 origsrc = repo.dirstate.copied(abs)
943 if origsrc is not None:
943 if origsrc is not None:
944 return origsrc
944 return origsrc
945 if exact:
945 if exact:
946 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
946 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
947 else:
947 else:
948 return abs
948 return abs
949
949
950 def copy(origsrc, abssrc, relsrc, target, exact):
950 def copy(origsrc, abssrc, relsrc, target, exact):
951 abstarget = util.canonpath(repo.root, cwd, target)
951 abstarget = util.canonpath(repo.root, cwd, target)
952 reltarget = util.pathto(cwd, abstarget)
952 reltarget = util.pathto(cwd, abstarget)
953 prevsrc = targets.get(abstarget)
953 prevsrc = targets.get(abstarget)
954 if prevsrc is not None:
954 if prevsrc is not None:
955 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
955 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
956 (reltarget, abssrc, prevsrc))
956 (reltarget, abssrc, prevsrc))
957 return
957 return
958 if (not opts['after'] and os.path.exists(reltarget) or
958 if (not opts['after'] and os.path.exists(reltarget) or
959 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
959 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
960 if not opts['force']:
960 if not opts['force']:
961 ui.warn(_('%s: not overwriting - file exists\n') %
961 ui.warn(_('%s: not overwriting - file exists\n') %
962 reltarget)
962 reltarget)
963 return
963 return
964 if not opts['after'] and not opts.get('dry_run'):
964 if not opts['after'] and not opts.get('dry_run'):
965 os.unlink(reltarget)
965 os.unlink(reltarget)
966 if opts['after']:
966 if opts['after']:
967 if not os.path.exists(reltarget):
967 if not os.path.exists(reltarget):
968 return
968 return
969 else:
969 else:
970 targetdir = os.path.dirname(reltarget) or '.'
970 targetdir = os.path.dirname(reltarget) or '.'
971 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
971 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
972 os.makedirs(targetdir)
972 os.makedirs(targetdir)
973 try:
973 try:
974 restore = repo.dirstate.state(abstarget) == 'r'
974 restore = repo.dirstate.state(abstarget) == 'r'
975 if restore and not opts.get('dry_run'):
975 if restore and not opts.get('dry_run'):
976 repo.undelete([abstarget], wlock)
976 repo.undelete([abstarget], wlock)
977 try:
977 try:
978 if not opts.get('dry_run'):
978 if not opts.get('dry_run'):
979 shutil.copyfile(relsrc, reltarget)
979 shutil.copyfile(relsrc, reltarget)
980 shutil.copymode(relsrc, reltarget)
980 shutil.copymode(relsrc, reltarget)
981 restore = False
981 restore = False
982 finally:
982 finally:
983 if restore:
983 if restore:
984 repo.remove([abstarget], wlock)
984 repo.remove([abstarget], wlock)
985 except shutil.Error, inst:
985 except shutil.Error, inst:
986 raise util.Abort(str(inst))
986 raise util.Abort(str(inst))
987 except IOError, inst:
987 except IOError, inst:
988 if inst.errno == errno.ENOENT:
988 if inst.errno == errno.ENOENT:
989 ui.warn(_('%s: deleted in working copy\n') % relsrc)
989 ui.warn(_('%s: deleted in working copy\n') % relsrc)
990 else:
990 else:
991 ui.warn(_('%s: cannot copy - %s\n') %
991 ui.warn(_('%s: cannot copy - %s\n') %
992 (relsrc, inst.strerror))
992 (relsrc, inst.strerror))
993 errors += 1
993 errors += 1
994 return
994 return
995 if ui.verbose or not exact:
995 if ui.verbose or not exact:
996 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
996 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
997 targets[abstarget] = abssrc
997 targets[abstarget] = abssrc
998 if abstarget != origsrc and not opts.get('dry_run'):
998 if abstarget != origsrc and not opts.get('dry_run'):
999 repo.copy(origsrc, abstarget, wlock)
999 repo.copy(origsrc, abstarget, wlock)
1000 copied.append((abssrc, relsrc, exact))
1000 copied.append((abssrc, relsrc, exact))
1001
1001
1002 def targetpathfn(pat, dest, srcs):
1002 def targetpathfn(pat, dest, srcs):
1003 if os.path.isdir(pat):
1003 if os.path.isdir(pat):
1004 abspfx = util.canonpath(repo.root, cwd, pat)
1004 abspfx = util.canonpath(repo.root, cwd, pat)
1005 if destdirexists:
1005 if destdirexists:
1006 striplen = len(os.path.split(abspfx)[0])
1006 striplen = len(os.path.split(abspfx)[0])
1007 else:
1007 else:
1008 striplen = len(abspfx)
1008 striplen = len(abspfx)
1009 if striplen:
1009 if striplen:
1010 striplen += len(os.sep)
1010 striplen += len(os.sep)
1011 res = lambda p: os.path.join(dest, p[striplen:])
1011 res = lambda p: os.path.join(dest, p[striplen:])
1012 elif destdirexists:
1012 elif destdirexists:
1013 res = lambda p: os.path.join(dest, os.path.basename(p))
1013 res = lambda p: os.path.join(dest, os.path.basename(p))
1014 else:
1014 else:
1015 res = lambda p: dest
1015 res = lambda p: dest
1016 return res
1016 return res
1017
1017
1018 def targetpathafterfn(pat, dest, srcs):
1018 def targetpathafterfn(pat, dest, srcs):
1019 if util.patkind(pat, None)[0]:
1019 if util.patkind(pat, None)[0]:
1020 # a mercurial pattern
1020 # a mercurial pattern
1021 res = lambda p: os.path.join(dest, os.path.basename(p))
1021 res = lambda p: os.path.join(dest, os.path.basename(p))
1022 else:
1022 else:
1023 abspfx = util.canonpath(repo.root, cwd, pat)
1023 abspfx = util.canonpath(repo.root, cwd, pat)
1024 if len(abspfx) < len(srcs[0][0]):
1024 if len(abspfx) < len(srcs[0][0]):
1025 # A directory. Either the target path contains the last
1025 # A directory. Either the target path contains the last
1026 # component of the source path or it does not.
1026 # component of the source path or it does not.
1027 def evalpath(striplen):
1027 def evalpath(striplen):
1028 score = 0
1028 score = 0
1029 for s in srcs:
1029 for s in srcs:
1030 t = os.path.join(dest, s[0][striplen:])
1030 t = os.path.join(dest, s[0][striplen:])
1031 if os.path.exists(t):
1031 if os.path.exists(t):
1032 score += 1
1032 score += 1
1033 return score
1033 return score
1034
1034
1035 striplen = len(abspfx)
1035 striplen = len(abspfx)
1036 if striplen:
1036 if striplen:
1037 striplen += len(os.sep)
1037 striplen += len(os.sep)
1038 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1038 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1039 score = evalpath(striplen)
1039 score = evalpath(striplen)
1040 striplen1 = len(os.path.split(abspfx)[0])
1040 striplen1 = len(os.path.split(abspfx)[0])
1041 if striplen1:
1041 if striplen1:
1042 striplen1 += len(os.sep)
1042 striplen1 += len(os.sep)
1043 if evalpath(striplen1) > score:
1043 if evalpath(striplen1) > score:
1044 striplen = striplen1
1044 striplen = striplen1
1045 res = lambda p: os.path.join(dest, p[striplen:])
1045 res = lambda p: os.path.join(dest, p[striplen:])
1046 else:
1046 else:
1047 # a file
1047 # a file
1048 if destdirexists:
1048 if destdirexists:
1049 res = lambda p: os.path.join(dest, os.path.basename(p))
1049 res = lambda p: os.path.join(dest, os.path.basename(p))
1050 else:
1050 else:
1051 res = lambda p: dest
1051 res = lambda p: dest
1052 return res
1052 return res
1053
1053
1054
1054
1055 pats = list(pats)
1055 pats = list(pats)
1056 if not pats:
1056 if not pats:
1057 raise util.Abort(_('no source or destination specified'))
1057 raise util.Abort(_('no source or destination specified'))
1058 if len(pats) == 1:
1058 if len(pats) == 1:
1059 raise util.Abort(_('no destination specified'))
1059 raise util.Abort(_('no destination specified'))
1060 dest = pats.pop()
1060 dest = pats.pop()
1061 destdirexists = os.path.isdir(dest)
1061 destdirexists = os.path.isdir(dest)
1062 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1062 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1063 raise util.Abort(_('with multiple sources, destination must be an '
1063 raise util.Abort(_('with multiple sources, destination must be an '
1064 'existing directory'))
1064 'existing directory'))
1065 if opts['after']:
1065 if opts['after']:
1066 tfn = targetpathafterfn
1066 tfn = targetpathafterfn
1067 else:
1067 else:
1068 tfn = targetpathfn
1068 tfn = targetpathfn
1069 copylist = []
1069 copylist = []
1070 for pat in pats:
1070 for pat in pats:
1071 srcs = []
1071 srcs = []
1072 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
1072 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
1073 origsrc = okaytocopy(abssrc, relsrc, exact)
1073 origsrc = okaytocopy(abssrc, relsrc, exact)
1074 if origsrc:
1074 if origsrc:
1075 srcs.append((origsrc, abssrc, relsrc, exact))
1075 srcs.append((origsrc, abssrc, relsrc, exact))
1076 if not srcs:
1076 if not srcs:
1077 continue
1077 continue
1078 copylist.append((tfn(pat, dest, srcs), srcs))
1078 copylist.append((tfn(pat, dest, srcs), srcs))
1079 if not copylist:
1079 if not copylist:
1080 raise util.Abort(_('no files to copy'))
1080 raise util.Abort(_('no files to copy'))
1081
1081
1082 for targetpath, srcs in copylist:
1082 for targetpath, srcs in copylist:
1083 for origsrc, abssrc, relsrc, exact in srcs:
1083 for origsrc, abssrc, relsrc, exact in srcs:
1084 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1084 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1085
1085
1086 if errors:
1086 if errors:
1087 ui.warn(_('(consider using --after)\n'))
1087 ui.warn(_('(consider using --after)\n'))
1088 return errors, copied
1088 return errors, copied
1089
1089
1090 def copy(ui, repo, *pats, **opts):
1090 def copy(ui, repo, *pats, **opts):
1091 """mark files as copied for the next commit
1091 """mark files as copied for the next commit
1092
1092
1093 Mark dest as having copies of source files. If dest is a
1093 Mark dest as having copies of source files. If dest is a
1094 directory, copies are put in that directory. If dest is a file,
1094 directory, copies are put in that directory. If dest is a file,
1095 there can only be one source.
1095 there can only be one source.
1096
1096
1097 By default, this command copies the contents of files as they
1097 By default, this command copies the contents of files as they
1098 stand in the working directory. If invoked with --after, the
1098 stand in the working directory. If invoked with --after, the
1099 operation is recorded, but no copying is performed.
1099 operation is recorded, but no copying is performed.
1100
1100
1101 This command takes effect in the next commit.
1101 This command takes effect in the next commit.
1102
1102
1103 NOTE: This command should be treated as experimental. While it
1103 NOTE: This command should be treated as experimental. While it
1104 should properly record copied files, this information is not yet
1104 should properly record copied files, this information is not yet
1105 fully used by merge, nor fully reported by log.
1105 fully used by merge, nor fully reported by log.
1106 """
1106 """
1107 wlock = repo.wlock(0)
1107 wlock = repo.wlock(0)
1108 errs, copied = docopy(ui, repo, pats, opts, wlock)
1108 errs, copied = docopy(ui, repo, pats, opts, wlock)
1109 return errs
1109 return errs
1110
1110
1111 def debugancestor(ui, index, rev1, rev2):
1111 def debugancestor(ui, index, rev1, rev2):
1112 """find the ancestor revision of two revisions in a given index"""
1112 """find the ancestor revision of two revisions in a given index"""
1113 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1113 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1114 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1114 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1115 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1115 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1116
1116
1117 def debugcomplete(ui, cmd='', **opts):
1117 def debugcomplete(ui, cmd='', **opts):
1118 """returns the completion list associated with the given command"""
1118 """returns the completion list associated with the given command"""
1119
1119
1120 if opts['options']:
1120 if opts['options']:
1121 options = []
1121 options = []
1122 otables = [globalopts]
1122 otables = [globalopts]
1123 if cmd:
1123 if cmd:
1124 aliases, entry = findcmd(ui, cmd)
1124 aliases, entry = findcmd(ui, cmd)
1125 otables.append(entry[1])
1125 otables.append(entry[1])
1126 for t in otables:
1126 for t in otables:
1127 for o in t:
1127 for o in t:
1128 if o[0]:
1128 if o[0]:
1129 options.append('-%s' % o[0])
1129 options.append('-%s' % o[0])
1130 options.append('--%s' % o[1])
1130 options.append('--%s' % o[1])
1131 ui.write("%s\n" % "\n".join(options))
1131 ui.write("%s\n" % "\n".join(options))
1132 return
1132 return
1133
1133
1134 clist = findpossible(ui, cmd).keys()
1134 clist = findpossible(ui, cmd).keys()
1135 clist.sort()
1135 clist.sort()
1136 ui.write("%s\n" % "\n".join(clist))
1136 ui.write("%s\n" % "\n".join(clist))
1137
1137
1138 def debugrebuildstate(ui, repo, rev=None):
1138 def debugrebuildstate(ui, repo, rev=None):
1139 """rebuild the dirstate as it would look like for the given revision"""
1139 """rebuild the dirstate as it would look like for the given revision"""
1140 if not rev:
1140 if not rev:
1141 rev = repo.changelog.tip()
1141 rev = repo.changelog.tip()
1142 else:
1142 else:
1143 rev = repo.lookup(rev)
1143 rev = repo.lookup(rev)
1144 change = repo.changelog.read(rev)
1144 change = repo.changelog.read(rev)
1145 n = change[0]
1145 n = change[0]
1146 files = repo.manifest.read(n)
1146 files = repo.manifest.read(n)
1147 wlock = repo.wlock()
1147 wlock = repo.wlock()
1148 repo.dirstate.rebuild(rev, files)
1148 repo.dirstate.rebuild(rev, files)
1149
1149
1150 def debugcheckstate(ui, repo):
1150 def debugcheckstate(ui, repo):
1151 """validate the correctness of the current dirstate"""
1151 """validate the correctness of the current dirstate"""
1152 parent1, parent2 = repo.dirstate.parents()
1152 parent1, parent2 = repo.dirstate.parents()
1153 repo.dirstate.read()
1153 repo.dirstate.read()
1154 dc = repo.dirstate.map
1154 dc = repo.dirstate.map
1155 keys = dc.keys()
1155 keys = dc.keys()
1156 keys.sort()
1156 keys.sort()
1157 m1n = repo.changelog.read(parent1)[0]
1157 m1n = repo.changelog.read(parent1)[0]
1158 m2n = repo.changelog.read(parent2)[0]
1158 m2n = repo.changelog.read(parent2)[0]
1159 m1 = repo.manifest.read(m1n)
1159 m1 = repo.manifest.read(m1n)
1160 m2 = repo.manifest.read(m2n)
1160 m2 = repo.manifest.read(m2n)
1161 errors = 0
1161 errors = 0
1162 for f in dc:
1162 for f in dc:
1163 state = repo.dirstate.state(f)
1163 state = repo.dirstate.state(f)
1164 if state in "nr" and f not in m1:
1164 if state in "nr" and f not in m1:
1165 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1165 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1166 errors += 1
1166 errors += 1
1167 if state in "a" and f in m1:
1167 if state in "a" and f in m1:
1168 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1168 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1169 errors += 1
1169 errors += 1
1170 if state in "m" and f not in m1 and f not in m2:
1170 if state in "m" and f not in m1 and f not in m2:
1171 ui.warn(_("%s in state %s, but not in either manifest\n") %
1171 ui.warn(_("%s in state %s, but not in either manifest\n") %
1172 (f, state))
1172 (f, state))
1173 errors += 1
1173 errors += 1
1174 for f in m1:
1174 for f in m1:
1175 state = repo.dirstate.state(f)
1175 state = repo.dirstate.state(f)
1176 if state not in "nrm":
1176 if state not in "nrm":
1177 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1177 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1178 errors += 1
1178 errors += 1
1179 if errors:
1179 if errors:
1180 error = _(".hg/dirstate inconsistent with current parent's manifest")
1180 error = _(".hg/dirstate inconsistent with current parent's manifest")
1181 raise util.Abort(error)
1181 raise util.Abort(error)
1182
1182
1183 def showconfig(ui, repo, *values):
1183 def showconfig(ui, repo, *values):
1184 """show combined config settings from all hgrc files
1184 """show combined config settings from all hgrc files
1185
1185
1186 With no args, print names and values of all config items.
1186 With no args, print names and values of all config items.
1187
1187
1188 With one arg of the form section.name, print just the value of
1188 With one arg of the form section.name, print just the value of
1189 that config item.
1189 that config item.
1190
1190
1191 With multiple args, print names and values of all config items
1191 With multiple args, print names and values of all config items
1192 with matching section names."""
1192 with matching section names."""
1193
1193
1194 if values:
1194 if values:
1195 if len([v for v in values if '.' in v]) > 1:
1195 if len([v for v in values if '.' in v]) > 1:
1196 raise util.Abort(_('only one config item permitted'))
1196 raise util.Abort(_('only one config item permitted'))
1197 for section, name, value in ui.walkconfig():
1197 for section, name, value in ui.walkconfig():
1198 sectname = section + '.' + name
1198 sectname = section + '.' + name
1199 if values:
1199 if values:
1200 for v in values:
1200 for v in values:
1201 if v == section:
1201 if v == section:
1202 ui.write('%s=%s\n' % (sectname, value))
1202 ui.write('%s=%s\n' % (sectname, value))
1203 elif v == sectname:
1203 elif v == sectname:
1204 ui.write(value, '\n')
1204 ui.write(value, '\n')
1205 else:
1205 else:
1206 ui.write('%s=%s\n' % (sectname, value))
1206 ui.write('%s=%s\n' % (sectname, value))
1207
1207
1208 def debugsetparents(ui, repo, rev1, rev2=None):
1208 def debugsetparents(ui, repo, rev1, rev2=None):
1209 """manually set the parents of the current working directory
1209 """manually set the parents of the current working directory
1210
1210
1211 This is useful for writing repository conversion tools, but should
1211 This is useful for writing repository conversion tools, but should
1212 be used with care.
1212 be used with care.
1213 """
1213 """
1214
1214
1215 if not rev2:
1215 if not rev2:
1216 rev2 = hex(nullid)
1216 rev2 = hex(nullid)
1217
1217
1218 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1218 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1219
1219
1220 def debugstate(ui, repo):
1220 def debugstate(ui, repo):
1221 """show the contents of the current dirstate"""
1221 """show the contents of the current dirstate"""
1222 repo.dirstate.read()
1222 repo.dirstate.read()
1223 dc = repo.dirstate.map
1223 dc = repo.dirstate.map
1224 keys = dc.keys()
1224 keys = dc.keys()
1225 keys.sort()
1225 keys.sort()
1226 for file_ in keys:
1226 for file_ in keys:
1227 ui.write("%c %3o %10d %s %s\n"
1227 ui.write("%c %3o %10d %s %s\n"
1228 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1228 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1229 time.strftime("%x %X",
1229 time.strftime("%x %X",
1230 time.localtime(dc[file_][3])), file_))
1230 time.localtime(dc[file_][3])), file_))
1231 for f in repo.dirstate.copies():
1231 for f in repo.dirstate.copies():
1232 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1232 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1233
1233
1234 def debugdata(ui, file_, rev):
1234 def debugdata(ui, file_, rev):
1235 """dump the contents of an data file revision"""
1235 """dump the contents of an data file revision"""
1236 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1236 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1237 file_[:-2] + ".i", file_, 0)
1237 file_[:-2] + ".i", file_, 0)
1238 try:
1238 try:
1239 ui.write(r.revision(r.lookup(rev)))
1239 ui.write(r.revision(r.lookup(rev)))
1240 except KeyError:
1240 except KeyError:
1241 raise util.Abort(_('invalid revision identifier %s') % rev)
1241 raise util.Abort(_('invalid revision identifier %s') % rev)
1242
1242
1243 def debugindex(ui, file_):
1243 def debugindex(ui, file_):
1244 """dump the contents of an index file"""
1244 """dump the contents of an index file"""
1245 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1245 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1246 ui.write(" rev offset length base linkrev" +
1246 ui.write(" rev offset length base linkrev" +
1247 " nodeid p1 p2\n")
1247 " nodeid p1 p2\n")
1248 for i in range(r.count()):
1248 for i in range(r.count()):
1249 node = r.node(i)
1249 node = r.node(i)
1250 pp = r.parents(node)
1250 pp = r.parents(node)
1251 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1251 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1252 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1252 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1253 short(node), short(pp[0]), short(pp[1])))
1253 short(node), short(pp[0]), short(pp[1])))
1254
1254
1255 def debugindexdot(ui, file_):
1255 def debugindexdot(ui, file_):
1256 """dump an index DAG as a .dot file"""
1256 """dump an index DAG as a .dot file"""
1257 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1257 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1258 ui.write("digraph G {\n")
1258 ui.write("digraph G {\n")
1259 for i in range(r.count()):
1259 for i in range(r.count()):
1260 node = r.node(i)
1260 node = r.node(i)
1261 pp = r.parents(node)
1261 pp = r.parents(node)
1262 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1262 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1263 if pp[1] != nullid:
1263 if pp[1] != nullid:
1264 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1264 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1265 ui.write("}\n")
1265 ui.write("}\n")
1266
1266
1267 def debugrename(ui, repo, file, rev=None):
1267 def debugrename(ui, repo, file, rev=None):
1268 """dump rename information"""
1268 """dump rename information"""
1269 r = repo.file(relpath(repo, [file])[0])
1269 r = repo.file(relpath(repo, [file])[0])
1270 if rev:
1270 if rev:
1271 try:
1271 try:
1272 # assume all revision numbers are for changesets
1272 # assume all revision numbers are for changesets
1273 n = repo.lookup(rev)
1273 n = repo.lookup(rev)
1274 change = repo.changelog.read(n)
1274 change = repo.changelog.read(n)
1275 m = repo.manifest.read(change[0])
1275 m = repo.manifest.read(change[0])
1276 n = m[relpath(repo, [file])[0]]
1276 n = m[relpath(repo, [file])[0]]
1277 except (hg.RepoError, KeyError):
1277 except (hg.RepoError, KeyError):
1278 n = r.lookup(rev)
1278 n = r.lookup(rev)
1279 else:
1279 else:
1280 n = r.tip()
1280 n = r.tip()
1281 m = r.renamed(n)
1281 m = r.renamed(n)
1282 if m:
1282 if m:
1283 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1283 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1284 else:
1284 else:
1285 ui.write(_("not renamed\n"))
1285 ui.write(_("not renamed\n"))
1286
1286
1287 def debugwalk(ui, repo, *pats, **opts):
1287 def debugwalk(ui, repo, *pats, **opts):
1288 """show how files match on given patterns"""
1288 """show how files match on given patterns"""
1289 items = list(cmdutil.walk(repo, pats, opts))
1289 items = list(cmdutil.walk(repo, pats, opts))
1290 if not items:
1290 if not items:
1291 return
1291 return
1292 fmt = '%%s %%-%ds %%-%ds %%s' % (
1292 fmt = '%%s %%-%ds %%-%ds %%s' % (
1293 max([len(abs) for (src, abs, rel, exact) in items]),
1293 max([len(abs) for (src, abs, rel, exact) in items]),
1294 max([len(rel) for (src, abs, rel, exact) in items]))
1294 max([len(rel) for (src, abs, rel, exact) in items]))
1295 for src, abs, rel, exact in items:
1295 for src, abs, rel, exact in items:
1296 line = fmt % (src, abs, rel, exact and 'exact' or '')
1296 line = fmt % (src, abs, rel, exact and 'exact' or '')
1297 ui.write("%s\n" % line.rstrip())
1297 ui.write("%s\n" % line.rstrip())
1298
1298
1299 def diff(ui, repo, *pats, **opts):
1299 def diff(ui, repo, *pats, **opts):
1300 """diff repository (or selected files)
1300 """diff repository (or selected files)
1301
1301
1302 Show differences between revisions for the specified files.
1302 Show differences between revisions for the specified files.
1303
1303
1304 Differences between files are shown using the unified diff format.
1304 Differences between files are shown using the unified diff format.
1305
1305
1306 When two revision arguments are given, then changes are shown
1306 When two revision arguments are given, then changes are shown
1307 between those revisions. If only one revision is specified then
1307 between those revisions. If only one revision is specified then
1308 that revision is compared to the working directory, and, when no
1308 that revision is compared to the working directory, and, when no
1309 revisions are specified, the working directory files are compared
1309 revisions are specified, the working directory files are compared
1310 to its parent.
1310 to its parent.
1311
1311
1312 Without the -a option, diff will avoid generating diffs of files
1312 Without the -a option, diff will avoid generating diffs of files
1313 it detects as binary. With -a, diff will generate a diff anyway,
1313 it detects as binary. With -a, diff will generate a diff anyway,
1314 probably with undesirable results.
1314 probably with undesirable results.
1315 """
1315 """
1316 node1, node2 = cmdutil.revpair(ui, repo, opts['rev'])
1316 node1, node2 = cmdutil.revpair(ui, repo, opts['rev'])
1317
1317
1318 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1318 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1319
1319
1320 patch.diff(repo, node1, node2, fns, match=matchfn,
1320 patch.diff(repo, node1, node2, fns, match=matchfn,
1321 opts=patch.diffopts(ui, opts))
1321 opts=patch.diffopts(ui, opts))
1322
1322
1323 def export(ui, repo, *changesets, **opts):
1323 def export(ui, repo, *changesets, **opts):
1324 """dump the header and diffs for one or more changesets
1324 """dump the header and diffs for one or more changesets
1325
1325
1326 Print the changeset header and diffs for one or more revisions.
1326 Print the changeset header and diffs for one or more revisions.
1327
1327
1328 The information shown in the changeset header is: author,
1328 The information shown in the changeset header is: author,
1329 changeset hash, parent and commit comment.
1329 changeset hash, parent and commit comment.
1330
1330
1331 Output may be to a file, in which case the name of the file is
1331 Output may be to a file, in which case the name of the file is
1332 given using a format string. The formatting rules are as follows:
1332 given using a format string. The formatting rules are as follows:
1333
1333
1334 %% literal "%" character
1334 %% literal "%" character
1335 %H changeset hash (40 bytes of hexadecimal)
1335 %H changeset hash (40 bytes of hexadecimal)
1336 %N number of patches being generated
1336 %N number of patches being generated
1337 %R changeset revision number
1337 %R changeset revision number
1338 %b basename of the exporting repository
1338 %b basename of the exporting repository
1339 %h short-form changeset hash (12 bytes of hexadecimal)
1339 %h short-form changeset hash (12 bytes of hexadecimal)
1340 %n zero-padded sequence number, starting at 1
1340 %n zero-padded sequence number, starting at 1
1341 %r zero-padded changeset revision number
1341 %r zero-padded changeset revision number
1342
1342
1343 Without the -a option, export will avoid generating diffs of files
1343 Without the -a option, export will avoid generating diffs of files
1344 it detects as binary. With -a, export will generate a diff anyway,
1344 it detects as binary. With -a, export will generate a diff anyway,
1345 probably with undesirable results.
1345 probably with undesirable results.
1346
1346
1347 With the --switch-parent option, the diff will be against the second
1347 With the --switch-parent option, the diff will be against the second
1348 parent. It can be useful to review a merge.
1348 parent. It can be useful to review a merge.
1349 """
1349 """
1350 if not changesets:
1350 if not changesets:
1351 raise util.Abort(_("export requires at least one changeset"))
1351 raise util.Abort(_("export requires at least one changeset"))
1352 revs = list(cmdutil.revrange(ui, repo, changesets))
1352 revs = list(cmdutil.revrange(ui, repo, changesets))
1353 if len(revs) > 1:
1353 if len(revs) > 1:
1354 ui.note(_('exporting patches:\n'))
1354 ui.note(_('exporting patches:\n'))
1355 else:
1355 else:
1356 ui.note(_('exporting patch:\n'))
1356 ui.note(_('exporting patch:\n'))
1357 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1357 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1358 switch_parent=opts['switch_parent'],
1358 switch_parent=opts['switch_parent'],
1359 opts=patch.diffopts(ui, opts))
1359 opts=patch.diffopts(ui, opts))
1360
1360
1361 def forget(ui, repo, *pats, **opts):
1361 def forget(ui, repo, *pats, **opts):
1362 """don't add the specified files on the next commit (DEPRECATED)
1362 """don't add the specified files on the next commit (DEPRECATED)
1363
1363
1364 (DEPRECATED)
1364 (DEPRECATED)
1365 Undo an 'hg add' scheduled for the next commit.
1365 Undo an 'hg add' scheduled for the next commit.
1366
1366
1367 This command is now deprecated and will be removed in a future
1367 This command is now deprecated and will be removed in a future
1368 release. Please use revert instead.
1368 release. Please use revert instead.
1369 """
1369 """
1370 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1370 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1371 forget = []
1371 forget = []
1372 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
1372 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
1373 if repo.dirstate.state(abs) == 'a':
1373 if repo.dirstate.state(abs) == 'a':
1374 forget.append(abs)
1374 forget.append(abs)
1375 if ui.verbose or not exact:
1375 if ui.verbose or not exact:
1376 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1376 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1377 repo.forget(forget)
1377 repo.forget(forget)
1378
1378
1379 def grep(ui, repo, pattern, *pats, **opts):
1379 def grep(ui, repo, pattern, *pats, **opts):
1380 """search for a pattern in specified files and revisions
1380 """search for a pattern in specified files and revisions
1381
1381
1382 Search revisions of files for a regular expression.
1382 Search revisions of files for a regular expression.
1383
1383
1384 This command behaves differently than Unix grep. It only accepts
1384 This command behaves differently than Unix grep. It only accepts
1385 Python/Perl regexps. It searches repository history, not the
1385 Python/Perl regexps. It searches repository history, not the
1386 working directory. It always prints the revision number in which
1386 working directory. It always prints the revision number in which
1387 a match appears.
1387 a match appears.
1388
1388
1389 By default, grep only prints output for the first revision of a
1389 By default, grep only prints output for the first revision of a
1390 file in which it finds a match. To get it to print every revision
1390 file in which it finds a match. To get it to print every revision
1391 that contains a change in match status ("-" for a match that
1391 that contains a change in match status ("-" for a match that
1392 becomes a non-match, or "+" for a non-match that becomes a match),
1392 becomes a non-match, or "+" for a non-match that becomes a match),
1393 use the --all flag.
1393 use the --all flag.
1394 """
1394 """
1395 reflags = 0
1395 reflags = 0
1396 if opts['ignore_case']:
1396 if opts['ignore_case']:
1397 reflags |= re.I
1397 reflags |= re.I
1398 regexp = re.compile(pattern, reflags)
1398 regexp = re.compile(pattern, reflags)
1399 sep, eol = ':', '\n'
1399 sep, eol = ':', '\n'
1400 if opts['print0']:
1400 if opts['print0']:
1401 sep = eol = '\0'
1401 sep = eol = '\0'
1402
1402
1403 fcache = {}
1403 fcache = {}
1404 def getfile(fn):
1404 def getfile(fn):
1405 if fn not in fcache:
1405 if fn not in fcache:
1406 fcache[fn] = repo.file(fn)
1406 fcache[fn] = repo.file(fn)
1407 return fcache[fn]
1407 return fcache[fn]
1408
1408
1409 def matchlines(body):
1409 def matchlines(body):
1410 begin = 0
1410 begin = 0
1411 linenum = 0
1411 linenum = 0
1412 while True:
1412 while True:
1413 match = regexp.search(body, begin)
1413 match = regexp.search(body, begin)
1414 if not match:
1414 if not match:
1415 break
1415 break
1416 mstart, mend = match.span()
1416 mstart, mend = match.span()
1417 linenum += body.count('\n', begin, mstart) + 1
1417 linenum += body.count('\n', begin, mstart) + 1
1418 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1418 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1419 lend = body.find('\n', mend)
1419 lend = body.find('\n', mend)
1420 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1420 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1421 begin = lend + 1
1421 begin = lend + 1
1422
1422
1423 class linestate(object):
1423 class linestate(object):
1424 def __init__(self, line, linenum, colstart, colend):
1424 def __init__(self, line, linenum, colstart, colend):
1425 self.line = line
1425 self.line = line
1426 self.linenum = linenum
1426 self.linenum = linenum
1427 self.colstart = colstart
1427 self.colstart = colstart
1428 self.colend = colend
1428 self.colend = colend
1429
1429
1430 def __eq__(self, other):
1430 def __eq__(self, other):
1431 return self.line == other.line
1431 return self.line == other.line
1432
1432
1433 matches = {}
1433 matches = {}
1434 copies = {}
1434 copies = {}
1435 def grepbody(fn, rev, body):
1435 def grepbody(fn, rev, body):
1436 matches[rev].setdefault(fn, [])
1436 matches[rev].setdefault(fn, [])
1437 m = matches[rev][fn]
1437 m = matches[rev][fn]
1438 for lnum, cstart, cend, line in matchlines(body):
1438 for lnum, cstart, cend, line in matchlines(body):
1439 s = linestate(line, lnum, cstart, cend)
1439 s = linestate(line, lnum, cstart, cend)
1440 m.append(s)
1440 m.append(s)
1441
1441
1442 def difflinestates(a, b):
1442 def difflinestates(a, b):
1443 sm = difflib.SequenceMatcher(None, a, b)
1443 sm = difflib.SequenceMatcher(None, a, b)
1444 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1444 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1445 if tag == 'insert':
1445 if tag == 'insert':
1446 for i in range(blo, bhi):
1446 for i in range(blo, bhi):
1447 yield ('+', b[i])
1447 yield ('+', b[i])
1448 elif tag == 'delete':
1448 elif tag == 'delete':
1449 for i in range(alo, ahi):
1449 for i in range(alo, ahi):
1450 yield ('-', a[i])
1450 yield ('-', a[i])
1451 elif tag == 'replace':
1451 elif tag == 'replace':
1452 for i in range(alo, ahi):
1452 for i in range(alo, ahi):
1453 yield ('-', a[i])
1453 yield ('-', a[i])
1454 for i in range(blo, bhi):
1454 for i in range(blo, bhi):
1455 yield ('+', b[i])
1455 yield ('+', b[i])
1456
1456
1457 prev = {}
1457 prev = {}
1458 ucache = {}
1458 ucache = {}
1459 def display(fn, rev, states, prevstates):
1459 def display(fn, rev, states, prevstates):
1460 counts = {'-': 0, '+': 0}
1460 counts = {'-': 0, '+': 0}
1461 filerevmatches = {}
1461 filerevmatches = {}
1462 if incrementing or not opts['all']:
1462 if incrementing or not opts['all']:
1463 a, b = prevstates, states
1463 a, b = prevstates, states
1464 else:
1464 else:
1465 a, b = states, prevstates
1465 a, b = states, prevstates
1466 for change, l in difflinestates(a, b):
1466 for change, l in difflinestates(a, b):
1467 if incrementing or not opts['all']:
1467 if incrementing or not opts['all']:
1468 r = rev
1468 r = rev
1469 else:
1469 else:
1470 r = prev[fn]
1470 r = prev[fn]
1471 cols = [fn, str(r)]
1471 cols = [fn, str(r)]
1472 if opts['line_number']:
1472 if opts['line_number']:
1473 cols.append(str(l.linenum))
1473 cols.append(str(l.linenum))
1474 if opts['all']:
1474 if opts['all']:
1475 cols.append(change)
1475 cols.append(change)
1476 if opts['user']:
1476 if opts['user']:
1477 cols.append(trimuser(ui, getchange(r)[1], rev,
1477 cols.append(trimuser(ui, getchange(r)[1], rev,
1478 ucache))
1478 ucache))
1479 if opts['files_with_matches']:
1479 if opts['files_with_matches']:
1480 c = (fn, rev)
1480 c = (fn, rev)
1481 if c in filerevmatches:
1481 if c in filerevmatches:
1482 continue
1482 continue
1483 filerevmatches[c] = 1
1483 filerevmatches[c] = 1
1484 else:
1484 else:
1485 cols.append(l.line)
1485 cols.append(l.line)
1486 ui.write(sep.join(cols), eol)
1486 ui.write(sep.join(cols), eol)
1487 counts[change] += 1
1487 counts[change] += 1
1488 return counts['+'], counts['-']
1488 return counts['+'], counts['-']
1489
1489
1490 fstate = {}
1490 fstate = {}
1491 skip = {}
1491 skip = {}
1492 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1492 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1493 count = 0
1493 count = 0
1494 incrementing = False
1494 incrementing = False
1495 follow = opts.get('follow')
1495 follow = opts.get('follow')
1496 for st, rev, fns in changeiter:
1496 for st, rev, fns in changeiter:
1497 if st == 'window':
1497 if st == 'window':
1498 incrementing = rev
1498 incrementing = rev
1499 matches.clear()
1499 matches.clear()
1500 elif st == 'add':
1500 elif st == 'add':
1501 change = repo.changelog.read(repo.lookup(str(rev)))
1501 change = repo.changelog.read(repo.lookup(str(rev)))
1502 mf = repo.manifest.read(change[0])
1502 mf = repo.manifest.read(change[0])
1503 matches[rev] = {}
1503 matches[rev] = {}
1504 for fn in fns:
1504 for fn in fns:
1505 if fn in skip:
1505 if fn in skip:
1506 continue
1506 continue
1507 fstate.setdefault(fn, {})
1507 fstate.setdefault(fn, {})
1508 try:
1508 try:
1509 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1509 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1510 if follow:
1510 if follow:
1511 copied = getfile(fn).renamed(mf[fn])
1511 copied = getfile(fn).renamed(mf[fn])
1512 if copied:
1512 if copied:
1513 copies.setdefault(rev, {})[fn] = copied[0]
1513 copies.setdefault(rev, {})[fn] = copied[0]
1514 except KeyError:
1514 except KeyError:
1515 pass
1515 pass
1516 elif st == 'iter':
1516 elif st == 'iter':
1517 states = matches[rev].items()
1517 states = matches[rev].items()
1518 states.sort()
1518 states.sort()
1519 for fn, m in states:
1519 for fn, m in states:
1520 copy = copies.get(rev, {}).get(fn)
1520 copy = copies.get(rev, {}).get(fn)
1521 if fn in skip:
1521 if fn in skip:
1522 if copy:
1522 if copy:
1523 skip[copy] = True
1523 skip[copy] = True
1524 continue
1524 continue
1525 if incrementing or not opts['all'] or fstate[fn]:
1525 if incrementing or not opts['all'] or fstate[fn]:
1526 pos, neg = display(fn, rev, m, fstate[fn])
1526 pos, neg = display(fn, rev, m, fstate[fn])
1527 count += pos + neg
1527 count += pos + neg
1528 if pos and not opts['all']:
1528 if pos and not opts['all']:
1529 skip[fn] = True
1529 skip[fn] = True
1530 if copy:
1530 if copy:
1531 skip[copy] = True
1531 skip[copy] = True
1532 fstate[fn] = m
1532 fstate[fn] = m
1533 if copy:
1533 if copy:
1534 fstate[copy] = m
1534 fstate[copy] = m
1535 prev[fn] = rev
1535 prev[fn] = rev
1536
1536
1537 if not incrementing:
1537 if not incrementing:
1538 fstate = fstate.items()
1538 fstate = fstate.items()
1539 fstate.sort()
1539 fstate.sort()
1540 for fn, state in fstate:
1540 for fn, state in fstate:
1541 if fn in skip:
1541 if fn in skip:
1542 continue
1542 continue
1543 if fn not in copies.get(prev[fn], {}):
1543 if fn not in copies.get(prev[fn], {}):
1544 display(fn, rev, {}, state)
1544 display(fn, rev, {}, state)
1545 return (count == 0 and 1) or 0
1545 return (count == 0 and 1) or 0
1546
1546
1547 def heads(ui, repo, **opts):
1547 def heads(ui, repo, **opts):
1548 """show current repository heads
1548 """show current repository heads
1549
1549
1550 Show all repository head changesets.
1550 Show all repository head changesets.
1551
1551
1552 Repository "heads" are changesets that don't have children
1552 Repository "heads" are changesets that don't have children
1553 changesets. They are where development generally takes place and
1553 changesets. They are where development generally takes place and
1554 are the usual targets for update and merge operations.
1554 are the usual targets for update and merge operations.
1555 """
1555 """
1556 if opts['rev']:
1556 if opts['rev']:
1557 heads = repo.heads(repo.lookup(opts['rev']))
1557 heads = repo.heads(repo.lookup(opts['rev']))
1558 else:
1558 else:
1559 heads = repo.heads()
1559 heads = repo.heads()
1560 br = None
1560 br = None
1561 if opts['branches']:
1561 if opts['branches']:
1562 br = repo.branchlookup(heads)
1562 br = repo.branchlookup(heads)
1563 displayer = show_changeset(ui, repo, opts)
1563 displayer = show_changeset(ui, repo, opts)
1564 for n in heads:
1564 for n in heads:
1565 displayer.show(changenode=n, brinfo=br)
1565 displayer.show(changenode=n, brinfo=br)
1566
1566
1567 def identify(ui, repo):
1567 def identify(ui, repo):
1568 """print information about the working copy
1568 """print information about the working copy
1569
1569
1570 Print a short summary of the current state of the repo.
1570 Print a short summary of the current state of the repo.
1571
1571
1572 This summary identifies the repository state using one or two parent
1572 This summary identifies the repository state using one or two parent
1573 hash identifiers, followed by a "+" if there are uncommitted changes
1573 hash identifiers, followed by a "+" if there are uncommitted changes
1574 in the working directory, followed by a list of tags for this revision.
1574 in the working directory, followed by a list of tags for this revision.
1575 """
1575 """
1576 parents = [p for p in repo.dirstate.parents() if p != nullid]
1576 parents = [p for p in repo.dirstate.parents() if p != nullid]
1577 if not parents:
1577 if not parents:
1578 ui.write(_("unknown\n"))
1578 ui.write(_("unknown\n"))
1579 return
1579 return
1580
1580
1581 hexfunc = ui.debugflag and hex or short
1581 hexfunc = ui.debugflag and hex or short
1582 modified, added, removed, deleted = repo.status()[:4]
1582 modified, added, removed, deleted = repo.status()[:4]
1583 output = ["%s%s" %
1583 output = ["%s%s" %
1584 ('+'.join([hexfunc(parent) for parent in parents]),
1584 ('+'.join([hexfunc(parent) for parent in parents]),
1585 (modified or added or removed or deleted) and "+" or "")]
1585 (modified or added or removed or deleted) and "+" or "")]
1586
1586
1587 if not ui.quiet:
1587 if not ui.quiet:
1588
1588
1589 branch = repo.workingctx().branch()
1589 branch = repo.workingctx().branch()
1590 if branch:
1590 if branch:
1591 output.append("(%s)" % branch)
1591 output.append("(%s)" % branch)
1592
1592
1593 # multiple tags for a single parent separated by '/'
1593 # multiple tags for a single parent separated by '/'
1594 parenttags = ['/'.join(tags)
1594 parenttags = ['/'.join(tags)
1595 for tags in map(repo.nodetags, parents) if tags]
1595 for tags in map(repo.nodetags, parents) if tags]
1596 # tags for multiple parents separated by ' + '
1596 # tags for multiple parents separated by ' + '
1597 if parenttags:
1597 if parenttags:
1598 output.append(' + '.join(parenttags))
1598 output.append(' + '.join(parenttags))
1599
1599
1600 ui.write("%s\n" % ' '.join(output))
1600 ui.write("%s\n" % ' '.join(output))
1601
1601
1602 def import_(ui, repo, patch1, *patches, **opts):
1602 def import_(ui, repo, patch1, *patches, **opts):
1603 """import an ordered set of patches
1603 """import an ordered set of patches
1604
1604
1605 Import a list of patches and commit them individually.
1605 Import a list of patches and commit them individually.
1606
1606
1607 If there are outstanding changes in the working directory, import
1607 If there are outstanding changes in the working directory, import
1608 will abort unless given the -f flag.
1608 will abort unless given the -f flag.
1609
1609
1610 You can import a patch straight from a mail message. Even patches
1610 You can import a patch straight from a mail message. Even patches
1611 as attachments work (body part must be type text/plain or
1611 as attachments work (body part must be type text/plain or
1612 text/x-patch to be used). From and Subject headers of email
1612 text/x-patch to be used). From and Subject headers of email
1613 message are used as default committer and commit message. All
1613 message are used as default committer and commit message. All
1614 text/plain body parts before first diff are added to commit
1614 text/plain body parts before first diff are added to commit
1615 message.
1615 message.
1616
1616
1617 If imported patch was generated by hg export, user and description
1617 If imported patch was generated by hg export, user and description
1618 from patch override values from message headers and body. Values
1618 from patch override values from message headers and body. Values
1619 given on command line with -m and -u override these.
1619 given on command line with -m and -u override these.
1620
1620
1621 To read a patch from standard input, use patch name "-".
1621 To read a patch from standard input, use patch name "-".
1622 """
1622 """
1623 patches = (patch1,) + patches
1623 patches = (patch1,) + patches
1624
1624
1625 if not opts['force']:
1625 if not opts['force']:
1626 bail_if_changed(repo)
1626 bail_if_changed(repo)
1627
1627
1628 d = opts["base"]
1628 d = opts["base"]
1629 strip = opts["strip"]
1629 strip = opts["strip"]
1630
1630
1631 wlock = repo.wlock()
1631 wlock = repo.wlock()
1632 lock = repo.lock()
1632 lock = repo.lock()
1633
1633
1634 for p in patches:
1634 for p in patches:
1635 pf = os.path.join(d, p)
1635 pf = os.path.join(d, p)
1636
1636
1637 if pf == '-':
1637 if pf == '-':
1638 ui.status(_("applying patch from stdin\n"))
1638 ui.status(_("applying patch from stdin\n"))
1639 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1639 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1640 else:
1640 else:
1641 ui.status(_("applying %s\n") % p)
1641 ui.status(_("applying %s\n") % p)
1642 tmpname, message, user, date = patch.extract(ui, file(pf))
1642 tmpname, message, user, date = patch.extract(ui, file(pf))
1643
1643
1644 if tmpname is None:
1644 if tmpname is None:
1645 raise util.Abort(_('no diffs found'))
1645 raise util.Abort(_('no diffs found'))
1646
1646
1647 try:
1647 try:
1648 if opts['message']:
1648 if opts['message']:
1649 # pickup the cmdline msg
1649 # pickup the cmdline msg
1650 message = opts['message']
1650 message = opts['message']
1651 elif message:
1651 elif message:
1652 # pickup the patch msg
1652 # pickup the patch msg
1653 message = message.strip()
1653 message = message.strip()
1654 else:
1654 else:
1655 # launch the editor
1655 # launch the editor
1656 message = None
1656 message = None
1657 ui.debug(_('message:\n%s\n') % message)
1657 ui.debug(_('message:\n%s\n') % message)
1658
1658
1659 files, fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root)
1659 files = {}
1660 files = patch.updatedir(ui, repo, files, wlock=wlock)
1660 try:
1661 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1662 files=files)
1663 finally:
1664 files = patch.updatedir(ui, repo, files, wlock=wlock)
1661 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1665 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1662 finally:
1666 finally:
1663 os.unlink(tmpname)
1667 os.unlink(tmpname)
1664
1668
1665 def incoming(ui, repo, source="default", **opts):
1669 def incoming(ui, repo, source="default", **opts):
1666 """show new changesets found in source
1670 """show new changesets found in source
1667
1671
1668 Show new changesets found in the specified path/URL or the default
1672 Show new changesets found in the specified path/URL or the default
1669 pull location. These are the changesets that would be pulled if a pull
1673 pull location. These are the changesets that would be pulled if a pull
1670 was requested.
1674 was requested.
1671
1675
1672 For remote repository, using --bundle avoids downloading the changesets
1676 For remote repository, using --bundle avoids downloading the changesets
1673 twice if the incoming is followed by a pull.
1677 twice if the incoming is followed by a pull.
1674
1678
1675 See pull for valid source format details.
1679 See pull for valid source format details.
1676 """
1680 """
1677 source = ui.expandpath(source)
1681 source = ui.expandpath(source)
1678 setremoteconfig(ui, opts)
1682 setremoteconfig(ui, opts)
1679
1683
1680 other = hg.repository(ui, source)
1684 other = hg.repository(ui, source)
1681 incoming = repo.findincoming(other, force=opts["force"])
1685 incoming = repo.findincoming(other, force=opts["force"])
1682 if not incoming:
1686 if not incoming:
1683 ui.status(_("no changes found\n"))
1687 ui.status(_("no changes found\n"))
1684 return
1688 return
1685
1689
1686 cleanup = None
1690 cleanup = None
1687 try:
1691 try:
1688 fname = opts["bundle"]
1692 fname = opts["bundle"]
1689 if fname or not other.local():
1693 if fname or not other.local():
1690 # create a bundle (uncompressed if other repo is not local)
1694 # create a bundle (uncompressed if other repo is not local)
1691 cg = other.changegroup(incoming, "incoming")
1695 cg = other.changegroup(incoming, "incoming")
1692 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1696 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1693 # keep written bundle?
1697 # keep written bundle?
1694 if opts["bundle"]:
1698 if opts["bundle"]:
1695 cleanup = None
1699 cleanup = None
1696 if not other.local():
1700 if not other.local():
1697 # use the created uncompressed bundlerepo
1701 # use the created uncompressed bundlerepo
1698 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1702 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1699
1703
1700 revs = None
1704 revs = None
1701 if opts['rev']:
1705 if opts['rev']:
1702 revs = [other.lookup(rev) for rev in opts['rev']]
1706 revs = [other.lookup(rev) for rev in opts['rev']]
1703 o = other.changelog.nodesbetween(incoming, revs)[0]
1707 o = other.changelog.nodesbetween(incoming, revs)[0]
1704 if opts['newest_first']:
1708 if opts['newest_first']:
1705 o.reverse()
1709 o.reverse()
1706 displayer = show_changeset(ui, other, opts)
1710 displayer = show_changeset(ui, other, opts)
1707 for n in o:
1711 for n in o:
1708 parents = [p for p in other.changelog.parents(n) if p != nullid]
1712 parents = [p for p in other.changelog.parents(n) if p != nullid]
1709 if opts['no_merges'] and len(parents) == 2:
1713 if opts['no_merges'] and len(parents) == 2:
1710 continue
1714 continue
1711 displayer.show(changenode=n)
1715 displayer.show(changenode=n)
1712 if opts['patch']:
1716 if opts['patch']:
1713 prev = (parents and parents[0]) or nullid
1717 prev = (parents and parents[0]) or nullid
1714 patch.diff(other, prev, n, fp=repo.ui)
1718 patch.diff(other, prev, n, fp=repo.ui)
1715 ui.write("\n")
1719 ui.write("\n")
1716 finally:
1720 finally:
1717 if hasattr(other, 'close'):
1721 if hasattr(other, 'close'):
1718 other.close()
1722 other.close()
1719 if cleanup:
1723 if cleanup:
1720 os.unlink(cleanup)
1724 os.unlink(cleanup)
1721
1725
1722 def init(ui, dest=".", **opts):
1726 def init(ui, dest=".", **opts):
1723 """create a new repository in the given directory
1727 """create a new repository in the given directory
1724
1728
1725 Initialize a new repository in the given directory. If the given
1729 Initialize a new repository in the given directory. If the given
1726 directory does not exist, it is created.
1730 directory does not exist, it is created.
1727
1731
1728 If no directory is given, the current directory is used.
1732 If no directory is given, the current directory is used.
1729
1733
1730 It is possible to specify an ssh:// URL as the destination.
1734 It is possible to specify an ssh:// URL as the destination.
1731 Look at the help text for the pull command for important details
1735 Look at the help text for the pull command for important details
1732 about ssh:// URLs.
1736 about ssh:// URLs.
1733 """
1737 """
1734 setremoteconfig(ui, opts)
1738 setremoteconfig(ui, opts)
1735 hg.repository(ui, dest, create=1)
1739 hg.repository(ui, dest, create=1)
1736
1740
1737 def locate(ui, repo, *pats, **opts):
1741 def locate(ui, repo, *pats, **opts):
1738 """locate files matching specific patterns
1742 """locate files matching specific patterns
1739
1743
1740 Print all files under Mercurial control whose names match the
1744 Print all files under Mercurial control whose names match the
1741 given patterns.
1745 given patterns.
1742
1746
1743 This command searches the current directory and its
1747 This command searches the current directory and its
1744 subdirectories. To search an entire repository, move to the root
1748 subdirectories. To search an entire repository, move to the root
1745 of the repository.
1749 of the repository.
1746
1750
1747 If no patterns are given to match, this command prints all file
1751 If no patterns are given to match, this command prints all file
1748 names.
1752 names.
1749
1753
1750 If you want to feed the output of this command into the "xargs"
1754 If you want to feed the output of this command into the "xargs"
1751 command, use the "-0" option to both this command and "xargs".
1755 command, use the "-0" option to both this command and "xargs".
1752 This will avoid the problem of "xargs" treating single filenames
1756 This will avoid the problem of "xargs" treating single filenames
1753 that contain white space as multiple filenames.
1757 that contain white space as multiple filenames.
1754 """
1758 """
1755 end = opts['print0'] and '\0' or '\n'
1759 end = opts['print0'] and '\0' or '\n'
1756 rev = opts['rev']
1760 rev = opts['rev']
1757 if rev:
1761 if rev:
1758 node = repo.lookup(rev)
1762 node = repo.lookup(rev)
1759 else:
1763 else:
1760 node = None
1764 node = None
1761
1765
1762 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1766 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1763 head='(?:.*/|)'):
1767 head='(?:.*/|)'):
1764 if not node and repo.dirstate.state(abs) == '?':
1768 if not node and repo.dirstate.state(abs) == '?':
1765 continue
1769 continue
1766 if opts['fullpath']:
1770 if opts['fullpath']:
1767 ui.write(os.path.join(repo.root, abs), end)
1771 ui.write(os.path.join(repo.root, abs), end)
1768 else:
1772 else:
1769 ui.write(((pats and rel) or abs), end)
1773 ui.write(((pats and rel) or abs), end)
1770
1774
1771 def log(ui, repo, *pats, **opts):
1775 def log(ui, repo, *pats, **opts):
1772 """show revision history of entire repository or files
1776 """show revision history of entire repository or files
1773
1777
1774 Print the revision history of the specified files or the entire
1778 Print the revision history of the specified files or the entire
1775 project.
1779 project.
1776
1780
1777 File history is shown without following rename or copy history of
1781 File history is shown without following rename or copy history of
1778 files. Use -f/--follow with a file name to follow history across
1782 files. Use -f/--follow with a file name to follow history across
1779 renames and copies. --follow without a file name will only show
1783 renames and copies. --follow without a file name will only show
1780 ancestors or descendants of the starting revision. --follow-first
1784 ancestors or descendants of the starting revision. --follow-first
1781 only follows the first parent of merge revisions.
1785 only follows the first parent of merge revisions.
1782
1786
1783 If no revision range is specified, the default is tip:0 unless
1787 If no revision range is specified, the default is tip:0 unless
1784 --follow is set, in which case the working directory parent is
1788 --follow is set, in which case the working directory parent is
1785 used as the starting revision.
1789 used as the starting revision.
1786
1790
1787 By default this command outputs: changeset id and hash, tags,
1791 By default this command outputs: changeset id and hash, tags,
1788 non-trivial parents, user, date and time, and a summary for each
1792 non-trivial parents, user, date and time, and a summary for each
1789 commit. When the -v/--verbose switch is used, the list of changed
1793 commit. When the -v/--verbose switch is used, the list of changed
1790 files and full commit message is shown.
1794 files and full commit message is shown.
1791 """
1795 """
1792 class dui(object):
1796 class dui(object):
1793 # Implement and delegate some ui protocol. Save hunks of
1797 # Implement and delegate some ui protocol. Save hunks of
1794 # output for later display in the desired order.
1798 # output for later display in the desired order.
1795 def __init__(self, ui):
1799 def __init__(self, ui):
1796 self.ui = ui
1800 self.ui = ui
1797 self.hunk = {}
1801 self.hunk = {}
1798 self.header = {}
1802 self.header = {}
1799 def bump(self, rev):
1803 def bump(self, rev):
1800 self.rev = rev
1804 self.rev = rev
1801 self.hunk[rev] = []
1805 self.hunk[rev] = []
1802 self.header[rev] = []
1806 self.header[rev] = []
1803 def note(self, *args):
1807 def note(self, *args):
1804 if self.verbose:
1808 if self.verbose:
1805 self.write(*args)
1809 self.write(*args)
1806 def status(self, *args):
1810 def status(self, *args):
1807 if not self.quiet:
1811 if not self.quiet:
1808 self.write(*args)
1812 self.write(*args)
1809 def write(self, *args):
1813 def write(self, *args):
1810 self.hunk[self.rev].append(args)
1814 self.hunk[self.rev].append(args)
1811 def write_header(self, *args):
1815 def write_header(self, *args):
1812 self.header[self.rev].append(args)
1816 self.header[self.rev].append(args)
1813 def debug(self, *args):
1817 def debug(self, *args):
1814 if self.debugflag:
1818 if self.debugflag:
1815 self.write(*args)
1819 self.write(*args)
1816 def __getattr__(self, key):
1820 def __getattr__(self, key):
1817 return getattr(self.ui, key)
1821 return getattr(self.ui, key)
1818
1822
1819 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1823 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1820
1824
1821 if opts['limit']:
1825 if opts['limit']:
1822 try:
1826 try:
1823 limit = int(opts['limit'])
1827 limit = int(opts['limit'])
1824 except ValueError:
1828 except ValueError:
1825 raise util.Abort(_('limit must be a positive integer'))
1829 raise util.Abort(_('limit must be a positive integer'))
1826 if limit <= 0: raise util.Abort(_('limit must be positive'))
1830 if limit <= 0: raise util.Abort(_('limit must be positive'))
1827 else:
1831 else:
1828 limit = sys.maxint
1832 limit = sys.maxint
1829 count = 0
1833 count = 0
1830
1834
1831 if opts['copies'] and opts['rev']:
1835 if opts['copies'] and opts['rev']:
1832 endrev = max([int(i)
1836 endrev = max([int(i)
1833 for i in cmdutil.revrange(ui, repo, opts['rev'])]) + 1
1837 for i in cmdutil.revrange(ui, repo, opts['rev'])]) + 1
1834 else:
1838 else:
1835 endrev = repo.changelog.count()
1839 endrev = repo.changelog.count()
1836 rcache = {}
1840 rcache = {}
1837 ncache = {}
1841 ncache = {}
1838 dcache = []
1842 dcache = []
1839 def getrenamed(fn, rev, man):
1843 def getrenamed(fn, rev, man):
1840 '''looks up all renames for a file (up to endrev) the first
1844 '''looks up all renames for a file (up to endrev) the first
1841 time the file is given. It indexes on the changerev and only
1845 time the file is given. It indexes on the changerev and only
1842 parses the manifest if linkrev != changerev.
1846 parses the manifest if linkrev != changerev.
1843 Returns rename info for fn at changerev rev.'''
1847 Returns rename info for fn at changerev rev.'''
1844 if fn not in rcache:
1848 if fn not in rcache:
1845 rcache[fn] = {}
1849 rcache[fn] = {}
1846 ncache[fn] = {}
1850 ncache[fn] = {}
1847 fl = repo.file(fn)
1851 fl = repo.file(fn)
1848 for i in xrange(fl.count()):
1852 for i in xrange(fl.count()):
1849 node = fl.node(i)
1853 node = fl.node(i)
1850 lr = fl.linkrev(node)
1854 lr = fl.linkrev(node)
1851 renamed = fl.renamed(node)
1855 renamed = fl.renamed(node)
1852 rcache[fn][lr] = renamed
1856 rcache[fn][lr] = renamed
1853 if renamed:
1857 if renamed:
1854 ncache[fn][node] = renamed
1858 ncache[fn][node] = renamed
1855 if lr >= endrev:
1859 if lr >= endrev:
1856 break
1860 break
1857 if rev in rcache[fn]:
1861 if rev in rcache[fn]:
1858 return rcache[fn][rev]
1862 return rcache[fn][rev]
1859 mr = repo.manifest.rev(man)
1863 mr = repo.manifest.rev(man)
1860 if repo.manifest.parentrevs(mr) != (mr - 1, -1):
1864 if repo.manifest.parentrevs(mr) != (mr - 1, -1):
1861 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1865 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1862 if not dcache or dcache[0] != man:
1866 if not dcache or dcache[0] != man:
1863 dcache[:] = [man, repo.manifest.readdelta(man)]
1867 dcache[:] = [man, repo.manifest.readdelta(man)]
1864 if fn in dcache[1]:
1868 if fn in dcache[1]:
1865 return ncache[fn].get(dcache[1][fn])
1869 return ncache[fn].get(dcache[1][fn])
1866 return None
1870 return None
1867
1871
1868 displayer = show_changeset(ui, repo, opts)
1872 displayer = show_changeset(ui, repo, opts)
1869 for st, rev, fns in changeiter:
1873 for st, rev, fns in changeiter:
1870 if st == 'window':
1874 if st == 'window':
1871 du = dui(ui)
1875 du = dui(ui)
1872 displayer.ui = du
1876 displayer.ui = du
1873 elif st == 'add':
1877 elif st == 'add':
1874 du.bump(rev)
1878 du.bump(rev)
1875 changenode = repo.changelog.node(rev)
1879 changenode = repo.changelog.node(rev)
1876 parents = [p for p in repo.changelog.parents(changenode)
1880 parents = [p for p in repo.changelog.parents(changenode)
1877 if p != nullid]
1881 if p != nullid]
1878 if opts['no_merges'] and len(parents) == 2:
1882 if opts['no_merges'] and len(parents) == 2:
1879 continue
1883 continue
1880 if opts['only_merges'] and len(parents) != 2:
1884 if opts['only_merges'] and len(parents) != 2:
1881 continue
1885 continue
1882
1886
1883 if opts['keyword']:
1887 if opts['keyword']:
1884 changes = getchange(rev)
1888 changes = getchange(rev)
1885 miss = 0
1889 miss = 0
1886 for k in [kw.lower() for kw in opts['keyword']]:
1890 for k in [kw.lower() for kw in opts['keyword']]:
1887 if not (k in changes[1].lower() or
1891 if not (k in changes[1].lower() or
1888 k in changes[4].lower() or
1892 k in changes[4].lower() or
1889 k in " ".join(changes[3][:20]).lower()):
1893 k in " ".join(changes[3][:20]).lower()):
1890 miss = 1
1894 miss = 1
1891 break
1895 break
1892 if miss:
1896 if miss:
1893 continue
1897 continue
1894
1898
1895 br = None
1899 br = None
1896 if opts['branches']:
1900 if opts['branches']:
1897 br = repo.branchlookup([repo.changelog.node(rev)])
1901 br = repo.branchlookup([repo.changelog.node(rev)])
1898
1902
1899 copies = []
1903 copies = []
1900 if opts.get('copies') and rev:
1904 if opts.get('copies') and rev:
1901 mf = getchange(rev)[0]
1905 mf = getchange(rev)[0]
1902 for fn in getchange(rev)[3]:
1906 for fn in getchange(rev)[3]:
1903 rename = getrenamed(fn, rev, mf)
1907 rename = getrenamed(fn, rev, mf)
1904 if rename:
1908 if rename:
1905 copies.append((fn, rename[0]))
1909 copies.append((fn, rename[0]))
1906 displayer.show(rev, brinfo=br, copies=copies)
1910 displayer.show(rev, brinfo=br, copies=copies)
1907 if opts['patch']:
1911 if opts['patch']:
1908 prev = (parents and parents[0]) or nullid
1912 prev = (parents and parents[0]) or nullid
1909 patch.diff(repo, prev, changenode, match=matchfn, fp=du)
1913 patch.diff(repo, prev, changenode, match=matchfn, fp=du)
1910 du.write("\n\n")
1914 du.write("\n\n")
1911 elif st == 'iter':
1915 elif st == 'iter':
1912 if count == limit: break
1916 if count == limit: break
1913 if du.header[rev]:
1917 if du.header[rev]:
1914 for args in du.header[rev]:
1918 for args in du.header[rev]:
1915 ui.write_header(*args)
1919 ui.write_header(*args)
1916 if du.hunk[rev]:
1920 if du.hunk[rev]:
1917 count += 1
1921 count += 1
1918 for args in du.hunk[rev]:
1922 for args in du.hunk[rev]:
1919 ui.write(*args)
1923 ui.write(*args)
1920
1924
1921 def manifest(ui, repo, rev=None):
1925 def manifest(ui, repo, rev=None):
1922 """output the latest or given revision of the project manifest
1926 """output the latest or given revision of the project manifest
1923
1927
1924 Print a list of version controlled files for the given revision.
1928 Print a list of version controlled files for the given revision.
1925
1929
1926 The manifest is the list of files being version controlled. If no revision
1930 The manifest is the list of files being version controlled. If no revision
1927 is given then the tip is used.
1931 is given then the tip is used.
1928 """
1932 """
1929 if rev:
1933 if rev:
1930 try:
1934 try:
1931 # assume all revision numbers are for changesets
1935 # assume all revision numbers are for changesets
1932 n = repo.lookup(rev)
1936 n = repo.lookup(rev)
1933 change = repo.changelog.read(n)
1937 change = repo.changelog.read(n)
1934 n = change[0]
1938 n = change[0]
1935 except hg.RepoError:
1939 except hg.RepoError:
1936 n = repo.manifest.lookup(rev)
1940 n = repo.manifest.lookup(rev)
1937 else:
1941 else:
1938 n = repo.manifest.tip()
1942 n = repo.manifest.tip()
1939 m = repo.manifest.read(n)
1943 m = repo.manifest.read(n)
1940 files = m.keys()
1944 files = m.keys()
1941 files.sort()
1945 files.sort()
1942
1946
1943 for f in files:
1947 for f in files:
1944 ui.write("%40s %3s %s\n" % (hex(m[f]),
1948 ui.write("%40s %3s %s\n" % (hex(m[f]),
1945 m.execf(f) and "755" or "644", f))
1949 m.execf(f) and "755" or "644", f))
1946
1950
1947 def merge(ui, repo, node=None, force=None, branch=None):
1951 def merge(ui, repo, node=None, force=None, branch=None):
1948 """Merge working directory with another revision
1952 """Merge working directory with another revision
1949
1953
1950 Merge the contents of the current working directory and the
1954 Merge the contents of the current working directory and the
1951 requested revision. Files that changed between either parent are
1955 requested revision. Files that changed between either parent are
1952 marked as changed for the next commit and a commit must be
1956 marked as changed for the next commit and a commit must be
1953 performed before any further updates are allowed.
1957 performed before any further updates are allowed.
1954
1958
1955 If no revision is specified, the working directory's parent is a
1959 If no revision is specified, the working directory's parent is a
1956 head revision, and the repository contains exactly one other head,
1960 head revision, and the repository contains exactly one other head,
1957 the other head is merged with by default. Otherwise, an explicit
1961 the other head is merged with by default. Otherwise, an explicit
1958 revision to merge with must be provided.
1962 revision to merge with must be provided.
1959 """
1963 """
1960
1964
1961 if node or branch:
1965 if node or branch:
1962 node = _lookup(repo, node, branch)
1966 node = _lookup(repo, node, branch)
1963 else:
1967 else:
1964 heads = repo.heads()
1968 heads = repo.heads()
1965 if len(heads) > 2:
1969 if len(heads) > 2:
1966 raise util.Abort(_('repo has %d heads - '
1970 raise util.Abort(_('repo has %d heads - '
1967 'please merge with an explicit rev') %
1971 'please merge with an explicit rev') %
1968 len(heads))
1972 len(heads))
1969 if len(heads) == 1:
1973 if len(heads) == 1:
1970 raise util.Abort(_('there is nothing to merge - '
1974 raise util.Abort(_('there is nothing to merge - '
1971 'use "hg update" instead'))
1975 'use "hg update" instead'))
1972 parent = repo.dirstate.parents()[0]
1976 parent = repo.dirstate.parents()[0]
1973 if parent not in heads:
1977 if parent not in heads:
1974 raise util.Abort(_('working dir not at a head rev - '
1978 raise util.Abort(_('working dir not at a head rev - '
1975 'use "hg update" or merge with an explicit rev'))
1979 'use "hg update" or merge with an explicit rev'))
1976 node = parent == heads[0] and heads[-1] or heads[0]
1980 node = parent == heads[0] and heads[-1] or heads[0]
1977 return hg.merge(repo, node, force=force)
1981 return hg.merge(repo, node, force=force)
1978
1982
1979 def outgoing(ui, repo, dest=None, **opts):
1983 def outgoing(ui, repo, dest=None, **opts):
1980 """show changesets not found in destination
1984 """show changesets not found in destination
1981
1985
1982 Show changesets not found in the specified destination repository or
1986 Show changesets not found in the specified destination repository or
1983 the default push location. These are the changesets that would be pushed
1987 the default push location. These are the changesets that would be pushed
1984 if a push was requested.
1988 if a push was requested.
1985
1989
1986 See pull for valid destination format details.
1990 See pull for valid destination format details.
1987 """
1991 """
1988 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1992 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1989 setremoteconfig(ui, opts)
1993 setremoteconfig(ui, opts)
1990 revs = None
1994 revs = None
1991 if opts['rev']:
1995 if opts['rev']:
1992 revs = [repo.lookup(rev) for rev in opts['rev']]
1996 revs = [repo.lookup(rev) for rev in opts['rev']]
1993
1997
1994 other = hg.repository(ui, dest)
1998 other = hg.repository(ui, dest)
1995 o = repo.findoutgoing(other, force=opts['force'])
1999 o = repo.findoutgoing(other, force=opts['force'])
1996 if not o:
2000 if not o:
1997 ui.status(_("no changes found\n"))
2001 ui.status(_("no changes found\n"))
1998 return
2002 return
1999 o = repo.changelog.nodesbetween(o, revs)[0]
2003 o = repo.changelog.nodesbetween(o, revs)[0]
2000 if opts['newest_first']:
2004 if opts['newest_first']:
2001 o.reverse()
2005 o.reverse()
2002 displayer = show_changeset(ui, repo, opts)
2006 displayer = show_changeset(ui, repo, opts)
2003 for n in o:
2007 for n in o:
2004 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2008 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2005 if opts['no_merges'] and len(parents) == 2:
2009 if opts['no_merges'] and len(parents) == 2:
2006 continue
2010 continue
2007 displayer.show(changenode=n)
2011 displayer.show(changenode=n)
2008 if opts['patch']:
2012 if opts['patch']:
2009 prev = (parents and parents[0]) or nullid
2013 prev = (parents and parents[0]) or nullid
2010 patch.diff(repo, prev, n)
2014 patch.diff(repo, prev, n)
2011 ui.write("\n")
2015 ui.write("\n")
2012
2016
2013 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
2017 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
2014 """show the parents of the working dir or revision
2018 """show the parents of the working dir or revision
2015
2019
2016 Print the working directory's parent revisions.
2020 Print the working directory's parent revisions.
2017 """
2021 """
2018 # legacy
2022 # legacy
2019 if file_ and not rev:
2023 if file_ and not rev:
2020 try:
2024 try:
2021 rev = repo.lookup(file_)
2025 rev = repo.lookup(file_)
2022 file_ = None
2026 file_ = None
2023 except hg.RepoError:
2027 except hg.RepoError:
2024 pass
2028 pass
2025 else:
2029 else:
2026 ui.warn(_("'hg parent REV' is deprecated, "
2030 ui.warn(_("'hg parent REV' is deprecated, "
2027 "please use 'hg parents -r REV instead\n"))
2031 "please use 'hg parents -r REV instead\n"))
2028
2032
2029 if rev:
2033 if rev:
2030 if file_:
2034 if file_:
2031 ctx = repo.filectx(file_, changeid=rev)
2035 ctx = repo.filectx(file_, changeid=rev)
2032 else:
2036 else:
2033 ctx = repo.changectx(rev)
2037 ctx = repo.changectx(rev)
2034 p = [cp.node() for cp in ctx.parents()]
2038 p = [cp.node() for cp in ctx.parents()]
2035 else:
2039 else:
2036 p = repo.dirstate.parents()
2040 p = repo.dirstate.parents()
2037
2041
2038 br = None
2042 br = None
2039 if branches is not None:
2043 if branches is not None:
2040 br = repo.branchlookup(p)
2044 br = repo.branchlookup(p)
2041 displayer = show_changeset(ui, repo, opts)
2045 displayer = show_changeset(ui, repo, opts)
2042 for n in p:
2046 for n in p:
2043 if n != nullid:
2047 if n != nullid:
2044 displayer.show(changenode=n, brinfo=br)
2048 displayer.show(changenode=n, brinfo=br)
2045
2049
2046 def paths(ui, repo, search=None):
2050 def paths(ui, repo, search=None):
2047 """show definition of symbolic path names
2051 """show definition of symbolic path names
2048
2052
2049 Show definition of symbolic path name NAME. If no name is given, show
2053 Show definition of symbolic path name NAME. If no name is given, show
2050 definition of available names.
2054 definition of available names.
2051
2055
2052 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2056 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2053 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2057 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2054 """
2058 """
2055 if search:
2059 if search:
2056 for name, path in ui.configitems("paths"):
2060 for name, path in ui.configitems("paths"):
2057 if name == search:
2061 if name == search:
2058 ui.write("%s\n" % path)
2062 ui.write("%s\n" % path)
2059 return
2063 return
2060 ui.warn(_("not found!\n"))
2064 ui.warn(_("not found!\n"))
2061 return 1
2065 return 1
2062 else:
2066 else:
2063 for name, path in ui.configitems("paths"):
2067 for name, path in ui.configitems("paths"):
2064 ui.write("%s = %s\n" % (name, path))
2068 ui.write("%s = %s\n" % (name, path))
2065
2069
2066 def postincoming(ui, repo, modheads, optupdate):
2070 def postincoming(ui, repo, modheads, optupdate):
2067 if modheads == 0:
2071 if modheads == 0:
2068 return
2072 return
2069 if optupdate:
2073 if optupdate:
2070 if modheads == 1:
2074 if modheads == 1:
2071 return hg.update(repo, repo.changelog.tip()) # update
2075 return hg.update(repo, repo.changelog.tip()) # update
2072 else:
2076 else:
2073 ui.status(_("not updating, since new heads added\n"))
2077 ui.status(_("not updating, since new heads added\n"))
2074 if modheads > 1:
2078 if modheads > 1:
2075 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2079 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2076 else:
2080 else:
2077 ui.status(_("(run 'hg update' to get a working copy)\n"))
2081 ui.status(_("(run 'hg update' to get a working copy)\n"))
2078
2082
2079 def pull(ui, repo, source="default", **opts):
2083 def pull(ui, repo, source="default", **opts):
2080 """pull changes from the specified source
2084 """pull changes from the specified source
2081
2085
2082 Pull changes from a remote repository to a local one.
2086 Pull changes from a remote repository to a local one.
2083
2087
2084 This finds all changes from the repository at the specified path
2088 This finds all changes from the repository at the specified path
2085 or URL and adds them to the local repository. By default, this
2089 or URL and adds them to the local repository. By default, this
2086 does not update the copy of the project in the working directory.
2090 does not update the copy of the project in the working directory.
2087
2091
2088 Valid URLs are of the form:
2092 Valid URLs are of the form:
2089
2093
2090 local/filesystem/path
2094 local/filesystem/path
2091 http://[user@]host[:port]/[path]
2095 http://[user@]host[:port]/[path]
2092 https://[user@]host[:port]/[path]
2096 https://[user@]host[:port]/[path]
2093 ssh://[user@]host[:port]/[path]
2097 ssh://[user@]host[:port]/[path]
2094
2098
2095 Some notes about using SSH with Mercurial:
2099 Some notes about using SSH with Mercurial:
2096 - SSH requires an accessible shell account on the destination machine
2100 - SSH requires an accessible shell account on the destination machine
2097 and a copy of hg in the remote path or specified with as remotecmd.
2101 and a copy of hg in the remote path or specified with as remotecmd.
2098 - path is relative to the remote user's home directory by default.
2102 - path is relative to the remote user's home directory by default.
2099 Use an extra slash at the start of a path to specify an absolute path:
2103 Use an extra slash at the start of a path to specify an absolute path:
2100 ssh://example.com//tmp/repository
2104 ssh://example.com//tmp/repository
2101 - Mercurial doesn't use its own compression via SSH; the right thing
2105 - Mercurial doesn't use its own compression via SSH; the right thing
2102 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2106 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2103 Host *.mylocalnetwork.example.com
2107 Host *.mylocalnetwork.example.com
2104 Compression off
2108 Compression off
2105 Host *
2109 Host *
2106 Compression on
2110 Compression on
2107 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2111 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2108 with the --ssh command line option.
2112 with the --ssh command line option.
2109 """
2113 """
2110 source = ui.expandpath(source)
2114 source = ui.expandpath(source)
2111 setremoteconfig(ui, opts)
2115 setremoteconfig(ui, opts)
2112
2116
2113 other = hg.repository(ui, source)
2117 other = hg.repository(ui, source)
2114 ui.status(_('pulling from %s\n') % (source))
2118 ui.status(_('pulling from %s\n') % (source))
2115 revs = None
2119 revs = None
2116 if opts['rev']:
2120 if opts['rev']:
2117 if 'lookup' in other.capabilities:
2121 if 'lookup' in other.capabilities:
2118 revs = [other.lookup(rev) for rev in opts['rev']]
2122 revs = [other.lookup(rev) for rev in opts['rev']]
2119 else:
2123 else:
2120 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
2124 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
2121 raise util.Abort(error)
2125 raise util.Abort(error)
2122 modheads = repo.pull(other, heads=revs, force=opts['force'])
2126 modheads = repo.pull(other, heads=revs, force=opts['force'])
2123 return postincoming(ui, repo, modheads, opts['update'])
2127 return postincoming(ui, repo, modheads, opts['update'])
2124
2128
2125 def push(ui, repo, dest=None, **opts):
2129 def push(ui, repo, dest=None, **opts):
2126 """push changes to the specified destination
2130 """push changes to the specified destination
2127
2131
2128 Push changes from the local repository to the given destination.
2132 Push changes from the local repository to the given destination.
2129
2133
2130 This is the symmetrical operation for pull. It helps to move
2134 This is the symmetrical operation for pull. It helps to move
2131 changes from the current repository to a different one. If the
2135 changes from the current repository to a different one. If the
2132 destination is local this is identical to a pull in that directory
2136 destination is local this is identical to a pull in that directory
2133 from the current one.
2137 from the current one.
2134
2138
2135 By default, push will refuse to run if it detects the result would
2139 By default, push will refuse to run if it detects the result would
2136 increase the number of remote heads. This generally indicates the
2140 increase the number of remote heads. This generally indicates the
2137 the client has forgotten to sync and merge before pushing.
2141 the client has forgotten to sync and merge before pushing.
2138
2142
2139 Valid URLs are of the form:
2143 Valid URLs are of the form:
2140
2144
2141 local/filesystem/path
2145 local/filesystem/path
2142 ssh://[user@]host[:port]/[path]
2146 ssh://[user@]host[:port]/[path]
2143
2147
2144 Look at the help text for the pull command for important details
2148 Look at the help text for the pull command for important details
2145 about ssh:// URLs.
2149 about ssh:// URLs.
2146
2150
2147 Pushing to http:// and https:// URLs is possible, too, if this
2151 Pushing to http:// and https:// URLs is possible, too, if this
2148 feature is enabled on the remote Mercurial server.
2152 feature is enabled on the remote Mercurial server.
2149 """
2153 """
2150 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2154 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2151 setremoteconfig(ui, opts)
2155 setremoteconfig(ui, opts)
2152
2156
2153 other = hg.repository(ui, dest)
2157 other = hg.repository(ui, dest)
2154 ui.status('pushing to %s\n' % (dest))
2158 ui.status('pushing to %s\n' % (dest))
2155 revs = None
2159 revs = None
2156 if opts['rev']:
2160 if opts['rev']:
2157 revs = [repo.lookup(rev) for rev in opts['rev']]
2161 revs = [repo.lookup(rev) for rev in opts['rev']]
2158 r = repo.push(other, opts['force'], revs=revs)
2162 r = repo.push(other, opts['force'], revs=revs)
2159 return r == 0
2163 return r == 0
2160
2164
2161 def rawcommit(ui, repo, *flist, **rc):
2165 def rawcommit(ui, repo, *flist, **rc):
2162 """raw commit interface (DEPRECATED)
2166 """raw commit interface (DEPRECATED)
2163
2167
2164 (DEPRECATED)
2168 (DEPRECATED)
2165 Lowlevel commit, for use in helper scripts.
2169 Lowlevel commit, for use in helper scripts.
2166
2170
2167 This command is not intended to be used by normal users, as it is
2171 This command is not intended to be used by normal users, as it is
2168 primarily useful for importing from other SCMs.
2172 primarily useful for importing from other SCMs.
2169
2173
2170 This command is now deprecated and will be removed in a future
2174 This command is now deprecated and will be removed in a future
2171 release, please use debugsetparents and commit instead.
2175 release, please use debugsetparents and commit instead.
2172 """
2176 """
2173
2177
2174 ui.warn(_("(the rawcommit command is deprecated)\n"))
2178 ui.warn(_("(the rawcommit command is deprecated)\n"))
2175
2179
2176 message = rc['message']
2180 message = rc['message']
2177 if not message and rc['logfile']:
2181 if not message and rc['logfile']:
2178 try:
2182 try:
2179 message = open(rc['logfile']).read()
2183 message = open(rc['logfile']).read()
2180 except IOError:
2184 except IOError:
2181 pass
2185 pass
2182 if not message and not rc['logfile']:
2186 if not message and not rc['logfile']:
2183 raise util.Abort(_("missing commit message"))
2187 raise util.Abort(_("missing commit message"))
2184
2188
2185 files = relpath(repo, list(flist))
2189 files = relpath(repo, list(flist))
2186 if rc['files']:
2190 if rc['files']:
2187 files += open(rc['files']).read().splitlines()
2191 files += open(rc['files']).read().splitlines()
2188
2192
2189 rc['parent'] = map(repo.lookup, rc['parent'])
2193 rc['parent'] = map(repo.lookup, rc['parent'])
2190
2194
2191 try:
2195 try:
2192 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2196 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2193 except ValueError, inst:
2197 except ValueError, inst:
2194 raise util.Abort(str(inst))
2198 raise util.Abort(str(inst))
2195
2199
2196 def recover(ui, repo):
2200 def recover(ui, repo):
2197 """roll back an interrupted transaction
2201 """roll back an interrupted transaction
2198
2202
2199 Recover from an interrupted commit or pull.
2203 Recover from an interrupted commit or pull.
2200
2204
2201 This command tries to fix the repository status after an interrupted
2205 This command tries to fix the repository status after an interrupted
2202 operation. It should only be necessary when Mercurial suggests it.
2206 operation. It should only be necessary when Mercurial suggests it.
2203 """
2207 """
2204 if repo.recover():
2208 if repo.recover():
2205 return hg.verify(repo)
2209 return hg.verify(repo)
2206 return 1
2210 return 1
2207
2211
2208 def remove(ui, repo, *pats, **opts):
2212 def remove(ui, repo, *pats, **opts):
2209 """remove the specified files on the next commit
2213 """remove the specified files on the next commit
2210
2214
2211 Schedule the indicated files for removal from the repository.
2215 Schedule the indicated files for removal from the repository.
2212
2216
2213 This command schedules the files to be removed at the next commit.
2217 This command schedules the files to be removed at the next commit.
2214 This only removes files from the current branch, not from the
2218 This only removes files from the current branch, not from the
2215 entire project history. If the files still exist in the working
2219 entire project history. If the files still exist in the working
2216 directory, they will be deleted from it. If invoked with --after,
2220 directory, they will be deleted from it. If invoked with --after,
2217 files that have been manually deleted are marked as removed.
2221 files that have been manually deleted are marked as removed.
2218
2222
2219 Modified files and added files are not removed by default. To
2223 Modified files and added files are not removed by default. To
2220 remove them, use the -f/--force option.
2224 remove them, use the -f/--force option.
2221 """
2225 """
2222 names = []
2226 names = []
2223 if not opts['after'] and not pats:
2227 if not opts['after'] and not pats:
2224 raise util.Abort(_('no files specified'))
2228 raise util.Abort(_('no files specified'))
2225 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2229 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2226 exact = dict.fromkeys(files)
2230 exact = dict.fromkeys(files)
2227 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2231 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2228 modified, added, removed, deleted, unknown = mardu
2232 modified, added, removed, deleted, unknown = mardu
2229 remove, forget = [], []
2233 remove, forget = [], []
2230 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2234 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2231 reason = None
2235 reason = None
2232 if abs not in deleted and opts['after']:
2236 if abs not in deleted and opts['after']:
2233 reason = _('is still present')
2237 reason = _('is still present')
2234 elif abs in modified and not opts['force']:
2238 elif abs in modified and not opts['force']:
2235 reason = _('is modified (use -f to force removal)')
2239 reason = _('is modified (use -f to force removal)')
2236 elif abs in added:
2240 elif abs in added:
2237 if opts['force']:
2241 if opts['force']:
2238 forget.append(abs)
2242 forget.append(abs)
2239 continue
2243 continue
2240 reason = _('has been marked for add (use -f to force removal)')
2244 reason = _('has been marked for add (use -f to force removal)')
2241 elif abs in unknown:
2245 elif abs in unknown:
2242 reason = _('is not managed')
2246 reason = _('is not managed')
2243 elif abs in removed:
2247 elif abs in removed:
2244 continue
2248 continue
2245 if reason:
2249 if reason:
2246 if exact:
2250 if exact:
2247 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2251 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2248 else:
2252 else:
2249 if ui.verbose or not exact:
2253 if ui.verbose or not exact:
2250 ui.status(_('removing %s\n') % rel)
2254 ui.status(_('removing %s\n') % rel)
2251 remove.append(abs)
2255 remove.append(abs)
2252 repo.forget(forget)
2256 repo.forget(forget)
2253 repo.remove(remove, unlink=not opts['after'])
2257 repo.remove(remove, unlink=not opts['after'])
2254
2258
2255 def rename(ui, repo, *pats, **opts):
2259 def rename(ui, repo, *pats, **opts):
2256 """rename files; equivalent of copy + remove
2260 """rename files; equivalent of copy + remove
2257
2261
2258 Mark dest as copies of sources; mark sources for deletion. If
2262 Mark dest as copies of sources; mark sources for deletion. If
2259 dest is a directory, copies are put in that directory. If dest is
2263 dest is a directory, copies are put in that directory. If dest is
2260 a file, there can only be one source.
2264 a file, there can only be one source.
2261
2265
2262 By default, this command copies the contents of files as they
2266 By default, this command copies the contents of files as they
2263 stand in the working directory. If invoked with --after, the
2267 stand in the working directory. If invoked with --after, the
2264 operation is recorded, but no copying is performed.
2268 operation is recorded, but no copying is performed.
2265
2269
2266 This command takes effect in the next commit.
2270 This command takes effect in the next commit.
2267
2271
2268 NOTE: This command should be treated as experimental. While it
2272 NOTE: This command should be treated as experimental. While it
2269 should properly record rename files, this information is not yet
2273 should properly record rename files, this information is not yet
2270 fully used by merge, nor fully reported by log.
2274 fully used by merge, nor fully reported by log.
2271 """
2275 """
2272 wlock = repo.wlock(0)
2276 wlock = repo.wlock(0)
2273 errs, copied = docopy(ui, repo, pats, opts, wlock)
2277 errs, copied = docopy(ui, repo, pats, opts, wlock)
2274 names = []
2278 names = []
2275 for abs, rel, exact in copied:
2279 for abs, rel, exact in copied:
2276 if ui.verbose or not exact:
2280 if ui.verbose or not exact:
2277 ui.status(_('removing %s\n') % rel)
2281 ui.status(_('removing %s\n') % rel)
2278 names.append(abs)
2282 names.append(abs)
2279 if not opts.get('dry_run'):
2283 if not opts.get('dry_run'):
2280 repo.remove(names, True, wlock)
2284 repo.remove(names, True, wlock)
2281 return errs
2285 return errs
2282
2286
2283 def revert(ui, repo, *pats, **opts):
2287 def revert(ui, repo, *pats, **opts):
2284 """revert files or dirs to their states as of some revision
2288 """revert files or dirs to their states as of some revision
2285
2289
2286 With no revision specified, revert the named files or directories
2290 With no revision specified, revert the named files or directories
2287 to the contents they had in the parent of the working directory.
2291 to the contents they had in the parent of the working directory.
2288 This restores the contents of the affected files to an unmodified
2292 This restores the contents of the affected files to an unmodified
2289 state. If the working directory has two parents, you must
2293 state. If the working directory has two parents, you must
2290 explicitly specify the revision to revert to.
2294 explicitly specify the revision to revert to.
2291
2295
2292 Modified files are saved with a .orig suffix before reverting.
2296 Modified files are saved with a .orig suffix before reverting.
2293 To disable these backups, use --no-backup.
2297 To disable these backups, use --no-backup.
2294
2298
2295 Using the -r option, revert the given files or directories to their
2299 Using the -r option, revert the given files or directories to their
2296 contents as of a specific revision. This can be helpful to "roll
2300 contents as of a specific revision. This can be helpful to "roll
2297 back" some or all of a change that should not have been committed.
2301 back" some or all of a change that should not have been committed.
2298
2302
2299 Revert modifies the working directory. It does not commit any
2303 Revert modifies the working directory. It does not commit any
2300 changes, or change the parent of the working directory. If you
2304 changes, or change the parent of the working directory. If you
2301 revert to a revision other than the parent of the working
2305 revert to a revision other than the parent of the working
2302 directory, the reverted files will thus appear modified
2306 directory, the reverted files will thus appear modified
2303 afterwards.
2307 afterwards.
2304
2308
2305 If a file has been deleted, it is recreated. If the executable
2309 If a file has been deleted, it is recreated. If the executable
2306 mode of a file was changed, it is reset.
2310 mode of a file was changed, it is reset.
2307
2311
2308 If names are given, all files matching the names are reverted.
2312 If names are given, all files matching the names are reverted.
2309
2313
2310 If no arguments are given, no files are reverted.
2314 If no arguments are given, no files are reverted.
2311 """
2315 """
2312
2316
2313 if not pats and not opts['all']:
2317 if not pats and not opts['all']:
2314 raise util.Abort(_('no files or directories specified; '
2318 raise util.Abort(_('no files or directories specified; '
2315 'use --all to revert the whole repo'))
2319 'use --all to revert the whole repo'))
2316
2320
2317 parent, p2 = repo.dirstate.parents()
2321 parent, p2 = repo.dirstate.parents()
2318 if not opts['rev'] and p2 != nullid:
2322 if not opts['rev'] and p2 != nullid:
2319 raise util.Abort(_('uncommitted merge - please provide a '
2323 raise util.Abort(_('uncommitted merge - please provide a '
2320 'specific revision'))
2324 'specific revision'))
2321 node = repo.changectx(opts['rev']).node()
2325 node = repo.changectx(opts['rev']).node()
2322 mf = repo.manifest.read(repo.changelog.read(node)[0])
2326 mf = repo.manifest.read(repo.changelog.read(node)[0])
2323 if node == parent:
2327 if node == parent:
2324 pmf = mf
2328 pmf = mf
2325 else:
2329 else:
2326 pmf = None
2330 pmf = None
2327
2331
2328 wlock = repo.wlock()
2332 wlock = repo.wlock()
2329
2333
2330 # need all matching names in dirstate and manifest of target rev,
2334 # need all matching names in dirstate and manifest of target rev,
2331 # so have to walk both. do not print errors if files exist in one
2335 # so have to walk both. do not print errors if files exist in one
2332 # but not other.
2336 # but not other.
2333
2337
2334 names = {}
2338 names = {}
2335 target_only = {}
2339 target_only = {}
2336
2340
2337 # walk dirstate.
2341 # walk dirstate.
2338
2342
2339 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2343 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2340 badmatch=mf.has_key):
2344 badmatch=mf.has_key):
2341 names[abs] = (rel, exact)
2345 names[abs] = (rel, exact)
2342 if src == 'b':
2346 if src == 'b':
2343 target_only[abs] = True
2347 target_only[abs] = True
2344
2348
2345 # walk target manifest.
2349 # walk target manifest.
2346
2350
2347 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2351 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2348 badmatch=names.has_key):
2352 badmatch=names.has_key):
2349 if abs in names: continue
2353 if abs in names: continue
2350 names[abs] = (rel, exact)
2354 names[abs] = (rel, exact)
2351 target_only[abs] = True
2355 target_only[abs] = True
2352
2356
2353 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2357 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2354 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2358 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2355
2359
2356 revert = ([], _('reverting %s\n'))
2360 revert = ([], _('reverting %s\n'))
2357 add = ([], _('adding %s\n'))
2361 add = ([], _('adding %s\n'))
2358 remove = ([], _('removing %s\n'))
2362 remove = ([], _('removing %s\n'))
2359 forget = ([], _('forgetting %s\n'))
2363 forget = ([], _('forgetting %s\n'))
2360 undelete = ([], _('undeleting %s\n'))
2364 undelete = ([], _('undeleting %s\n'))
2361 update = {}
2365 update = {}
2362
2366
2363 disptable = (
2367 disptable = (
2364 # dispatch table:
2368 # dispatch table:
2365 # file state
2369 # file state
2366 # action if in target manifest
2370 # action if in target manifest
2367 # action if not in target manifest
2371 # action if not in target manifest
2368 # make backup if in target manifest
2372 # make backup if in target manifest
2369 # make backup if not in target manifest
2373 # make backup if not in target manifest
2370 (modified, revert, remove, True, True),
2374 (modified, revert, remove, True, True),
2371 (added, revert, forget, True, False),
2375 (added, revert, forget, True, False),
2372 (removed, undelete, None, False, False),
2376 (removed, undelete, None, False, False),
2373 (deleted, revert, remove, False, False),
2377 (deleted, revert, remove, False, False),
2374 (unknown, add, None, True, False),
2378 (unknown, add, None, True, False),
2375 (target_only, add, None, False, False),
2379 (target_only, add, None, False, False),
2376 )
2380 )
2377
2381
2378 entries = names.items()
2382 entries = names.items()
2379 entries.sort()
2383 entries.sort()
2380
2384
2381 for abs, (rel, exact) in entries:
2385 for abs, (rel, exact) in entries:
2382 mfentry = mf.get(abs)
2386 mfentry = mf.get(abs)
2383 def handle(xlist, dobackup):
2387 def handle(xlist, dobackup):
2384 xlist[0].append(abs)
2388 xlist[0].append(abs)
2385 update[abs] = 1
2389 update[abs] = 1
2386 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2390 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2387 bakname = "%s.orig" % rel
2391 bakname = "%s.orig" % rel
2388 ui.note(_('saving current version of %s as %s\n') %
2392 ui.note(_('saving current version of %s as %s\n') %
2389 (rel, bakname))
2393 (rel, bakname))
2390 if not opts.get('dry_run'):
2394 if not opts.get('dry_run'):
2391 shutil.copyfile(rel, bakname)
2395 shutil.copyfile(rel, bakname)
2392 shutil.copymode(rel, bakname)
2396 shutil.copymode(rel, bakname)
2393 if ui.verbose or not exact:
2397 if ui.verbose or not exact:
2394 ui.status(xlist[1] % rel)
2398 ui.status(xlist[1] % rel)
2395 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2399 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2396 if abs not in table: continue
2400 if abs not in table: continue
2397 # file has changed in dirstate
2401 # file has changed in dirstate
2398 if mfentry:
2402 if mfentry:
2399 handle(hitlist, backuphit)
2403 handle(hitlist, backuphit)
2400 elif misslist is not None:
2404 elif misslist is not None:
2401 handle(misslist, backupmiss)
2405 handle(misslist, backupmiss)
2402 else:
2406 else:
2403 if exact: ui.warn(_('file not managed: %s\n' % rel))
2407 if exact: ui.warn(_('file not managed: %s\n' % rel))
2404 break
2408 break
2405 else:
2409 else:
2406 # file has not changed in dirstate
2410 # file has not changed in dirstate
2407 if node == parent:
2411 if node == parent:
2408 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2412 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2409 continue
2413 continue
2410 if pmf is None:
2414 if pmf is None:
2411 # only need parent manifest in this unlikely case,
2415 # only need parent manifest in this unlikely case,
2412 # so do not read by default
2416 # so do not read by default
2413 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2417 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2414 if abs in pmf:
2418 if abs in pmf:
2415 if mfentry:
2419 if mfentry:
2416 # if version of file is same in parent and target
2420 # if version of file is same in parent and target
2417 # manifests, do nothing
2421 # manifests, do nothing
2418 if pmf[abs] != mfentry:
2422 if pmf[abs] != mfentry:
2419 handle(revert, False)
2423 handle(revert, False)
2420 else:
2424 else:
2421 handle(remove, False)
2425 handle(remove, False)
2422
2426
2423 if not opts.get('dry_run'):
2427 if not opts.get('dry_run'):
2424 repo.dirstate.forget(forget[0])
2428 repo.dirstate.forget(forget[0])
2425 r = hg.revert(repo, node, update.has_key, wlock)
2429 r = hg.revert(repo, node, update.has_key, wlock)
2426 repo.dirstate.update(add[0], 'a')
2430 repo.dirstate.update(add[0], 'a')
2427 repo.dirstate.update(undelete[0], 'n')
2431 repo.dirstate.update(undelete[0], 'n')
2428 repo.dirstate.update(remove[0], 'r')
2432 repo.dirstate.update(remove[0], 'r')
2429 return r
2433 return r
2430
2434
2431 def rollback(ui, repo):
2435 def rollback(ui, repo):
2432 """roll back the last transaction in this repository
2436 """roll back the last transaction in this repository
2433
2437
2434 Roll back the last transaction in this repository, restoring the
2438 Roll back the last transaction in this repository, restoring the
2435 project to its state prior to the transaction.
2439 project to its state prior to the transaction.
2436
2440
2437 Transactions are used to encapsulate the effects of all commands
2441 Transactions are used to encapsulate the effects of all commands
2438 that create new changesets or propagate existing changesets into a
2442 that create new changesets or propagate existing changesets into a
2439 repository. For example, the following commands are transactional,
2443 repository. For example, the following commands are transactional,
2440 and their effects can be rolled back:
2444 and their effects can be rolled back:
2441
2445
2442 commit
2446 commit
2443 import
2447 import
2444 pull
2448 pull
2445 push (with this repository as destination)
2449 push (with this repository as destination)
2446 unbundle
2450 unbundle
2447
2451
2448 This command should be used with care. There is only one level of
2452 This command should be used with care. There is only one level of
2449 rollback, and there is no way to undo a rollback.
2453 rollback, and there is no way to undo a rollback.
2450
2454
2451 This command is not intended for use on public repositories. Once
2455 This command is not intended for use on public repositories. Once
2452 changes are visible for pull by other users, rolling a transaction
2456 changes are visible for pull by other users, rolling a transaction
2453 back locally is ineffective (someone else may already have pulled
2457 back locally is ineffective (someone else may already have pulled
2454 the changes). Furthermore, a race is possible with readers of the
2458 the changes). Furthermore, a race is possible with readers of the
2455 repository; for example an in-progress pull from the repository
2459 repository; for example an in-progress pull from the repository
2456 may fail if a rollback is performed.
2460 may fail if a rollback is performed.
2457 """
2461 """
2458 repo.rollback()
2462 repo.rollback()
2459
2463
2460 def root(ui, repo):
2464 def root(ui, repo):
2461 """print the root (top) of the current working dir
2465 """print the root (top) of the current working dir
2462
2466
2463 Print the root directory of the current repository.
2467 Print the root directory of the current repository.
2464 """
2468 """
2465 ui.write(repo.root + "\n")
2469 ui.write(repo.root + "\n")
2466
2470
2467 def serve(ui, repo, **opts):
2471 def serve(ui, repo, **opts):
2468 """export the repository via HTTP
2472 """export the repository via HTTP
2469
2473
2470 Start a local HTTP repository browser and pull server.
2474 Start a local HTTP repository browser and pull server.
2471
2475
2472 By default, the server logs accesses to stdout and errors to
2476 By default, the server logs accesses to stdout and errors to
2473 stderr. Use the "-A" and "-E" options to log to files.
2477 stderr. Use the "-A" and "-E" options to log to files.
2474 """
2478 """
2475
2479
2476 if opts["stdio"]:
2480 if opts["stdio"]:
2477 if repo is None:
2481 if repo is None:
2478 raise hg.RepoError(_("There is no Mercurial repository here"
2482 raise hg.RepoError(_("There is no Mercurial repository here"
2479 " (.hg not found)"))
2483 " (.hg not found)"))
2480 s = sshserver.sshserver(ui, repo)
2484 s = sshserver.sshserver(ui, repo)
2481 s.serve_forever()
2485 s.serve_forever()
2482
2486
2483 optlist = ("name templates style address port ipv6"
2487 optlist = ("name templates style address port ipv6"
2484 " accesslog errorlog webdir_conf")
2488 " accesslog errorlog webdir_conf")
2485 for o in optlist.split():
2489 for o in optlist.split():
2486 if opts[o]:
2490 if opts[o]:
2487 ui.setconfig("web", o, str(opts[o]))
2491 ui.setconfig("web", o, str(opts[o]))
2488
2492
2489 if repo is None and not ui.config("web", "webdir_conf"):
2493 if repo is None and not ui.config("web", "webdir_conf"):
2490 raise hg.RepoError(_("There is no Mercurial repository here"
2494 raise hg.RepoError(_("There is no Mercurial repository here"
2491 " (.hg not found)"))
2495 " (.hg not found)"))
2492
2496
2493 if opts['daemon'] and not opts['daemon_pipefds']:
2497 if opts['daemon'] and not opts['daemon_pipefds']:
2494 rfd, wfd = os.pipe()
2498 rfd, wfd = os.pipe()
2495 args = sys.argv[:]
2499 args = sys.argv[:]
2496 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2500 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2497 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2501 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2498 args[0], args)
2502 args[0], args)
2499 os.close(wfd)
2503 os.close(wfd)
2500 os.read(rfd, 1)
2504 os.read(rfd, 1)
2501 os._exit(0)
2505 os._exit(0)
2502
2506
2503 try:
2507 try:
2504 httpd = hgweb.server.create_server(ui, repo)
2508 httpd = hgweb.server.create_server(ui, repo)
2505 except socket.error, inst:
2509 except socket.error, inst:
2506 raise util.Abort(_('cannot start server: %s') % inst.args[1])
2510 raise util.Abort(_('cannot start server: %s') % inst.args[1])
2507
2511
2508 if ui.verbose:
2512 if ui.verbose:
2509 addr, port = httpd.socket.getsockname()
2513 addr, port = httpd.socket.getsockname()
2510 if addr == '0.0.0.0':
2514 if addr == '0.0.0.0':
2511 addr = socket.gethostname()
2515 addr = socket.gethostname()
2512 else:
2516 else:
2513 try:
2517 try:
2514 addr = socket.gethostbyaddr(addr)[0]
2518 addr = socket.gethostbyaddr(addr)[0]
2515 except socket.error:
2519 except socket.error:
2516 pass
2520 pass
2517 if port != 80:
2521 if port != 80:
2518 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2522 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2519 else:
2523 else:
2520 ui.status(_('listening at http://%s/\n') % addr)
2524 ui.status(_('listening at http://%s/\n') % addr)
2521
2525
2522 if opts['pid_file']:
2526 if opts['pid_file']:
2523 fp = open(opts['pid_file'], 'w')
2527 fp = open(opts['pid_file'], 'w')
2524 fp.write(str(os.getpid()) + '\n')
2528 fp.write(str(os.getpid()) + '\n')
2525 fp.close()
2529 fp.close()
2526
2530
2527 if opts['daemon_pipefds']:
2531 if opts['daemon_pipefds']:
2528 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2532 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2529 os.close(rfd)
2533 os.close(rfd)
2530 os.write(wfd, 'y')
2534 os.write(wfd, 'y')
2531 os.close(wfd)
2535 os.close(wfd)
2532 sys.stdout.flush()
2536 sys.stdout.flush()
2533 sys.stderr.flush()
2537 sys.stderr.flush()
2534 fd = os.open(util.nulldev, os.O_RDWR)
2538 fd = os.open(util.nulldev, os.O_RDWR)
2535 if fd != 0: os.dup2(fd, 0)
2539 if fd != 0: os.dup2(fd, 0)
2536 if fd != 1: os.dup2(fd, 1)
2540 if fd != 1: os.dup2(fd, 1)
2537 if fd != 2: os.dup2(fd, 2)
2541 if fd != 2: os.dup2(fd, 2)
2538 if fd not in (0, 1, 2): os.close(fd)
2542 if fd not in (0, 1, 2): os.close(fd)
2539
2543
2540 httpd.serve_forever()
2544 httpd.serve_forever()
2541
2545
2542 def status(ui, repo, *pats, **opts):
2546 def status(ui, repo, *pats, **opts):
2543 """show changed files in the working directory
2547 """show changed files in the working directory
2544
2548
2545 Show status of files in the repository. If names are given, only
2549 Show status of files in the repository. If names are given, only
2546 files that match are shown. Files that are clean or ignored, are
2550 files that match are shown. Files that are clean or ignored, are
2547 not listed unless -c (clean), -i (ignored) or -A is given.
2551 not listed unless -c (clean), -i (ignored) or -A is given.
2548
2552
2549 The codes used to show the status of files are:
2553 The codes used to show the status of files are:
2550 M = modified
2554 M = modified
2551 A = added
2555 A = added
2552 R = removed
2556 R = removed
2553 C = clean
2557 C = clean
2554 ! = deleted, but still tracked
2558 ! = deleted, but still tracked
2555 ? = not tracked
2559 ? = not tracked
2556 I = ignored (not shown by default)
2560 I = ignored (not shown by default)
2557 = the previous added file was copied from here
2561 = the previous added file was copied from here
2558 """
2562 """
2559
2563
2560 all = opts['all']
2564 all = opts['all']
2561
2565
2562 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2566 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2563 cwd = (pats and repo.getcwd()) or ''
2567 cwd = (pats and repo.getcwd()) or ''
2564 modified, added, removed, deleted, unknown, ignored, clean = [
2568 modified, added, removed, deleted, unknown, ignored, clean = [
2565 [util.pathto(cwd, x) for x in n]
2569 [util.pathto(cwd, x) for x in n]
2566 for n in repo.status(files=files, match=matchfn,
2570 for n in repo.status(files=files, match=matchfn,
2567 list_ignored=all or opts['ignored'],
2571 list_ignored=all or opts['ignored'],
2568 list_clean=all or opts['clean'])]
2572 list_clean=all or opts['clean'])]
2569
2573
2570 changetypes = (('modified', 'M', modified),
2574 changetypes = (('modified', 'M', modified),
2571 ('added', 'A', added),
2575 ('added', 'A', added),
2572 ('removed', 'R', removed),
2576 ('removed', 'R', removed),
2573 ('deleted', '!', deleted),
2577 ('deleted', '!', deleted),
2574 ('unknown', '?', unknown),
2578 ('unknown', '?', unknown),
2575 ('ignored', 'I', ignored))
2579 ('ignored', 'I', ignored))
2576
2580
2577 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2581 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2578
2582
2579 end = opts['print0'] and '\0' or '\n'
2583 end = opts['print0'] and '\0' or '\n'
2580
2584
2581 for opt, char, changes in ([ct for ct in explicit_changetypes
2585 for opt, char, changes in ([ct for ct in explicit_changetypes
2582 if all or opts[ct[0]]]
2586 if all or opts[ct[0]]]
2583 or changetypes):
2587 or changetypes):
2584 if opts['no_status']:
2588 if opts['no_status']:
2585 format = "%%s%s" % end
2589 format = "%%s%s" % end
2586 else:
2590 else:
2587 format = "%s %%s%s" % (char, end)
2591 format = "%s %%s%s" % (char, end)
2588
2592
2589 for f in changes:
2593 for f in changes:
2590 ui.write(format % f)
2594 ui.write(format % f)
2591 if ((all or opts.get('copies')) and not opts.get('no_status')):
2595 if ((all or opts.get('copies')) and not opts.get('no_status')):
2592 copied = repo.dirstate.copied(f)
2596 copied = repo.dirstate.copied(f)
2593 if copied:
2597 if copied:
2594 ui.write(' %s%s' % (copied, end))
2598 ui.write(' %s%s' % (copied, end))
2595
2599
2596 def tag(ui, repo, name, rev_=None, **opts):
2600 def tag(ui, repo, name, rev_=None, **opts):
2597 """add a tag for the current tip or a given revision
2601 """add a tag for the current tip or a given revision
2598
2602
2599 Name a particular revision using <name>.
2603 Name a particular revision using <name>.
2600
2604
2601 Tags are used to name particular revisions of the repository and are
2605 Tags are used to name particular revisions of the repository and are
2602 very useful to compare different revision, to go back to significant
2606 very useful to compare different revision, to go back to significant
2603 earlier versions or to mark branch points as releases, etc.
2607 earlier versions or to mark branch points as releases, etc.
2604
2608
2605 If no revision is given, the parent of the working directory is used.
2609 If no revision is given, the parent of the working directory is used.
2606
2610
2607 To facilitate version control, distribution, and merging of tags,
2611 To facilitate version control, distribution, and merging of tags,
2608 they are stored as a file named ".hgtags" which is managed
2612 they are stored as a file named ".hgtags" which is managed
2609 similarly to other project files and can be hand-edited if
2613 similarly to other project files and can be hand-edited if
2610 necessary. The file '.hg/localtags' is used for local tags (not
2614 necessary. The file '.hg/localtags' is used for local tags (not
2611 shared among repositories).
2615 shared among repositories).
2612 """
2616 """
2613 if name in ['tip', '.']:
2617 if name in ['tip', '.']:
2614 raise util.Abort(_("the name '%s' is reserved") % name)
2618 raise util.Abort(_("the name '%s' is reserved") % name)
2615 if rev_ is not None:
2619 if rev_ is not None:
2616 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2620 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2617 "please use 'hg tag [-r REV] NAME' instead\n"))
2621 "please use 'hg tag [-r REV] NAME' instead\n"))
2618 if opts['rev']:
2622 if opts['rev']:
2619 raise util.Abort(_("use only one form to specify the revision"))
2623 raise util.Abort(_("use only one form to specify the revision"))
2620 if opts['rev']:
2624 if opts['rev']:
2621 rev_ = opts['rev']
2625 rev_ = opts['rev']
2622 if not rev_ and repo.dirstate.parents()[1] != nullid:
2626 if not rev_ and repo.dirstate.parents()[1] != nullid:
2623 raise util.Abort(_('uncommitted merge - please provide a '
2627 raise util.Abort(_('uncommitted merge - please provide a '
2624 'specific revision'))
2628 'specific revision'))
2625 r = repo.changectx(rev_).node()
2629 r = repo.changectx(rev_).node()
2626
2630
2627 message = opts['message']
2631 message = opts['message']
2628 if not message:
2632 if not message:
2629 message = _('Added tag %s for changeset %s') % (name, short(r))
2633 message = _('Added tag %s for changeset %s') % (name, short(r))
2630
2634
2631 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2635 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2632
2636
2633 def tags(ui, repo):
2637 def tags(ui, repo):
2634 """list repository tags
2638 """list repository tags
2635
2639
2636 List the repository tags.
2640 List the repository tags.
2637
2641
2638 This lists both regular and local tags.
2642 This lists both regular and local tags.
2639 """
2643 """
2640
2644
2641 l = repo.tagslist()
2645 l = repo.tagslist()
2642 l.reverse()
2646 l.reverse()
2643 hexfunc = ui.debugflag and hex or short
2647 hexfunc = ui.debugflag and hex or short
2644 for t, n in l:
2648 for t, n in l:
2645 try:
2649 try:
2646 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2650 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2647 except KeyError:
2651 except KeyError:
2648 r = " ?:?"
2652 r = " ?:?"
2649 if ui.quiet:
2653 if ui.quiet:
2650 ui.write("%s\n" % t)
2654 ui.write("%s\n" % t)
2651 else:
2655 else:
2652 ui.write("%-30s %s\n" % (t, r))
2656 ui.write("%-30s %s\n" % (t, r))
2653
2657
2654 def tip(ui, repo, **opts):
2658 def tip(ui, repo, **opts):
2655 """show the tip revision
2659 """show the tip revision
2656
2660
2657 Show the tip revision.
2661 Show the tip revision.
2658 """
2662 """
2659 n = repo.changelog.tip()
2663 n = repo.changelog.tip()
2660 br = None
2664 br = None
2661 if opts['branches']:
2665 if opts['branches']:
2662 br = repo.branchlookup([n])
2666 br = repo.branchlookup([n])
2663 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2667 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2664 if opts['patch']:
2668 if opts['patch']:
2665 patch.diff(repo, repo.changelog.parents(n)[0], n)
2669 patch.diff(repo, repo.changelog.parents(n)[0], n)
2666
2670
2667 def unbundle(ui, repo, fname, **opts):
2671 def unbundle(ui, repo, fname, **opts):
2668 """apply a changegroup file
2672 """apply a changegroup file
2669
2673
2670 Apply a compressed changegroup file generated by the bundle
2674 Apply a compressed changegroup file generated by the bundle
2671 command.
2675 command.
2672 """
2676 """
2673 f = urllib.urlopen(fname)
2677 f = urllib.urlopen(fname)
2674
2678
2675 header = f.read(6)
2679 header = f.read(6)
2676 if not header.startswith("HG"):
2680 if not header.startswith("HG"):
2677 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2681 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2678 elif not header.startswith("HG10"):
2682 elif not header.startswith("HG10"):
2679 raise util.Abort(_("%s: unknown bundle version") % fname)
2683 raise util.Abort(_("%s: unknown bundle version") % fname)
2680 elif header == "HG10BZ":
2684 elif header == "HG10BZ":
2681 def generator(f):
2685 def generator(f):
2682 zd = bz2.BZ2Decompressor()
2686 zd = bz2.BZ2Decompressor()
2683 zd.decompress("BZ")
2687 zd.decompress("BZ")
2684 for chunk in f:
2688 for chunk in f:
2685 yield zd.decompress(chunk)
2689 yield zd.decompress(chunk)
2686 elif header == "HG10UN":
2690 elif header == "HG10UN":
2687 def generator(f):
2691 def generator(f):
2688 for chunk in f:
2692 for chunk in f:
2689 yield chunk
2693 yield chunk
2690 else:
2694 else:
2691 raise util.Abort(_("%s: unknown bundle compression type")
2695 raise util.Abort(_("%s: unknown bundle compression type")
2692 % fname)
2696 % fname)
2693 gen = generator(util.filechunkiter(f, 4096))
2697 gen = generator(util.filechunkiter(f, 4096))
2694 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle',
2698 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle',
2695 'bundle:' + fname)
2699 'bundle:' + fname)
2696 return postincoming(ui, repo, modheads, opts['update'])
2700 return postincoming(ui, repo, modheads, opts['update'])
2697
2701
2698 def undo(ui, repo):
2702 def undo(ui, repo):
2699 """undo the last commit or pull (DEPRECATED)
2703 """undo the last commit or pull (DEPRECATED)
2700
2704
2701 (DEPRECATED)
2705 (DEPRECATED)
2702 This command is now deprecated and will be removed in a future
2706 This command is now deprecated and will be removed in a future
2703 release. Please use the rollback command instead. For usage
2707 release. Please use the rollback command instead. For usage
2704 instructions, see the rollback command.
2708 instructions, see the rollback command.
2705 """
2709 """
2706 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2710 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2707 repo.rollback()
2711 repo.rollback()
2708
2712
2709 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2713 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2710 branch=None):
2714 branch=None):
2711 """update or merge working directory
2715 """update or merge working directory
2712
2716
2713 Update the working directory to the specified revision.
2717 Update the working directory to the specified revision.
2714
2718
2715 If there are no outstanding changes in the working directory and
2719 If there are no outstanding changes in the working directory and
2716 there is a linear relationship between the current version and the
2720 there is a linear relationship between the current version and the
2717 requested version, the result is the requested version.
2721 requested version, the result is the requested version.
2718
2722
2719 To merge the working directory with another revision, use the
2723 To merge the working directory with another revision, use the
2720 merge command.
2724 merge command.
2721
2725
2722 By default, update will refuse to run if doing so would require
2726 By default, update will refuse to run if doing so would require
2723 merging or discarding local changes.
2727 merging or discarding local changes.
2724 """
2728 """
2725 node = _lookup(repo, node, branch)
2729 node = _lookup(repo, node, branch)
2726 if merge:
2730 if merge:
2727 ui.warn(_('(the -m/--merge option is deprecated; '
2731 ui.warn(_('(the -m/--merge option is deprecated; '
2728 'use the merge command instead)\n'))
2732 'use the merge command instead)\n'))
2729 return hg.merge(repo, node, force=force)
2733 return hg.merge(repo, node, force=force)
2730 elif clean:
2734 elif clean:
2731 return hg.clean(repo, node)
2735 return hg.clean(repo, node)
2732 else:
2736 else:
2733 return hg.update(repo, node)
2737 return hg.update(repo, node)
2734
2738
2735 def _lookup(repo, node, branch=None):
2739 def _lookup(repo, node, branch=None):
2736 if branch:
2740 if branch:
2737 br = repo.branchlookup(branch=branch)
2741 br = repo.branchlookup(branch=branch)
2738 found = []
2742 found = []
2739 for x in br:
2743 for x in br:
2740 if branch in br[x]:
2744 if branch in br[x]:
2741 found.append(x)
2745 found.append(x)
2742 if len(found) > 1:
2746 if len(found) > 1:
2743 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2747 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2744 for x in found:
2748 for x in found:
2745 show_changeset(ui, repo, {}).show(changenode=x, brinfo=br)
2749 show_changeset(ui, repo, {}).show(changenode=x, brinfo=br)
2746 raise util.Abort("")
2750 raise util.Abort("")
2747 if len(found) == 1:
2751 if len(found) == 1:
2748 node = found[0]
2752 node = found[0]
2749 repo.ui.warn(_("Using head %s for branch %s\n")
2753 repo.ui.warn(_("Using head %s for branch %s\n")
2750 % (short(node), branch))
2754 % (short(node), branch))
2751 else:
2755 else:
2752 raise util.Abort(_("branch %s not found") % branch)
2756 raise util.Abort(_("branch %s not found") % branch)
2753 else:
2757 else:
2754 node = node and repo.lookup(node) or repo.changelog.tip()
2758 node = node and repo.lookup(node) or repo.changelog.tip()
2755 return node
2759 return node
2756
2760
2757 def verify(ui, repo):
2761 def verify(ui, repo):
2758 """verify the integrity of the repository
2762 """verify the integrity of the repository
2759
2763
2760 Verify the integrity of the current repository.
2764 Verify the integrity of the current repository.
2761
2765
2762 This will perform an extensive check of the repository's
2766 This will perform an extensive check of the repository's
2763 integrity, validating the hashes and checksums of each entry in
2767 integrity, validating the hashes and checksums of each entry in
2764 the changelog, manifest, and tracked files, as well as the
2768 the changelog, manifest, and tracked files, as well as the
2765 integrity of their crosslinks and indices.
2769 integrity of their crosslinks and indices.
2766 """
2770 """
2767 return hg.verify(repo)
2771 return hg.verify(repo)
2768
2772
2769 # Command options and aliases are listed here, alphabetically
2773 # Command options and aliases are listed here, alphabetically
2770
2774
2771 globalopts = [
2775 globalopts = [
2772 ('R', 'repository', '',
2776 ('R', 'repository', '',
2773 _('repository root directory or symbolic path name')),
2777 _('repository root directory or symbolic path name')),
2774 ('', 'cwd', '', _('change working directory')),
2778 ('', 'cwd', '', _('change working directory')),
2775 ('y', 'noninteractive', None,
2779 ('y', 'noninteractive', None,
2776 _('do not prompt, assume \'yes\' for any required answers')),
2780 _('do not prompt, assume \'yes\' for any required answers')),
2777 ('q', 'quiet', None, _('suppress output')),
2781 ('q', 'quiet', None, _('suppress output')),
2778 ('v', 'verbose', None, _('enable additional output')),
2782 ('v', 'verbose', None, _('enable additional output')),
2779 ('', 'config', [], _('set/override config option')),
2783 ('', 'config', [], _('set/override config option')),
2780 ('', 'debug', None, _('enable debugging output')),
2784 ('', 'debug', None, _('enable debugging output')),
2781 ('', 'debugger', None, _('start debugger')),
2785 ('', 'debugger', None, _('start debugger')),
2782 ('', 'lsprof', None, _('print improved command execution profile')),
2786 ('', 'lsprof', None, _('print improved command execution profile')),
2783 ('', 'traceback', None, _('print traceback on exception')),
2787 ('', 'traceback', None, _('print traceback on exception')),
2784 ('', 'time', None, _('time how long the command takes')),
2788 ('', 'time', None, _('time how long the command takes')),
2785 ('', 'profile', None, _('print command execution profile')),
2789 ('', 'profile', None, _('print command execution profile')),
2786 ('', 'version', None, _('output version information and exit')),
2790 ('', 'version', None, _('output version information and exit')),
2787 ('h', 'help', None, _('display help and exit')),
2791 ('h', 'help', None, _('display help and exit')),
2788 ]
2792 ]
2789
2793
2790 dryrunopts = [('n', 'dry-run', None,
2794 dryrunopts = [('n', 'dry-run', None,
2791 _('do not perform actions, just print output'))]
2795 _('do not perform actions, just print output'))]
2792
2796
2793 remoteopts = [
2797 remoteopts = [
2794 ('e', 'ssh', '', _('specify ssh command to use')),
2798 ('e', 'ssh', '', _('specify ssh command to use')),
2795 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2799 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2796 ]
2800 ]
2797
2801
2798 walkopts = [
2802 walkopts = [
2799 ('I', 'include', [], _('include names matching the given patterns')),
2803 ('I', 'include', [], _('include names matching the given patterns')),
2800 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2804 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2801 ]
2805 ]
2802
2806
2803 table = {
2807 table = {
2804 "^add":
2808 "^add":
2805 (add,
2809 (add,
2806 walkopts + dryrunopts,
2810 walkopts + dryrunopts,
2807 _('hg add [OPTION]... [FILE]...')),
2811 _('hg add [OPTION]... [FILE]...')),
2808 "addremove":
2812 "addremove":
2809 (addremove,
2813 (addremove,
2810 [('s', 'similarity', '',
2814 [('s', 'similarity', '',
2811 _('guess renamed files by similarity (0<=s<=100)')),
2815 _('guess renamed files by similarity (0<=s<=100)')),
2812 ] + walkopts + dryrunopts,
2816 ] + walkopts + dryrunopts,
2813 _('hg addremove [OPTION]... [FILE]...')),
2817 _('hg addremove [OPTION]... [FILE]...')),
2814 "^annotate":
2818 "^annotate":
2815 (annotate,
2819 (annotate,
2816 [('r', 'rev', '', _('annotate the specified revision')),
2820 [('r', 'rev', '', _('annotate the specified revision')),
2817 ('f', 'follow', None, _('follow file copies and renames')),
2821 ('f', 'follow', None, _('follow file copies and renames')),
2818 ('a', 'text', None, _('treat all files as text')),
2822 ('a', 'text', None, _('treat all files as text')),
2819 ('u', 'user', None, _('list the author')),
2823 ('u', 'user', None, _('list the author')),
2820 ('d', 'date', None, _('list the date')),
2824 ('d', 'date', None, _('list the date')),
2821 ('n', 'number', None, _('list the revision number (default)')),
2825 ('n', 'number', None, _('list the revision number (default)')),
2822 ('c', 'changeset', None, _('list the changeset')),
2826 ('c', 'changeset', None, _('list the changeset')),
2823 ] + walkopts,
2827 ] + walkopts,
2824 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2828 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2825 "archive":
2829 "archive":
2826 (archive,
2830 (archive,
2827 [('', 'no-decode', None, _('do not pass files through decoders')),
2831 [('', 'no-decode', None, _('do not pass files through decoders')),
2828 ('p', 'prefix', '', _('directory prefix for files in archive')),
2832 ('p', 'prefix', '', _('directory prefix for files in archive')),
2829 ('r', 'rev', '', _('revision to distribute')),
2833 ('r', 'rev', '', _('revision to distribute')),
2830 ('t', 'type', '', _('type of distribution to create')),
2834 ('t', 'type', '', _('type of distribution to create')),
2831 ] + walkopts,
2835 ] + walkopts,
2832 _('hg archive [OPTION]... DEST')),
2836 _('hg archive [OPTION]... DEST')),
2833 "backout":
2837 "backout":
2834 (backout,
2838 (backout,
2835 [('', 'merge', None,
2839 [('', 'merge', None,
2836 _('merge with old dirstate parent after backout')),
2840 _('merge with old dirstate parent after backout')),
2837 ('m', 'message', '', _('use <text> as commit message')),
2841 ('m', 'message', '', _('use <text> as commit message')),
2838 ('l', 'logfile', '', _('read commit message from <file>')),
2842 ('l', 'logfile', '', _('read commit message from <file>')),
2839 ('d', 'date', '', _('record datecode as commit date')),
2843 ('d', 'date', '', _('record datecode as commit date')),
2840 ('', 'parent', '', _('parent to choose when backing out merge')),
2844 ('', 'parent', '', _('parent to choose when backing out merge')),
2841 ('u', 'user', '', _('record user as committer')),
2845 ('u', 'user', '', _('record user as committer')),
2842 ] + walkopts,
2846 ] + walkopts,
2843 _('hg backout [OPTION]... REV')),
2847 _('hg backout [OPTION]... REV')),
2844 "bundle":
2848 "bundle":
2845 (bundle,
2849 (bundle,
2846 [('f', 'force', None,
2850 [('f', 'force', None,
2847 _('run even when remote repository is unrelated')),
2851 _('run even when remote repository is unrelated')),
2848 ('r', 'rev', [],
2852 ('r', 'rev', [],
2849 _('a changeset you would like to bundle')),
2853 _('a changeset you would like to bundle')),
2850 ('', 'base', [],
2854 ('', 'base', [],
2851 _('a base changeset to specify instead of a destination')),
2855 _('a base changeset to specify instead of a destination')),
2852 ] + remoteopts,
2856 ] + remoteopts,
2853 _('hg bundle [--base REV]... [--rev REV]... FILE [DEST]')),
2857 _('hg bundle [--base REV]... [--rev REV]... FILE [DEST]')),
2854 "cat":
2858 "cat":
2855 (cat,
2859 (cat,
2856 [('o', 'output', '', _('print output to file with formatted name')),
2860 [('o', 'output', '', _('print output to file with formatted name')),
2857 ('r', 'rev', '', _('print the given revision')),
2861 ('r', 'rev', '', _('print the given revision')),
2858 ] + walkopts,
2862 ] + walkopts,
2859 _('hg cat [OPTION]... FILE...')),
2863 _('hg cat [OPTION]... FILE...')),
2860 "^clone":
2864 "^clone":
2861 (clone,
2865 (clone,
2862 [('U', 'noupdate', None, _('do not update the new working directory')),
2866 [('U', 'noupdate', None, _('do not update the new working directory')),
2863 ('r', 'rev', [],
2867 ('r', 'rev', [],
2864 _('a changeset you would like to have after cloning')),
2868 _('a changeset you would like to have after cloning')),
2865 ('', 'pull', None, _('use pull protocol to copy metadata')),
2869 ('', 'pull', None, _('use pull protocol to copy metadata')),
2866 ('', 'uncompressed', None,
2870 ('', 'uncompressed', None,
2867 _('use uncompressed transfer (fast over LAN)')),
2871 _('use uncompressed transfer (fast over LAN)')),
2868 ] + remoteopts,
2872 ] + remoteopts,
2869 _('hg clone [OPTION]... SOURCE [DEST]')),
2873 _('hg clone [OPTION]... SOURCE [DEST]')),
2870 "^commit|ci":
2874 "^commit|ci":
2871 (commit,
2875 (commit,
2872 [('A', 'addremove', None,
2876 [('A', 'addremove', None,
2873 _('mark new/missing files as added/removed before committing')),
2877 _('mark new/missing files as added/removed before committing')),
2874 ('m', 'message', '', _('use <text> as commit message')),
2878 ('m', 'message', '', _('use <text> as commit message')),
2875 ('l', 'logfile', '', _('read the commit message from <file>')),
2879 ('l', 'logfile', '', _('read the commit message from <file>')),
2876 ('d', 'date', '', _('record datecode as commit date')),
2880 ('d', 'date', '', _('record datecode as commit date')),
2877 ('u', 'user', '', _('record user as commiter')),
2881 ('u', 'user', '', _('record user as commiter')),
2878 ] + walkopts,
2882 ] + walkopts,
2879 _('hg commit [OPTION]... [FILE]...')),
2883 _('hg commit [OPTION]... [FILE]...')),
2880 "copy|cp":
2884 "copy|cp":
2881 (copy,
2885 (copy,
2882 [('A', 'after', None, _('record a copy that has already occurred')),
2886 [('A', 'after', None, _('record a copy that has already occurred')),
2883 ('f', 'force', None,
2887 ('f', 'force', None,
2884 _('forcibly copy over an existing managed file')),
2888 _('forcibly copy over an existing managed file')),
2885 ] + walkopts + dryrunopts,
2889 ] + walkopts + dryrunopts,
2886 _('hg copy [OPTION]... [SOURCE]... DEST')),
2890 _('hg copy [OPTION]... [SOURCE]... DEST')),
2887 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2891 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2888 "debugcomplete":
2892 "debugcomplete":
2889 (debugcomplete,
2893 (debugcomplete,
2890 [('o', 'options', None, _('show the command options'))],
2894 [('o', 'options', None, _('show the command options'))],
2891 _('debugcomplete [-o] CMD')),
2895 _('debugcomplete [-o] CMD')),
2892 "debugrebuildstate":
2896 "debugrebuildstate":
2893 (debugrebuildstate,
2897 (debugrebuildstate,
2894 [('r', 'rev', '', _('revision to rebuild to'))],
2898 [('r', 'rev', '', _('revision to rebuild to'))],
2895 _('debugrebuildstate [-r REV] [REV]')),
2899 _('debugrebuildstate [-r REV] [REV]')),
2896 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2900 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2897 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2901 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2898 "debugstate": (debugstate, [], _('debugstate')),
2902 "debugstate": (debugstate, [], _('debugstate')),
2899 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2903 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2900 "debugindex": (debugindex, [], _('debugindex FILE')),
2904 "debugindex": (debugindex, [], _('debugindex FILE')),
2901 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2905 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2902 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2906 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2903 "debugwalk":
2907 "debugwalk":
2904 (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2908 (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2905 "^diff":
2909 "^diff":
2906 (diff,
2910 (diff,
2907 [('r', 'rev', [], _('revision')),
2911 [('r', 'rev', [], _('revision')),
2908 ('a', 'text', None, _('treat all files as text')),
2912 ('a', 'text', None, _('treat all files as text')),
2909 ('p', 'show-function', None,
2913 ('p', 'show-function', None,
2910 _('show which function each change is in')),
2914 _('show which function each change is in')),
2911 ('g', 'git', None, _('use git extended diff format')),
2915 ('g', 'git', None, _('use git extended diff format')),
2912 ('', 'nodates', None, _("don't include dates in diff headers")),
2916 ('', 'nodates', None, _("don't include dates in diff headers")),
2913 ('w', 'ignore-all-space', None,
2917 ('w', 'ignore-all-space', None,
2914 _('ignore white space when comparing lines')),
2918 _('ignore white space when comparing lines')),
2915 ('b', 'ignore-space-change', None,
2919 ('b', 'ignore-space-change', None,
2916 _('ignore changes in the amount of white space')),
2920 _('ignore changes in the amount of white space')),
2917 ('B', 'ignore-blank-lines', None,
2921 ('B', 'ignore-blank-lines', None,
2918 _('ignore changes whose lines are all blank')),
2922 _('ignore changes whose lines are all blank')),
2919 ] + walkopts,
2923 ] + walkopts,
2920 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2924 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2921 "^export":
2925 "^export":
2922 (export,
2926 (export,
2923 [('o', 'output', '', _('print output to file with formatted name')),
2927 [('o', 'output', '', _('print output to file with formatted name')),
2924 ('a', 'text', None, _('treat all files as text')),
2928 ('a', 'text', None, _('treat all files as text')),
2925 ('g', 'git', None, _('use git extended diff format')),
2929 ('g', 'git', None, _('use git extended diff format')),
2926 ('', 'nodates', None, _("don't include dates in diff headers")),
2930 ('', 'nodates', None, _("don't include dates in diff headers")),
2927 ('', 'switch-parent', None, _('diff against the second parent'))],
2931 ('', 'switch-parent', None, _('diff against the second parent'))],
2928 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2932 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2929 "debugforget|forget":
2933 "debugforget|forget":
2930 (forget, walkopts, _('hg forget [OPTION]... FILE...')),
2934 (forget, walkopts, _('hg forget [OPTION]... FILE...')),
2931 "grep":
2935 "grep":
2932 (grep,
2936 (grep,
2933 [('0', 'print0', None, _('end fields with NUL')),
2937 [('0', 'print0', None, _('end fields with NUL')),
2934 ('', 'all', None, _('print all revisions that match')),
2938 ('', 'all', None, _('print all revisions that match')),
2935 ('f', 'follow', None,
2939 ('f', 'follow', None,
2936 _('follow changeset history, or file history across copies and renames')),
2940 _('follow changeset history, or file history across copies and renames')),
2937 ('i', 'ignore-case', None, _('ignore case when matching')),
2941 ('i', 'ignore-case', None, _('ignore case when matching')),
2938 ('l', 'files-with-matches', None,
2942 ('l', 'files-with-matches', None,
2939 _('print only filenames and revs that match')),
2943 _('print only filenames and revs that match')),
2940 ('n', 'line-number', None, _('print matching line numbers')),
2944 ('n', 'line-number', None, _('print matching line numbers')),
2941 ('r', 'rev', [], _('search in given revision range')),
2945 ('r', 'rev', [], _('search in given revision range')),
2942 ('u', 'user', None, _('print user who committed change')),
2946 ('u', 'user', None, _('print user who committed change')),
2943 ] + walkopts,
2947 ] + walkopts,
2944 _('hg grep [OPTION]... PATTERN [FILE]...')),
2948 _('hg grep [OPTION]... PATTERN [FILE]...')),
2945 "heads":
2949 "heads":
2946 (heads,
2950 (heads,
2947 [('b', 'branches', None, _('show branches')),
2951 [('b', 'branches', None, _('show branches')),
2948 ('', 'style', '', _('display using template map file')),
2952 ('', 'style', '', _('display using template map file')),
2949 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2953 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2950 ('', 'template', '', _('display with template'))],
2954 ('', 'template', '', _('display with template'))],
2951 _('hg heads [-b] [-r <rev>]')),
2955 _('hg heads [-b] [-r <rev>]')),
2952 "help": (help_, [], _('hg help [COMMAND]')),
2956 "help": (help_, [], _('hg help [COMMAND]')),
2953 "identify|id": (identify, [], _('hg identify')),
2957 "identify|id": (identify, [], _('hg identify')),
2954 "import|patch":
2958 "import|patch":
2955 (import_,
2959 (import_,
2956 [('p', 'strip', 1,
2960 [('p', 'strip', 1,
2957 _('directory strip option for patch. This has the same\n'
2961 _('directory strip option for patch. This has the same\n'
2958 'meaning as the corresponding patch option')),
2962 'meaning as the corresponding patch option')),
2959 ('m', 'message', '', _('use <text> as commit message')),
2963 ('m', 'message', '', _('use <text> as commit message')),
2960 ('b', 'base', '', _('base path')),
2964 ('b', 'base', '', _('base path')),
2961 ('f', 'force', None,
2965 ('f', 'force', None,
2962 _('skip check for outstanding uncommitted changes'))],
2966 _('skip check for outstanding uncommitted changes'))],
2963 _('hg import [-p NUM] [-b BASE] [-m MESSAGE] [-f] PATCH...')),
2967 _('hg import [-p NUM] [-b BASE] [-m MESSAGE] [-f] PATCH...')),
2964 "incoming|in": (incoming,
2968 "incoming|in": (incoming,
2965 [('M', 'no-merges', None, _('do not show merges')),
2969 [('M', 'no-merges', None, _('do not show merges')),
2966 ('f', 'force', None,
2970 ('f', 'force', None,
2967 _('run even when remote repository is unrelated')),
2971 _('run even when remote repository is unrelated')),
2968 ('', 'style', '', _('display using template map file')),
2972 ('', 'style', '', _('display using template map file')),
2969 ('n', 'newest-first', None, _('show newest record first')),
2973 ('n', 'newest-first', None, _('show newest record first')),
2970 ('', 'bundle', '', _('file to store the bundles into')),
2974 ('', 'bundle', '', _('file to store the bundles into')),
2971 ('p', 'patch', None, _('show patch')),
2975 ('p', 'patch', None, _('show patch')),
2972 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2976 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2973 ('', 'template', '', _('display with template')),
2977 ('', 'template', '', _('display with template')),
2974 ] + remoteopts,
2978 ] + remoteopts,
2975 _('hg incoming [-p] [-n] [-M] [-r REV]...'
2979 _('hg incoming [-p] [-n] [-M] [-r REV]...'
2976 ' [--bundle FILENAME] [SOURCE]')),
2980 ' [--bundle FILENAME] [SOURCE]')),
2977 "^init":
2981 "^init":
2978 (init, remoteopts, _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
2982 (init, remoteopts, _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
2979 "locate":
2983 "locate":
2980 (locate,
2984 (locate,
2981 [('r', 'rev', '', _('search the repository as it stood at rev')),
2985 [('r', 'rev', '', _('search the repository as it stood at rev')),
2982 ('0', 'print0', None,
2986 ('0', 'print0', None,
2983 _('end filenames with NUL, for use with xargs')),
2987 _('end filenames with NUL, for use with xargs')),
2984 ('f', 'fullpath', None,
2988 ('f', 'fullpath', None,
2985 _('print complete paths from the filesystem root')),
2989 _('print complete paths from the filesystem root')),
2986 ] + walkopts,
2990 ] + walkopts,
2987 _('hg locate [OPTION]... [PATTERN]...')),
2991 _('hg locate [OPTION]... [PATTERN]...')),
2988 "^log|history":
2992 "^log|history":
2989 (log,
2993 (log,
2990 [('b', 'branches', None, _('show branches')),
2994 [('b', 'branches', None, _('show branches')),
2991 ('f', 'follow', None,
2995 ('f', 'follow', None,
2992 _('follow changeset history, or file history across copies and renames')),
2996 _('follow changeset history, or file history across copies and renames')),
2993 ('', 'follow-first', None,
2997 ('', 'follow-first', None,
2994 _('only follow the first parent of merge changesets')),
2998 _('only follow the first parent of merge changesets')),
2995 ('C', 'copies', None, _('show copied files')),
2999 ('C', 'copies', None, _('show copied files')),
2996 ('k', 'keyword', [], _('search for a keyword')),
3000 ('k', 'keyword', [], _('search for a keyword')),
2997 ('l', 'limit', '', _('limit number of changes displayed')),
3001 ('l', 'limit', '', _('limit number of changes displayed')),
2998 ('r', 'rev', [], _('show the specified revision or range')),
3002 ('r', 'rev', [], _('show the specified revision or range')),
2999 ('M', 'no-merges', None, _('do not show merges')),
3003 ('M', 'no-merges', None, _('do not show merges')),
3000 ('', 'style', '', _('display using template map file')),
3004 ('', 'style', '', _('display using template map file')),
3001 ('m', 'only-merges', None, _('show only merges')),
3005 ('m', 'only-merges', None, _('show only merges')),
3002 ('p', 'patch', None, _('show patch')),
3006 ('p', 'patch', None, _('show patch')),
3003 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3007 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3004 ('', 'template', '', _('display with template')),
3008 ('', 'template', '', _('display with template')),
3005 ] + walkopts,
3009 ] + walkopts,
3006 _('hg log [OPTION]... [FILE]')),
3010 _('hg log [OPTION]... [FILE]')),
3007 "manifest": (manifest, [], _('hg manifest [REV]')),
3011 "manifest": (manifest, [], _('hg manifest [REV]')),
3008 "merge":
3012 "merge":
3009 (merge,
3013 (merge,
3010 [('b', 'branch', '', _('merge with head of a specific branch')),
3014 [('b', 'branch', '', _('merge with head of a specific branch')),
3011 ('f', 'force', None, _('force a merge with outstanding changes'))],
3015 ('f', 'force', None, _('force a merge with outstanding changes'))],
3012 _('hg merge [-b TAG] [-f] [REV]')),
3016 _('hg merge [-b TAG] [-f] [REV]')),
3013 "outgoing|out": (outgoing,
3017 "outgoing|out": (outgoing,
3014 [('M', 'no-merges', None, _('do not show merges')),
3018 [('M', 'no-merges', None, _('do not show merges')),
3015 ('f', 'force', None,
3019 ('f', 'force', None,
3016 _('run even when remote repository is unrelated')),
3020 _('run even when remote repository is unrelated')),
3017 ('p', 'patch', None, _('show patch')),
3021 ('p', 'patch', None, _('show patch')),
3018 ('', 'style', '', _('display using template map file')),
3022 ('', 'style', '', _('display using template map file')),
3019 ('r', 'rev', [], _('a specific revision you would like to push')),
3023 ('r', 'rev', [], _('a specific revision you would like to push')),
3020 ('n', 'newest-first', None, _('show newest record first')),
3024 ('n', 'newest-first', None, _('show newest record first')),
3021 ('', 'template', '', _('display with template')),
3025 ('', 'template', '', _('display with template')),
3022 ] + remoteopts,
3026 ] + remoteopts,
3023 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3027 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3024 "^parents":
3028 "^parents":
3025 (parents,
3029 (parents,
3026 [('b', 'branches', None, _('show branches')),
3030 [('b', 'branches', None, _('show branches')),
3027 ('r', 'rev', '', _('show parents from the specified rev')),
3031 ('r', 'rev', '', _('show parents from the specified rev')),
3028 ('', 'style', '', _('display using template map file')),
3032 ('', 'style', '', _('display using template map file')),
3029 ('', 'template', '', _('display with template'))],
3033 ('', 'template', '', _('display with template'))],
3030 _('hg parents [-b] [-r REV] [FILE]')),
3034 _('hg parents [-b] [-r REV] [FILE]')),
3031 "paths": (paths, [], _('hg paths [NAME]')),
3035 "paths": (paths, [], _('hg paths [NAME]')),
3032 "^pull":
3036 "^pull":
3033 (pull,
3037 (pull,
3034 [('u', 'update', None,
3038 [('u', 'update', None,
3035 _('update to new tip if changesets were pulled')),
3039 _('update to new tip if changesets were pulled')),
3036 ('f', 'force', None,
3040 ('f', 'force', None,
3037 _('run even when remote repository is unrelated')),
3041 _('run even when remote repository is unrelated')),
3038 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
3042 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
3039 ] + remoteopts,
3043 ] + remoteopts,
3040 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
3044 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
3041 "^push":
3045 "^push":
3042 (push,
3046 (push,
3043 [('f', 'force', None, _('force push')),
3047 [('f', 'force', None, _('force push')),
3044 ('r', 'rev', [], _('a specific revision you would like to push')),
3048 ('r', 'rev', [], _('a specific revision you would like to push')),
3045 ] + remoteopts,
3049 ] + remoteopts,
3046 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
3050 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
3047 "debugrawcommit|rawcommit":
3051 "debugrawcommit|rawcommit":
3048 (rawcommit,
3052 (rawcommit,
3049 [('p', 'parent', [], _('parent')),
3053 [('p', 'parent', [], _('parent')),
3050 ('d', 'date', '', _('date code')),
3054 ('d', 'date', '', _('date code')),
3051 ('u', 'user', '', _('user')),
3055 ('u', 'user', '', _('user')),
3052 ('F', 'files', '', _('file list')),
3056 ('F', 'files', '', _('file list')),
3053 ('m', 'message', '', _('commit message')),
3057 ('m', 'message', '', _('commit message')),
3054 ('l', 'logfile', '', _('commit message file'))],
3058 ('l', 'logfile', '', _('commit message file'))],
3055 _('hg debugrawcommit [OPTION]... [FILE]...')),
3059 _('hg debugrawcommit [OPTION]... [FILE]...')),
3056 "recover": (recover, [], _('hg recover')),
3060 "recover": (recover, [], _('hg recover')),
3057 "^remove|rm":
3061 "^remove|rm":
3058 (remove,
3062 (remove,
3059 [('A', 'after', None, _('record remove that has already occurred')),
3063 [('A', 'after', None, _('record remove that has already occurred')),
3060 ('f', 'force', None, _('remove file even if modified')),
3064 ('f', 'force', None, _('remove file even if modified')),
3061 ] + walkopts,
3065 ] + walkopts,
3062 _('hg remove [OPTION]... FILE...')),
3066 _('hg remove [OPTION]... FILE...')),
3063 "rename|mv":
3067 "rename|mv":
3064 (rename,
3068 (rename,
3065 [('A', 'after', None, _('record a rename that has already occurred')),
3069 [('A', 'after', None, _('record a rename that has already occurred')),
3066 ('f', 'force', None,
3070 ('f', 'force', None,
3067 _('forcibly copy over an existing managed file')),
3071 _('forcibly copy over an existing managed file')),
3068 ] + walkopts + dryrunopts,
3072 ] + walkopts + dryrunopts,
3069 _('hg rename [OPTION]... SOURCE... DEST')),
3073 _('hg rename [OPTION]... SOURCE... DEST')),
3070 "^revert":
3074 "^revert":
3071 (revert,
3075 (revert,
3072 [('a', 'all', None, _('revert all changes when no arguments given')),
3076 [('a', 'all', None, _('revert all changes when no arguments given')),
3073 ('r', 'rev', '', _('revision to revert to')),
3077 ('r', 'rev', '', _('revision to revert to')),
3074 ('', 'no-backup', None, _('do not save backup copies of files')),
3078 ('', 'no-backup', None, _('do not save backup copies of files')),
3075 ] + walkopts + dryrunopts,
3079 ] + walkopts + dryrunopts,
3076 _('hg revert [-r REV] [NAME]...')),
3080 _('hg revert [-r REV] [NAME]...')),
3077 "rollback": (rollback, [], _('hg rollback')),
3081 "rollback": (rollback, [], _('hg rollback')),
3078 "root": (root, [], _('hg root')),
3082 "root": (root, [], _('hg root')),
3079 "showconfig|debugconfig": (showconfig, [], _('showconfig [NAME]...')),
3083 "showconfig|debugconfig": (showconfig, [], _('showconfig [NAME]...')),
3080 "^serve":
3084 "^serve":
3081 (serve,
3085 (serve,
3082 [('A', 'accesslog', '', _('name of access log file to write to')),
3086 [('A', 'accesslog', '', _('name of access log file to write to')),
3083 ('d', 'daemon', None, _('run server in background')),
3087 ('d', 'daemon', None, _('run server in background')),
3084 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3088 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3085 ('E', 'errorlog', '', _('name of error log file to write to')),
3089 ('E', 'errorlog', '', _('name of error log file to write to')),
3086 ('p', 'port', 0, _('port to use (default: 8000)')),
3090 ('p', 'port', 0, _('port to use (default: 8000)')),
3087 ('a', 'address', '', _('address to use')),
3091 ('a', 'address', '', _('address to use')),
3088 ('n', 'name', '',
3092 ('n', 'name', '',
3089 _('name to show in web pages (default: working dir)')),
3093 _('name to show in web pages (default: working dir)')),
3090 ('', 'webdir-conf', '', _('name of the webdir config file'
3094 ('', 'webdir-conf', '', _('name of the webdir config file'
3091 ' (serve more than one repo)')),
3095 ' (serve more than one repo)')),
3092 ('', 'pid-file', '', _('name of file to write process ID to')),
3096 ('', 'pid-file', '', _('name of file to write process ID to')),
3093 ('', 'stdio', None, _('for remote clients')),
3097 ('', 'stdio', None, _('for remote clients')),
3094 ('t', 'templates', '', _('web templates to use')),
3098 ('t', 'templates', '', _('web templates to use')),
3095 ('', 'style', '', _('template style to use')),
3099 ('', 'style', '', _('template style to use')),
3096 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3100 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3097 _('hg serve [OPTION]...')),
3101 _('hg serve [OPTION]...')),
3098 "^status|st":
3102 "^status|st":
3099 (status,
3103 (status,
3100 [('A', 'all', None, _('show status of all files')),
3104 [('A', 'all', None, _('show status of all files')),
3101 ('m', 'modified', None, _('show only modified files')),
3105 ('m', 'modified', None, _('show only modified files')),
3102 ('a', 'added', None, _('show only added files')),
3106 ('a', 'added', None, _('show only added files')),
3103 ('r', 'removed', None, _('show only removed files')),
3107 ('r', 'removed', None, _('show only removed files')),
3104 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3108 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3105 ('c', 'clean', None, _('show only files without changes')),
3109 ('c', 'clean', None, _('show only files without changes')),
3106 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3110 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3107 ('i', 'ignored', None, _('show ignored files')),
3111 ('i', 'ignored', None, _('show ignored files')),
3108 ('n', 'no-status', None, _('hide status prefix')),
3112 ('n', 'no-status', None, _('hide status prefix')),
3109 ('C', 'copies', None, _('show source of copied files')),
3113 ('C', 'copies', None, _('show source of copied files')),
3110 ('0', 'print0', None,
3114 ('0', 'print0', None,
3111 _('end filenames with NUL, for use with xargs')),
3115 _('end filenames with NUL, for use with xargs')),
3112 ] + walkopts,
3116 ] + walkopts,
3113 _('hg status [OPTION]... [FILE]...')),
3117 _('hg status [OPTION]... [FILE]...')),
3114 "tag":
3118 "tag":
3115 (tag,
3119 (tag,
3116 [('l', 'local', None, _('make the tag local')),
3120 [('l', 'local', None, _('make the tag local')),
3117 ('m', 'message', '', _('message for tag commit log entry')),
3121 ('m', 'message', '', _('message for tag commit log entry')),
3118 ('d', 'date', '', _('record datecode as commit date')),
3122 ('d', 'date', '', _('record datecode as commit date')),
3119 ('u', 'user', '', _('record user as commiter')),
3123 ('u', 'user', '', _('record user as commiter')),
3120 ('r', 'rev', '', _('revision to tag'))],
3124 ('r', 'rev', '', _('revision to tag'))],
3121 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3125 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3122 "tags": (tags, [], _('hg tags')),
3126 "tags": (tags, [], _('hg tags')),
3123 "tip":
3127 "tip":
3124 (tip,
3128 (tip,
3125 [('b', 'branches', None, _('show branches')),
3129 [('b', 'branches', None, _('show branches')),
3126 ('', 'style', '', _('display using template map file')),
3130 ('', 'style', '', _('display using template map file')),
3127 ('p', 'patch', None, _('show patch')),
3131 ('p', 'patch', None, _('show patch')),
3128 ('', 'template', '', _('display with template'))],
3132 ('', 'template', '', _('display with template'))],
3129 _('hg tip [-b] [-p]')),
3133 _('hg tip [-b] [-p]')),
3130 "unbundle":
3134 "unbundle":
3131 (unbundle,
3135 (unbundle,
3132 [('u', 'update', None,
3136 [('u', 'update', None,
3133 _('update to new tip if changesets were unbundled'))],
3137 _('update to new tip if changesets were unbundled'))],
3134 _('hg unbundle [-u] FILE')),
3138 _('hg unbundle [-u] FILE')),
3135 "debugundo|undo": (undo, [], _('hg undo')),
3139 "debugundo|undo": (undo, [], _('hg undo')),
3136 "^update|up|checkout|co":
3140 "^update|up|checkout|co":
3137 (update,
3141 (update,
3138 [('b', 'branch', '', _('checkout the head of a specific branch')),
3142 [('b', 'branch', '', _('checkout the head of a specific branch')),
3139 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3143 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3140 ('C', 'clean', None, _('overwrite locally modified files')),
3144 ('C', 'clean', None, _('overwrite locally modified files')),
3141 ('f', 'force', None, _('force a merge with outstanding changes'))],
3145 ('f', 'force', None, _('force a merge with outstanding changes'))],
3142 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3146 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3143 "verify": (verify, [], _('hg verify')),
3147 "verify": (verify, [], _('hg verify')),
3144 "version": (show_version, [], _('hg version')),
3148 "version": (show_version, [], _('hg version')),
3145 }
3149 }
3146
3150
3147 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3151 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3148 " debugindex debugindexdot")
3152 " debugindex debugindexdot")
3149 optionalrepo = ("paths serve showconfig")
3153 optionalrepo = ("paths serve showconfig")
3150
3154
3151 def findpossible(ui, cmd):
3155 def findpossible(ui, cmd):
3152 """
3156 """
3153 Return cmd -> (aliases, command table entry)
3157 Return cmd -> (aliases, command table entry)
3154 for each matching command.
3158 for each matching command.
3155 Return debug commands (or their aliases) only if no normal command matches.
3159 Return debug commands (or their aliases) only if no normal command matches.
3156 """
3160 """
3157 choice = {}
3161 choice = {}
3158 debugchoice = {}
3162 debugchoice = {}
3159 for e in table.keys():
3163 for e in table.keys():
3160 aliases = e.lstrip("^").split("|")
3164 aliases = e.lstrip("^").split("|")
3161 found = None
3165 found = None
3162 if cmd in aliases:
3166 if cmd in aliases:
3163 found = cmd
3167 found = cmd
3164 elif not ui.config("ui", "strict"):
3168 elif not ui.config("ui", "strict"):
3165 for a in aliases:
3169 for a in aliases:
3166 if a.startswith(cmd):
3170 if a.startswith(cmd):
3167 found = a
3171 found = a
3168 break
3172 break
3169 if found is not None:
3173 if found is not None:
3170 if aliases[0].startswith("debug") or found.startswith("debug"):
3174 if aliases[0].startswith("debug") or found.startswith("debug"):
3171 debugchoice[found] = (aliases, table[e])
3175 debugchoice[found] = (aliases, table[e])
3172 else:
3176 else:
3173 choice[found] = (aliases, table[e])
3177 choice[found] = (aliases, table[e])
3174
3178
3175 if not choice and debugchoice:
3179 if not choice and debugchoice:
3176 choice = debugchoice
3180 choice = debugchoice
3177
3181
3178 return choice
3182 return choice
3179
3183
3180 def findcmd(ui, cmd):
3184 def findcmd(ui, cmd):
3181 """Return (aliases, command table entry) for command string."""
3185 """Return (aliases, command table entry) for command string."""
3182 choice = findpossible(ui, cmd)
3186 choice = findpossible(ui, cmd)
3183
3187
3184 if choice.has_key(cmd):
3188 if choice.has_key(cmd):
3185 return choice[cmd]
3189 return choice[cmd]
3186
3190
3187 if len(choice) > 1:
3191 if len(choice) > 1:
3188 clist = choice.keys()
3192 clist = choice.keys()
3189 clist.sort()
3193 clist.sort()
3190 raise AmbiguousCommand(cmd, clist)
3194 raise AmbiguousCommand(cmd, clist)
3191
3195
3192 if choice:
3196 if choice:
3193 return choice.values()[0]
3197 return choice.values()[0]
3194
3198
3195 raise UnknownCommand(cmd)
3199 raise UnknownCommand(cmd)
3196
3200
3197 def catchterm(*args):
3201 def catchterm(*args):
3198 raise util.SignalInterrupt
3202 raise util.SignalInterrupt
3199
3203
3200 def run():
3204 def run():
3201 sys.exit(dispatch(sys.argv[1:]))
3205 sys.exit(dispatch(sys.argv[1:]))
3202
3206
3203 class ParseError(Exception):
3207 class ParseError(Exception):
3204 """Exception raised on errors in parsing the command line."""
3208 """Exception raised on errors in parsing the command line."""
3205
3209
3206 def parse(ui, args):
3210 def parse(ui, args):
3207 options = {}
3211 options = {}
3208 cmdoptions = {}
3212 cmdoptions = {}
3209
3213
3210 try:
3214 try:
3211 args = fancyopts.fancyopts(args, globalopts, options)
3215 args = fancyopts.fancyopts(args, globalopts, options)
3212 except fancyopts.getopt.GetoptError, inst:
3216 except fancyopts.getopt.GetoptError, inst:
3213 raise ParseError(None, inst)
3217 raise ParseError(None, inst)
3214
3218
3215 if args:
3219 if args:
3216 cmd, args = args[0], args[1:]
3220 cmd, args = args[0], args[1:]
3217 aliases, i = findcmd(ui, cmd)
3221 aliases, i = findcmd(ui, cmd)
3218 cmd = aliases[0]
3222 cmd = aliases[0]
3219 defaults = ui.config("defaults", cmd)
3223 defaults = ui.config("defaults", cmd)
3220 if defaults:
3224 if defaults:
3221 args = shlex.split(defaults) + args
3225 args = shlex.split(defaults) + args
3222 c = list(i[1])
3226 c = list(i[1])
3223 else:
3227 else:
3224 cmd = None
3228 cmd = None
3225 c = []
3229 c = []
3226
3230
3227 # combine global options into local
3231 # combine global options into local
3228 for o in globalopts:
3232 for o in globalopts:
3229 c.append((o[0], o[1], options[o[1]], o[3]))
3233 c.append((o[0], o[1], options[o[1]], o[3]))
3230
3234
3231 try:
3235 try:
3232 args = fancyopts.fancyopts(args, c, cmdoptions)
3236 args = fancyopts.fancyopts(args, c, cmdoptions)
3233 except fancyopts.getopt.GetoptError, inst:
3237 except fancyopts.getopt.GetoptError, inst:
3234 raise ParseError(cmd, inst)
3238 raise ParseError(cmd, inst)
3235
3239
3236 # separate global options back out
3240 # separate global options back out
3237 for o in globalopts:
3241 for o in globalopts:
3238 n = o[1]
3242 n = o[1]
3239 options[n] = cmdoptions[n]
3243 options[n] = cmdoptions[n]
3240 del cmdoptions[n]
3244 del cmdoptions[n]
3241
3245
3242 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3246 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3243
3247
3244 external = {}
3248 external = {}
3245
3249
3246 def findext(name):
3250 def findext(name):
3247 '''return module with given extension name'''
3251 '''return module with given extension name'''
3248 try:
3252 try:
3249 return sys.modules[external[name]]
3253 return sys.modules[external[name]]
3250 except KeyError:
3254 except KeyError:
3251 for k, v in external.iteritems():
3255 for k, v in external.iteritems():
3252 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3256 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3253 return sys.modules[v]
3257 return sys.modules[v]
3254 raise KeyError(name)
3258 raise KeyError(name)
3255
3259
3256 def load_extensions(ui):
3260 def load_extensions(ui):
3257 added = []
3261 added = []
3258 for ext_name, load_from_name in ui.extensions():
3262 for ext_name, load_from_name in ui.extensions():
3259 if ext_name in external:
3263 if ext_name in external:
3260 continue
3264 continue
3261 try:
3265 try:
3262 if load_from_name:
3266 if load_from_name:
3263 # the module will be loaded in sys.modules
3267 # the module will be loaded in sys.modules
3264 # choose an unique name so that it doesn't
3268 # choose an unique name so that it doesn't
3265 # conflicts with other modules
3269 # conflicts with other modules
3266 module_name = "hgext_%s" % ext_name.replace('.', '_')
3270 module_name = "hgext_%s" % ext_name.replace('.', '_')
3267 mod = imp.load_source(module_name, load_from_name)
3271 mod = imp.load_source(module_name, load_from_name)
3268 else:
3272 else:
3269 def importh(name):
3273 def importh(name):
3270 mod = __import__(name)
3274 mod = __import__(name)
3271 components = name.split('.')
3275 components = name.split('.')
3272 for comp in components[1:]:
3276 for comp in components[1:]:
3273 mod = getattr(mod, comp)
3277 mod = getattr(mod, comp)
3274 return mod
3278 return mod
3275 try:
3279 try:
3276 mod = importh("hgext.%s" % ext_name)
3280 mod = importh("hgext.%s" % ext_name)
3277 except ImportError:
3281 except ImportError:
3278 mod = importh(ext_name)
3282 mod = importh(ext_name)
3279 external[ext_name] = mod.__name__
3283 external[ext_name] = mod.__name__
3280 added.append((mod, ext_name))
3284 added.append((mod, ext_name))
3281 except (util.SignalInterrupt, KeyboardInterrupt):
3285 except (util.SignalInterrupt, KeyboardInterrupt):
3282 raise
3286 raise
3283 except Exception, inst:
3287 except Exception, inst:
3284 ui.warn(_("*** failed to import extension %s: %s\n") %
3288 ui.warn(_("*** failed to import extension %s: %s\n") %
3285 (ext_name, inst))
3289 (ext_name, inst))
3286 if ui.print_exc():
3290 if ui.print_exc():
3287 return 1
3291 return 1
3288
3292
3289 for mod, name in added:
3293 for mod, name in added:
3290 uisetup = getattr(mod, 'uisetup', None)
3294 uisetup = getattr(mod, 'uisetup', None)
3291 if uisetup:
3295 if uisetup:
3292 uisetup(ui)
3296 uisetup(ui)
3293 cmdtable = getattr(mod, 'cmdtable', {})
3297 cmdtable = getattr(mod, 'cmdtable', {})
3294 for t in cmdtable:
3298 for t in cmdtable:
3295 if t in table:
3299 if t in table:
3296 ui.warn(_("module %s overrides %s\n") % (name, t))
3300 ui.warn(_("module %s overrides %s\n") % (name, t))
3297 table.update(cmdtable)
3301 table.update(cmdtable)
3298
3302
3299 def parseconfig(config):
3303 def parseconfig(config):
3300 """parse the --config options from the command line"""
3304 """parse the --config options from the command line"""
3301 parsed = []
3305 parsed = []
3302 for cfg in config:
3306 for cfg in config:
3303 try:
3307 try:
3304 name, value = cfg.split('=', 1)
3308 name, value = cfg.split('=', 1)
3305 section, name = name.split('.', 1)
3309 section, name = name.split('.', 1)
3306 if not section or not name:
3310 if not section or not name:
3307 raise IndexError
3311 raise IndexError
3308 parsed.append((section, name, value))
3312 parsed.append((section, name, value))
3309 except (IndexError, ValueError):
3313 except (IndexError, ValueError):
3310 raise util.Abort(_('malformed --config option: %s') % cfg)
3314 raise util.Abort(_('malformed --config option: %s') % cfg)
3311 return parsed
3315 return parsed
3312
3316
3313 def dispatch(args):
3317 def dispatch(args):
3314 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3318 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3315 num = getattr(signal, name, None)
3319 num = getattr(signal, name, None)
3316 if num: signal.signal(num, catchterm)
3320 if num: signal.signal(num, catchterm)
3317
3321
3318 try:
3322 try:
3319 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3323 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3320 except util.Abort, inst:
3324 except util.Abort, inst:
3321 sys.stderr.write(_("abort: %s\n") % inst)
3325 sys.stderr.write(_("abort: %s\n") % inst)
3322 return -1
3326 return -1
3323
3327
3324 load_extensions(u)
3328 load_extensions(u)
3325 u.addreadhook(load_extensions)
3329 u.addreadhook(load_extensions)
3326
3330
3327 try:
3331 try:
3328 cmd, func, args, options, cmdoptions = parse(u, args)
3332 cmd, func, args, options, cmdoptions = parse(u, args)
3329 if options["time"]:
3333 if options["time"]:
3330 def get_times():
3334 def get_times():
3331 t = os.times()
3335 t = os.times()
3332 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3336 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3333 t = (t[0], t[1], t[2], t[3], time.clock())
3337 t = (t[0], t[1], t[2], t[3], time.clock())
3334 return t
3338 return t
3335 s = get_times()
3339 s = get_times()
3336 def print_time():
3340 def print_time():
3337 t = get_times()
3341 t = get_times()
3338 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3342 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3339 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3343 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3340 atexit.register(print_time)
3344 atexit.register(print_time)
3341
3345
3342 # enter the debugger before command execution
3346 # enter the debugger before command execution
3343 if options['debugger']:
3347 if options['debugger']:
3344 pdb.set_trace()
3348 pdb.set_trace()
3345
3349
3346 try:
3350 try:
3347 if options['cwd']:
3351 if options['cwd']:
3348 try:
3352 try:
3349 os.chdir(options['cwd'])
3353 os.chdir(options['cwd'])
3350 except OSError, inst:
3354 except OSError, inst:
3351 raise util.Abort('%s: %s' %
3355 raise util.Abort('%s: %s' %
3352 (options['cwd'], inst.strerror))
3356 (options['cwd'], inst.strerror))
3353
3357
3354 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3358 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3355 not options["noninteractive"], options["traceback"],
3359 not options["noninteractive"], options["traceback"],
3356 parseconfig(options["config"]))
3360 parseconfig(options["config"]))
3357
3361
3358 path = u.expandpath(options["repository"]) or ""
3362 path = u.expandpath(options["repository"]) or ""
3359 repo = path and hg.repository(u, path=path) or None
3363 repo = path and hg.repository(u, path=path) or None
3360 if repo and not repo.local():
3364 if repo and not repo.local():
3361 raise util.Abort(_("repository '%s' is not local") % path)
3365 raise util.Abort(_("repository '%s' is not local") % path)
3362
3366
3363 if options['help']:
3367 if options['help']:
3364 return help_(u, cmd, options['version'])
3368 return help_(u, cmd, options['version'])
3365 elif options['version']:
3369 elif options['version']:
3366 return show_version(u)
3370 return show_version(u)
3367 elif not cmd:
3371 elif not cmd:
3368 return help_(u, 'shortlist')
3372 return help_(u, 'shortlist')
3369
3373
3370 if cmd not in norepo.split():
3374 if cmd not in norepo.split():
3371 try:
3375 try:
3372 if not repo:
3376 if not repo:
3373 repo = hg.repository(u, path=path)
3377 repo = hg.repository(u, path=path)
3374 u = repo.ui
3378 u = repo.ui
3375 for name in external.itervalues():
3379 for name in external.itervalues():
3376 mod = sys.modules[name]
3380 mod = sys.modules[name]
3377 if hasattr(mod, 'reposetup'):
3381 if hasattr(mod, 'reposetup'):
3378 mod.reposetup(u, repo)
3382 mod.reposetup(u, repo)
3379 hg.repo_setup_hooks.append(mod.reposetup)
3383 hg.repo_setup_hooks.append(mod.reposetup)
3380 except hg.RepoError:
3384 except hg.RepoError:
3381 if cmd not in optionalrepo.split():
3385 if cmd not in optionalrepo.split():
3382 raise
3386 raise
3383 d = lambda: func(u, repo, *args, **cmdoptions)
3387 d = lambda: func(u, repo, *args, **cmdoptions)
3384 else:
3388 else:
3385 d = lambda: func(u, *args, **cmdoptions)
3389 d = lambda: func(u, *args, **cmdoptions)
3386
3390
3387 try:
3391 try:
3388 if options['profile']:
3392 if options['profile']:
3389 import hotshot, hotshot.stats
3393 import hotshot, hotshot.stats
3390 prof = hotshot.Profile("hg.prof")
3394 prof = hotshot.Profile("hg.prof")
3391 try:
3395 try:
3392 try:
3396 try:
3393 return prof.runcall(d)
3397 return prof.runcall(d)
3394 except:
3398 except:
3395 try:
3399 try:
3396 u.warn(_('exception raised - generating '
3400 u.warn(_('exception raised - generating '
3397 'profile anyway\n'))
3401 'profile anyway\n'))
3398 except:
3402 except:
3399 pass
3403 pass
3400 raise
3404 raise
3401 finally:
3405 finally:
3402 prof.close()
3406 prof.close()
3403 stats = hotshot.stats.load("hg.prof")
3407 stats = hotshot.stats.load("hg.prof")
3404 stats.strip_dirs()
3408 stats.strip_dirs()
3405 stats.sort_stats('time', 'calls')
3409 stats.sort_stats('time', 'calls')
3406 stats.print_stats(40)
3410 stats.print_stats(40)
3407 elif options['lsprof']:
3411 elif options['lsprof']:
3408 try:
3412 try:
3409 from mercurial import lsprof
3413 from mercurial import lsprof
3410 except ImportError:
3414 except ImportError:
3411 raise util.Abort(_(
3415 raise util.Abort(_(
3412 'lsprof not available - install from '
3416 'lsprof not available - install from '
3413 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3417 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3414 p = lsprof.Profiler()
3418 p = lsprof.Profiler()
3415 p.enable(subcalls=True)
3419 p.enable(subcalls=True)
3416 try:
3420 try:
3417 return d()
3421 return d()
3418 finally:
3422 finally:
3419 p.disable()
3423 p.disable()
3420 stats = lsprof.Stats(p.getstats())
3424 stats = lsprof.Stats(p.getstats())
3421 stats.sort()
3425 stats.sort()
3422 stats.pprint(top=10, file=sys.stderr, climit=5)
3426 stats.pprint(top=10, file=sys.stderr, climit=5)
3423 else:
3427 else:
3424 return d()
3428 return d()
3425 finally:
3429 finally:
3426 u.flush()
3430 u.flush()
3427 except:
3431 except:
3428 # enter the debugger when we hit an exception
3432 # enter the debugger when we hit an exception
3429 if options['debugger']:
3433 if options['debugger']:
3430 pdb.post_mortem(sys.exc_info()[2])
3434 pdb.post_mortem(sys.exc_info()[2])
3431 u.print_exc()
3435 u.print_exc()
3432 raise
3436 raise
3433 except ParseError, inst:
3437 except ParseError, inst:
3434 if inst.args[0]:
3438 if inst.args[0]:
3435 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3439 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3436 help_(u, inst.args[0])
3440 help_(u, inst.args[0])
3437 else:
3441 else:
3438 u.warn(_("hg: %s\n") % inst.args[1])
3442 u.warn(_("hg: %s\n") % inst.args[1])
3439 help_(u, 'shortlist')
3443 help_(u, 'shortlist')
3440 except AmbiguousCommand, inst:
3444 except AmbiguousCommand, inst:
3441 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3445 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3442 (inst.args[0], " ".join(inst.args[1])))
3446 (inst.args[0], " ".join(inst.args[1])))
3443 except UnknownCommand, inst:
3447 except UnknownCommand, inst:
3444 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3448 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3445 help_(u, 'shortlist')
3449 help_(u, 'shortlist')
3446 except hg.RepoError, inst:
3450 except hg.RepoError, inst:
3447 u.warn(_("abort: %s!\n") % inst)
3451 u.warn(_("abort: %s!\n") % inst)
3448 except lock.LockHeld, inst:
3452 except lock.LockHeld, inst:
3449 if inst.errno == errno.ETIMEDOUT:
3453 if inst.errno == errno.ETIMEDOUT:
3450 reason = _('timed out waiting for lock held by %s') % inst.locker
3454 reason = _('timed out waiting for lock held by %s') % inst.locker
3451 else:
3455 else:
3452 reason = _('lock held by %s') % inst.locker
3456 reason = _('lock held by %s') % inst.locker
3453 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3457 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3454 except lock.LockUnavailable, inst:
3458 except lock.LockUnavailable, inst:
3455 u.warn(_("abort: could not lock %s: %s\n") %
3459 u.warn(_("abort: could not lock %s: %s\n") %
3456 (inst.desc or inst.filename, inst.strerror))
3460 (inst.desc or inst.filename, inst.strerror))
3457 except revlog.RevlogError, inst:
3461 except revlog.RevlogError, inst:
3458 u.warn(_("abort: %s!\n") % inst)
3462 u.warn(_("abort: %s!\n") % inst)
3459 except util.SignalInterrupt:
3463 except util.SignalInterrupt:
3460 u.warn(_("killed!\n"))
3464 u.warn(_("killed!\n"))
3461 except KeyboardInterrupt:
3465 except KeyboardInterrupt:
3462 try:
3466 try:
3463 u.warn(_("interrupted!\n"))
3467 u.warn(_("interrupted!\n"))
3464 except IOError, inst:
3468 except IOError, inst:
3465 if inst.errno == errno.EPIPE:
3469 if inst.errno == errno.EPIPE:
3466 if u.debugflag:
3470 if u.debugflag:
3467 u.warn(_("\nbroken pipe\n"))
3471 u.warn(_("\nbroken pipe\n"))
3468 else:
3472 else:
3469 raise
3473 raise
3470 except IOError, inst:
3474 except IOError, inst:
3471 if hasattr(inst, "code"):
3475 if hasattr(inst, "code"):
3472 u.warn(_("abort: %s\n") % inst)
3476 u.warn(_("abort: %s\n") % inst)
3473 elif hasattr(inst, "reason"):
3477 elif hasattr(inst, "reason"):
3474 u.warn(_("abort: error: %s\n") % inst.reason[1])
3478 u.warn(_("abort: error: %s\n") % inst.reason[1])
3475 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3479 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3476 if u.debugflag:
3480 if u.debugflag:
3477 u.warn(_("broken pipe\n"))
3481 u.warn(_("broken pipe\n"))
3478 elif getattr(inst, "strerror", None):
3482 elif getattr(inst, "strerror", None):
3479 if getattr(inst, "filename", None):
3483 if getattr(inst, "filename", None):
3480 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3484 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3481 else:
3485 else:
3482 u.warn(_("abort: %s\n") % inst.strerror)
3486 u.warn(_("abort: %s\n") % inst.strerror)
3483 else:
3487 else:
3484 raise
3488 raise
3485 except OSError, inst:
3489 except OSError, inst:
3486 if getattr(inst, "filename", None):
3490 if getattr(inst, "filename", None):
3487 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3491 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3488 else:
3492 else:
3489 u.warn(_("abort: %s\n") % inst.strerror)
3493 u.warn(_("abort: %s\n") % inst.strerror)
3490 except util.Abort, inst:
3494 except util.Abort, inst:
3491 u.warn(_("abort: %s\n") % inst)
3495 u.warn(_("abort: %s\n") % inst)
3492 except TypeError, inst:
3496 except TypeError, inst:
3493 # was this an argument error?
3497 # was this an argument error?
3494 tb = traceback.extract_tb(sys.exc_info()[2])
3498 tb = traceback.extract_tb(sys.exc_info()[2])
3495 if len(tb) > 2: # no
3499 if len(tb) > 2: # no
3496 raise
3500 raise
3497 u.debug(inst, "\n")
3501 u.debug(inst, "\n")
3498 u.warn(_("%s: invalid arguments\n") % cmd)
3502 u.warn(_("%s: invalid arguments\n") % cmd)
3499 help_(u, cmd)
3503 help_(u, cmd)
3500 except SystemExit, inst:
3504 except SystemExit, inst:
3501 # Commands shouldn't sys.exit directly, but give a return code.
3505 # Commands shouldn't sys.exit directly, but give a return code.
3502 # Just in case catch this and and pass exit code to caller.
3506 # Just in case catch this and and pass exit code to caller.
3503 return inst.code
3507 return inst.code
3504 except:
3508 except:
3505 u.warn(_("** unknown exception encountered, details follow\n"))
3509 u.warn(_("** unknown exception encountered, details follow\n"))
3506 u.warn(_("** report bug details to "
3510 u.warn(_("** report bug details to "
3507 "http://www.selenic.com/mercurial/bts\n"))
3511 "http://www.selenic.com/mercurial/bts\n"))
3508 u.warn(_("** or mercurial@selenic.com\n"))
3512 u.warn(_("** or mercurial@selenic.com\n"))
3509 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3513 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3510 % version.get_version())
3514 % version.get_version())
3511 raise
3515 raise
3512
3516
3513 return -1
3517 return -1
@@ -1,659 +1,657 b''
1 # patch.py - patch file parsing routines
1 # patch.py - patch file parsing routines
2 #
2 #
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006 Brendan Cully <brendan@kublai.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 i18n import gettext as _
9 from i18n import gettext as _
10 from node import *
10 from node import *
11 demandload(globals(), "base85 cmdutil mdiff util")
11 demandload(globals(), "base85 cmdutil mdiff util")
12 demandload(globals(), "cStringIO email.Parser errno os popen2 re shutil sha")
12 demandload(globals(), "cStringIO email.Parser errno os popen2 re shutil sha")
13 demandload(globals(), "sys tempfile zlib")
13 demandload(globals(), "sys tempfile zlib")
14
14
15 # helper functions
15 # helper functions
16
16
17 def copyfile(src, dst, basedir=None):
17 def copyfile(src, dst, basedir=None):
18 if not basedir:
18 if not basedir:
19 basedir = os.getcwd()
19 basedir = os.getcwd()
20
20
21 abssrc, absdst = [os.path.join(basedir, n) for n in (src, dst)]
21 abssrc, absdst = [os.path.join(basedir, n) for n in (src, dst)]
22 if os.path.exists(absdst):
22 if os.path.exists(absdst):
23 raise util.Abort(_("cannot create %s: destination already exists") %
23 raise util.Abort(_("cannot create %s: destination already exists") %
24 dst)
24 dst)
25
25
26 targetdir = os.path.dirname(absdst)
26 targetdir = os.path.dirname(absdst)
27 if not os.path.isdir(targetdir):
27 if not os.path.isdir(targetdir):
28 os.makedirs(targetdir)
28 os.makedirs(targetdir)
29 try:
29 try:
30 shutil.copyfile(abssrc, absdst)
30 shutil.copyfile(abssrc, absdst)
31 shutil.copymode(abssrc, absdst)
31 shutil.copymode(abssrc, absdst)
32 except shutil.Error, inst:
32 except shutil.Error, inst:
33 raise util.Abort(str(inst))
33 raise util.Abort(str(inst))
34
34
35 # public functions
35 # public functions
36
36
37 def extract(ui, fileobj):
37 def extract(ui, fileobj):
38 '''extract patch from data read from fileobj.
38 '''extract patch from data read from fileobj.
39
39
40 patch can be normal patch or contained in email message.
40 patch can be normal patch or contained in email message.
41
41
42 return tuple (filename, message, user, date). any item in returned
42 return tuple (filename, message, user, date). any item in returned
43 tuple can be None. if filename is None, fileobj did not contain
43 tuple can be None. if filename is None, fileobj did not contain
44 patch. caller must unlink filename when done.'''
44 patch. caller must unlink filename when done.'''
45
45
46 # attempt to detect the start of a patch
46 # attempt to detect the start of a patch
47 # (this heuristic is borrowed from quilt)
47 # (this heuristic is borrowed from quilt)
48 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
48 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
49 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
49 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
50 '(---|\*\*\*)[ \t])', re.MULTILINE)
50 '(---|\*\*\*)[ \t])', re.MULTILINE)
51
51
52 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
52 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
53 tmpfp = os.fdopen(fd, 'w')
53 tmpfp = os.fdopen(fd, 'w')
54 try:
54 try:
55 hgpatch = False
55 hgpatch = False
56
56
57 msg = email.Parser.Parser().parse(fileobj)
57 msg = email.Parser.Parser().parse(fileobj)
58
58
59 message = msg['Subject']
59 message = msg['Subject']
60 user = msg['From']
60 user = msg['From']
61 # should try to parse msg['Date']
61 # should try to parse msg['Date']
62 date = None
62 date = None
63
63
64 if message:
64 if message:
65 message = message.replace('\n\t', ' ')
65 message = message.replace('\n\t', ' ')
66 ui.debug('Subject: %s\n' % message)
66 ui.debug('Subject: %s\n' % message)
67 if user:
67 if user:
68 ui.debug('From: %s\n' % user)
68 ui.debug('From: %s\n' % user)
69 diffs_seen = 0
69 diffs_seen = 0
70 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
70 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
71
71
72 for part in msg.walk():
72 for part in msg.walk():
73 content_type = part.get_content_type()
73 content_type = part.get_content_type()
74 ui.debug('Content-Type: %s\n' % content_type)
74 ui.debug('Content-Type: %s\n' % content_type)
75 if content_type not in ok_types:
75 if content_type not in ok_types:
76 continue
76 continue
77 payload = part.get_payload(decode=True)
77 payload = part.get_payload(decode=True)
78 m = diffre.search(payload)
78 m = diffre.search(payload)
79 if m:
79 if m:
80 ui.debug(_('found patch at byte %d\n') % m.start(0))
80 ui.debug(_('found patch at byte %d\n') % m.start(0))
81 diffs_seen += 1
81 diffs_seen += 1
82 cfp = cStringIO.StringIO()
82 cfp = cStringIO.StringIO()
83 if message:
83 if message:
84 cfp.write(message)
84 cfp.write(message)
85 cfp.write('\n')
85 cfp.write('\n')
86 for line in payload[:m.start(0)].splitlines():
86 for line in payload[:m.start(0)].splitlines():
87 if line.startswith('# HG changeset patch'):
87 if line.startswith('# HG changeset patch'):
88 ui.debug(_('patch generated by hg export\n'))
88 ui.debug(_('patch generated by hg export\n'))
89 hgpatch = True
89 hgpatch = True
90 # drop earlier commit message content
90 # drop earlier commit message content
91 cfp.seek(0)
91 cfp.seek(0)
92 cfp.truncate()
92 cfp.truncate()
93 elif hgpatch:
93 elif hgpatch:
94 if line.startswith('# User '):
94 if line.startswith('# User '):
95 user = line[7:]
95 user = line[7:]
96 ui.debug('From: %s\n' % user)
96 ui.debug('From: %s\n' % user)
97 elif line.startswith("# Date "):
97 elif line.startswith("# Date "):
98 date = line[7:]
98 date = line[7:]
99 if not line.startswith('# '):
99 if not line.startswith('# '):
100 cfp.write(line)
100 cfp.write(line)
101 cfp.write('\n')
101 cfp.write('\n')
102 message = cfp.getvalue()
102 message = cfp.getvalue()
103 if tmpfp:
103 if tmpfp:
104 tmpfp.write(payload)
104 tmpfp.write(payload)
105 if not payload.endswith('\n'):
105 if not payload.endswith('\n'):
106 tmpfp.write('\n')
106 tmpfp.write('\n')
107 elif not diffs_seen and message and content_type == 'text/plain':
107 elif not diffs_seen and message and content_type == 'text/plain':
108 message += '\n' + payload
108 message += '\n' + payload
109 except:
109 except:
110 tmpfp.close()
110 tmpfp.close()
111 os.unlink(tmpname)
111 os.unlink(tmpname)
112 raise
112 raise
113
113
114 tmpfp.close()
114 tmpfp.close()
115 if not diffs_seen:
115 if not diffs_seen:
116 os.unlink(tmpname)
116 os.unlink(tmpname)
117 return None, message, user, date
117 return None, message, user, date
118 return tmpname, message, user, date
118 return tmpname, message, user, date
119
119
120 def readgitpatch(patchname):
120 def readgitpatch(patchname):
121 """extract git-style metadata about patches from <patchname>"""
121 """extract git-style metadata about patches from <patchname>"""
122 class gitpatch:
122 class gitpatch:
123 "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
123 "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
124 def __init__(self, path):
124 def __init__(self, path):
125 self.path = path
125 self.path = path
126 self.oldpath = None
126 self.oldpath = None
127 self.mode = None
127 self.mode = None
128 self.op = 'MODIFY'
128 self.op = 'MODIFY'
129 self.copymod = False
129 self.copymod = False
130 self.lineno = 0
130 self.lineno = 0
131 self.binary = False
131 self.binary = False
132
132
133 # Filter patch for git information
133 # Filter patch for git information
134 gitre = re.compile('diff --git a/(.*) b/(.*)')
134 gitre = re.compile('diff --git a/(.*) b/(.*)')
135 pf = file(patchname)
135 pf = file(patchname)
136 gp = None
136 gp = None
137 gitpatches = []
137 gitpatches = []
138 # Can have a git patch with only metadata, causing patch to complain
138 # Can have a git patch with only metadata, causing patch to complain
139 dopatch = False
139 dopatch = False
140
140
141 lineno = 0
141 lineno = 0
142 for line in pf:
142 for line in pf:
143 lineno += 1
143 lineno += 1
144 if line.startswith('diff --git'):
144 if line.startswith('diff --git'):
145 m = gitre.match(line)
145 m = gitre.match(line)
146 if m:
146 if m:
147 if gp:
147 if gp:
148 gitpatches.append(gp)
148 gitpatches.append(gp)
149 src, dst = m.group(1,2)
149 src, dst = m.group(1,2)
150 gp = gitpatch(dst)
150 gp = gitpatch(dst)
151 gp.lineno = lineno
151 gp.lineno = lineno
152 elif gp:
152 elif gp:
153 if line.startswith('--- '):
153 if line.startswith('--- '):
154 if gp.op in ('COPY', 'RENAME'):
154 if gp.op in ('COPY', 'RENAME'):
155 gp.copymod = True
155 gp.copymod = True
156 dopatch = 'filter'
156 dopatch = 'filter'
157 gitpatches.append(gp)
157 gitpatches.append(gp)
158 gp = None
158 gp = None
159 if not dopatch:
159 if not dopatch:
160 dopatch = True
160 dopatch = True
161 continue
161 continue
162 if line.startswith('rename from '):
162 if line.startswith('rename from '):
163 gp.op = 'RENAME'
163 gp.op = 'RENAME'
164 gp.oldpath = line[12:].rstrip()
164 gp.oldpath = line[12:].rstrip()
165 elif line.startswith('rename to '):
165 elif line.startswith('rename to '):
166 gp.path = line[10:].rstrip()
166 gp.path = line[10:].rstrip()
167 elif line.startswith('copy from '):
167 elif line.startswith('copy from '):
168 gp.op = 'COPY'
168 gp.op = 'COPY'
169 gp.oldpath = line[10:].rstrip()
169 gp.oldpath = line[10:].rstrip()
170 elif line.startswith('copy to '):
170 elif line.startswith('copy to '):
171 gp.path = line[8:].rstrip()
171 gp.path = line[8:].rstrip()
172 elif line.startswith('deleted file'):
172 elif line.startswith('deleted file'):
173 gp.op = 'DELETE'
173 gp.op = 'DELETE'
174 elif line.startswith('new file mode '):
174 elif line.startswith('new file mode '):
175 gp.op = 'ADD'
175 gp.op = 'ADD'
176 gp.mode = int(line.rstrip()[-3:], 8)
176 gp.mode = int(line.rstrip()[-3:], 8)
177 elif line.startswith('new mode '):
177 elif line.startswith('new mode '):
178 gp.mode = int(line.rstrip()[-3:], 8)
178 gp.mode = int(line.rstrip()[-3:], 8)
179 elif line.startswith('GIT binary patch'):
179 elif line.startswith('GIT binary patch'):
180 if not dopatch:
180 if not dopatch:
181 dopatch = 'binary'
181 dopatch = 'binary'
182 gp.binary = True
182 gp.binary = True
183 if gp:
183 if gp:
184 gitpatches.append(gp)
184 gitpatches.append(gp)
185
185
186 if not gitpatches:
186 if not gitpatches:
187 dopatch = True
187 dopatch = True
188
188
189 return (dopatch, gitpatches)
189 return (dopatch, gitpatches)
190
190
191 def dogitpatch(patchname, gitpatches, cwd=None):
191 def dogitpatch(patchname, gitpatches, cwd=None):
192 """Preprocess git patch so that vanilla patch can handle it"""
192 """Preprocess git patch so that vanilla patch can handle it"""
193 def extractbin(fp):
193 def extractbin(fp):
194 line = fp.readline().rstrip()
194 line = fp.readline().rstrip()
195 while line and not line.startswith('literal '):
195 while line and not line.startswith('literal '):
196 line = fp.readline().rstrip()
196 line = fp.readline().rstrip()
197 if not line:
197 if not line:
198 return
198 return
199 size = int(line[8:])
199 size = int(line[8:])
200 dec = []
200 dec = []
201 line = fp.readline().rstrip()
201 line = fp.readline().rstrip()
202 while line:
202 while line:
203 l = line[0]
203 l = line[0]
204 if l <= 'Z' and l >= 'A':
204 if l <= 'Z' and l >= 'A':
205 l = ord(l) - ord('A') + 1
205 l = ord(l) - ord('A') + 1
206 else:
206 else:
207 l = ord(l) - ord('a') + 27
207 l = ord(l) - ord('a') + 27
208 dec.append(base85.b85decode(line[1:])[:l])
208 dec.append(base85.b85decode(line[1:])[:l])
209 line = fp.readline().rstrip()
209 line = fp.readline().rstrip()
210 text = zlib.decompress(''.join(dec))
210 text = zlib.decompress(''.join(dec))
211 if len(text) != size:
211 if len(text) != size:
212 raise util.Abort(_('binary patch is %d bytes, not %d') %
212 raise util.Abort(_('binary patch is %d bytes, not %d') %
213 (len(text), size))
213 (len(text), size))
214 return text
214 return text
215
215
216 pf = file(patchname)
216 pf = file(patchname)
217 pfline = 1
217 pfline = 1
218
218
219 fd, patchname = tempfile.mkstemp(prefix='hg-patch-')
219 fd, patchname = tempfile.mkstemp(prefix='hg-patch-')
220 tmpfp = os.fdopen(fd, 'w')
220 tmpfp = os.fdopen(fd, 'w')
221
221
222 try:
222 try:
223 for i in range(len(gitpatches)):
223 for i in range(len(gitpatches)):
224 p = gitpatches[i]
224 p = gitpatches[i]
225 if not p.copymod and not p.binary:
225 if not p.copymod and not p.binary:
226 continue
226 continue
227
227
228 # rewrite patch hunk
228 # rewrite patch hunk
229 while pfline < p.lineno:
229 while pfline < p.lineno:
230 tmpfp.write(pf.readline())
230 tmpfp.write(pf.readline())
231 pfline += 1
231 pfline += 1
232
232
233 if p.binary:
233 if p.binary:
234 text = extractbin(pf)
234 text = extractbin(pf)
235 if not text:
235 if not text:
236 raise util.Abort(_('binary patch extraction failed'))
236 raise util.Abort(_('binary patch extraction failed'))
237 if not cwd:
237 if not cwd:
238 cwd = os.getcwd()
238 cwd = os.getcwd()
239 absdst = os.path.join(cwd, p.path)
239 absdst = os.path.join(cwd, p.path)
240 basedir = os.path.dirname(absdst)
240 basedir = os.path.dirname(absdst)
241 if not os.path.isdir(basedir):
241 if not os.path.isdir(basedir):
242 os.makedirs(basedir)
242 os.makedirs(basedir)
243 out = file(absdst, 'wb')
243 out = file(absdst, 'wb')
244 out.write(text)
244 out.write(text)
245 out.close()
245 out.close()
246 elif p.copymod:
246 elif p.copymod:
247 copyfile(p.oldpath, p.path, basedir=cwd)
247 copyfile(p.oldpath, p.path, basedir=cwd)
248 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path))
248 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path))
249 line = pf.readline()
249 line = pf.readline()
250 pfline += 1
250 pfline += 1
251 while not line.startswith('--- a/'):
251 while not line.startswith('--- a/'):
252 tmpfp.write(line)
252 tmpfp.write(line)
253 line = pf.readline()
253 line = pf.readline()
254 pfline += 1
254 pfline += 1
255 tmpfp.write('--- a/%s\n' % p.path)
255 tmpfp.write('--- a/%s\n' % p.path)
256
256
257 line = pf.readline()
257 line = pf.readline()
258 while line:
258 while line:
259 tmpfp.write(line)
259 tmpfp.write(line)
260 line = pf.readline()
260 line = pf.readline()
261 except:
261 except:
262 tmpfp.close()
262 tmpfp.close()
263 os.unlink(patchname)
263 os.unlink(patchname)
264 raise
264 raise
265
265
266 tmpfp.close()
266 tmpfp.close()
267 return patchname
267 return patchname
268
268
269 def patch(patchname, ui, strip=1, cwd=None):
269 def patch(patchname, ui, strip=1, cwd=None, files={}):
270 """apply the patch <patchname> to the working directory.
270 """apply the patch <patchname> to the working directory.
271 a list of patched files is returned"""
271 a list of patched files is returned"""
272
272
273 # helper function
273 # helper function
274 def __patch(patchname):
274 def __patch(patchname):
275 """patch and updates the files and fuzz variables"""
275 """patch and updates the files and fuzz variables"""
276 files = {}
277 fuzz = False
276 fuzz = False
278
277
279 patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''),
278 patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''),
280 'patch')
279 'patch')
281 args = []
280 args = []
282 if cwd:
281 if cwd:
283 args.append('-d %s' % util.shellquote(cwd))
282 args.append('-d %s' % util.shellquote(cwd))
284 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
283 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
285 util.shellquote(patchname)))
284 util.shellquote(patchname)))
286
285
287 for line in fp:
286 for line in fp:
288 line = line.rstrip()
287 line = line.rstrip()
289 ui.note(line + '\n')
288 ui.note(line + '\n')
290 if line.startswith('patching file '):
289 if line.startswith('patching file '):
291 pf = util.parse_patch_output(line)
290 pf = util.parse_patch_output(line)
292 printed_file = False
291 printed_file = False
293 files.setdefault(pf, (None, None))
292 files.setdefault(pf, (None, None))
294 elif line.find('with fuzz') >= 0:
293 elif line.find('with fuzz') >= 0:
295 fuzz = True
294 fuzz = True
296 if not printed_file:
295 if not printed_file:
297 ui.warn(pf + '\n')
296 ui.warn(pf + '\n')
298 printed_file = True
297 printed_file = True
299 ui.warn(line + '\n')
298 ui.warn(line + '\n')
300 elif line.find('saving rejects to file') >= 0:
299 elif line.find('saving rejects to file') >= 0:
301 ui.warn(line + '\n')
300 ui.warn(line + '\n')
302 elif line.find('FAILED') >= 0:
301 elif line.find('FAILED') >= 0:
303 if not printed_file:
302 if not printed_file:
304 ui.warn(pf + '\n')
303 ui.warn(pf + '\n')
305 printed_file = True
304 printed_file = True
306 ui.warn(line + '\n')
305 ui.warn(line + '\n')
307 code = fp.close()
306 code = fp.close()
308 if code:
307 if code:
309 raise util.Abort(_("patch command failed: %s") %
308 raise util.Abort(_("patch command failed: %s") %
310 util.explain_exit(code)[0])
309 util.explain_exit(code)[0])
311 return files, fuzz
310 return fuzz
312
311
313 (dopatch, gitpatches) = readgitpatch(patchname)
312 (dopatch, gitpatches) = readgitpatch(patchname)
313 for gp in gitpatches:
314 files[gp.path] = (gp.op, gp)
314
315
315 files, fuzz = {}, False
316 fuzz = False
316 if dopatch:
317 if dopatch:
317 if dopatch in ('filter', 'binary'):
318 if dopatch in ('filter', 'binary'):
318 patchname = dogitpatch(patchname, gitpatches, cwd=cwd)
319 patchname = dogitpatch(patchname, gitpatches, cwd=cwd)
319 try:
320 try:
320 if dopatch != 'binary':
321 if dopatch != 'binary':
321 files, fuzz = __patch(patchname)
322 fuzz = __patch(patchname)
322 finally:
323 finally:
323 if dopatch == 'filter':
324 if dopatch == 'filter':
324 os.unlink(patchname)
325 os.unlink(patchname)
325
326
326 for gp in gitpatches:
327 return fuzz
327 files[gp.path] = (gp.op, gp)
328
329 return (files, fuzz)
330
328
331 def diffopts(ui, opts={}):
329 def diffopts(ui, opts={}):
332 return mdiff.diffopts(
330 return mdiff.diffopts(
333 text=opts.get('text'),
331 text=opts.get('text'),
334 git=(opts.get('git') or
332 git=(opts.get('git') or
335 ui.configbool('diff', 'git', None)),
333 ui.configbool('diff', 'git', None)),
336 nodates=(opts.get('nodates') or
334 nodates=(opts.get('nodates') or
337 ui.configbool('diff', 'nodates', None)),
335 ui.configbool('diff', 'nodates', None)),
338 showfunc=(opts.get('show_function') or
336 showfunc=(opts.get('show_function') or
339 ui.configbool('diff', 'showfunc', None)),
337 ui.configbool('diff', 'showfunc', None)),
340 ignorews=(opts.get('ignore_all_space') or
338 ignorews=(opts.get('ignore_all_space') or
341 ui.configbool('diff', 'ignorews', None)),
339 ui.configbool('diff', 'ignorews', None)),
342 ignorewsamount=(opts.get('ignore_space_change') or
340 ignorewsamount=(opts.get('ignore_space_change') or
343 ui.configbool('diff', 'ignorewsamount', None)),
341 ui.configbool('diff', 'ignorewsamount', None)),
344 ignoreblanklines=(opts.get('ignore_blank_lines') or
342 ignoreblanklines=(opts.get('ignore_blank_lines') or
345 ui.configbool('diff', 'ignoreblanklines', None)))
343 ui.configbool('diff', 'ignoreblanklines', None)))
346
344
347 def updatedir(ui, repo, patches, wlock=None):
345 def updatedir(ui, repo, patches, wlock=None):
348 '''Update dirstate after patch application according to metadata'''
346 '''Update dirstate after patch application according to metadata'''
349 if not patches:
347 if not patches:
350 return
348 return
351 copies = []
349 copies = []
352 removes = []
350 removes = []
353 cfiles = patches.keys()
351 cfiles = patches.keys()
354 cwd = repo.getcwd()
352 cwd = repo.getcwd()
355 if cwd:
353 if cwd:
356 cfiles = [util.pathto(cwd, f) for f in patches.keys()]
354 cfiles = [util.pathto(cwd, f) for f in patches.keys()]
357 for f in patches:
355 for f in patches:
358 ctype, gp = patches[f]
356 ctype, gp = patches[f]
359 if ctype == 'RENAME':
357 if ctype == 'RENAME':
360 copies.append((gp.oldpath, gp.path, gp.copymod))
358 copies.append((gp.oldpath, gp.path, gp.copymod))
361 removes.append(gp.oldpath)
359 removes.append(gp.oldpath)
362 elif ctype == 'COPY':
360 elif ctype == 'COPY':
363 copies.append((gp.oldpath, gp.path, gp.copymod))
361 copies.append((gp.oldpath, gp.path, gp.copymod))
364 elif ctype == 'DELETE':
362 elif ctype == 'DELETE':
365 removes.append(gp.path)
363 removes.append(gp.path)
366 for src, dst, after in copies:
364 for src, dst, after in copies:
367 if not after:
365 if not after:
368 copyfile(src, dst, repo.root)
366 copyfile(src, dst, repo.root)
369 repo.copy(src, dst, wlock=wlock)
367 repo.copy(src, dst, wlock=wlock)
370 if removes:
368 if removes:
371 repo.remove(removes, True, wlock=wlock)
369 repo.remove(removes, True, wlock=wlock)
372 for f in patches:
370 for f in patches:
373 ctype, gp = patches[f]
371 ctype, gp = patches[f]
374 if gp and gp.mode:
372 if gp and gp.mode:
375 x = gp.mode & 0100 != 0
373 x = gp.mode & 0100 != 0
376 dst = os.path.join(repo.root, gp.path)
374 dst = os.path.join(repo.root, gp.path)
377 util.set_exec(dst, x)
375 util.set_exec(dst, x)
378 cmdutil.addremove(repo, cfiles, wlock=wlock)
376 cmdutil.addremove(repo, cfiles, wlock=wlock)
379 files = patches.keys()
377 files = patches.keys()
380 files.extend([r for r in removes if r not in files])
378 files.extend([r for r in removes if r not in files])
381 files.sort()
379 files.sort()
382
380
383 return files
381 return files
384
382
385 def b85diff(fp, to, tn):
383 def b85diff(fp, to, tn):
386 '''print base85-encoded binary diff'''
384 '''print base85-encoded binary diff'''
387 def gitindex(text):
385 def gitindex(text):
388 if not text:
386 if not text:
389 return '0' * 40
387 return '0' * 40
390 l = len(text)
388 l = len(text)
391 s = sha.new('blob %d\0' % l)
389 s = sha.new('blob %d\0' % l)
392 s.update(text)
390 s.update(text)
393 return s.hexdigest()
391 return s.hexdigest()
394
392
395 def fmtline(line):
393 def fmtline(line):
396 l = len(line)
394 l = len(line)
397 if l <= 26:
395 if l <= 26:
398 l = chr(ord('A') + l - 1)
396 l = chr(ord('A') + l - 1)
399 else:
397 else:
400 l = chr(l - 26 + ord('a') - 1)
398 l = chr(l - 26 + ord('a') - 1)
401 return '%c%s\n' % (l, base85.b85encode(line, True))
399 return '%c%s\n' % (l, base85.b85encode(line, True))
402
400
403 def chunk(text, csize=52):
401 def chunk(text, csize=52):
404 l = len(text)
402 l = len(text)
405 i = 0
403 i = 0
406 while i < l:
404 while i < l:
407 yield text[i:i+csize]
405 yield text[i:i+csize]
408 i += csize
406 i += csize
409
407
410 # TODO: deltas
408 # TODO: deltas
411 l = len(tn)
409 l = len(tn)
412 fp.write('index %s..%s\nGIT binary patch\nliteral %s\n' %
410 fp.write('index %s..%s\nGIT binary patch\nliteral %s\n' %
413 (gitindex(to), gitindex(tn), len(tn)))
411 (gitindex(to), gitindex(tn), len(tn)))
414
412
415 tn = ''.join([fmtline(l) for l in chunk(zlib.compress(tn))])
413 tn = ''.join([fmtline(l) for l in chunk(zlib.compress(tn))])
416 fp.write(tn)
414 fp.write(tn)
417 fp.write('\n')
415 fp.write('\n')
418
416
419 def diff(repo, node1=None, node2=None, files=None, match=util.always,
417 def diff(repo, node1=None, node2=None, files=None, match=util.always,
420 fp=None, changes=None, opts=None):
418 fp=None, changes=None, opts=None):
421 '''print diff of changes to files between two nodes, or node and
419 '''print diff of changes to files between two nodes, or node and
422 working directory.
420 working directory.
423
421
424 if node1 is None, use first dirstate parent instead.
422 if node1 is None, use first dirstate parent instead.
425 if node2 is None, compare node1 with working directory.'''
423 if node2 is None, compare node1 with working directory.'''
426
424
427 if opts is None:
425 if opts is None:
428 opts = mdiff.defaultopts
426 opts = mdiff.defaultopts
429 if fp is None:
427 if fp is None:
430 fp = repo.ui
428 fp = repo.ui
431
429
432 if not node1:
430 if not node1:
433 node1 = repo.dirstate.parents()[0]
431 node1 = repo.dirstate.parents()[0]
434
432
435 clcache = {}
433 clcache = {}
436 def getchangelog(n):
434 def getchangelog(n):
437 if n not in clcache:
435 if n not in clcache:
438 clcache[n] = repo.changelog.read(n)
436 clcache[n] = repo.changelog.read(n)
439 return clcache[n]
437 return clcache[n]
440 mcache = {}
438 mcache = {}
441 def getmanifest(n):
439 def getmanifest(n):
442 if n not in mcache:
440 if n not in mcache:
443 mcache[n] = repo.manifest.read(n)
441 mcache[n] = repo.manifest.read(n)
444 return mcache[n]
442 return mcache[n]
445 fcache = {}
443 fcache = {}
446 def getfile(f):
444 def getfile(f):
447 if f not in fcache:
445 if f not in fcache:
448 fcache[f] = repo.file(f)
446 fcache[f] = repo.file(f)
449 return fcache[f]
447 return fcache[f]
450
448
451 # reading the data for node1 early allows it to play nicely
449 # reading the data for node1 early allows it to play nicely
452 # with repo.status and the revlog cache.
450 # with repo.status and the revlog cache.
453 change = getchangelog(node1)
451 change = getchangelog(node1)
454 mmap = getmanifest(change[0])
452 mmap = getmanifest(change[0])
455 date1 = util.datestr(change[2])
453 date1 = util.datestr(change[2])
456
454
457 if not changes:
455 if not changes:
458 changes = repo.status(node1, node2, files, match=match)[:5]
456 changes = repo.status(node1, node2, files, match=match)[:5]
459 modified, added, removed, deleted, unknown = changes
457 modified, added, removed, deleted, unknown = changes
460 if files:
458 if files:
461 def filterfiles(filters):
459 def filterfiles(filters):
462 l = [x for x in filters if x in files]
460 l = [x for x in filters if x in files]
463
461
464 for t in files:
462 for t in files:
465 if not t.endswith("/"):
463 if not t.endswith("/"):
466 t += "/"
464 t += "/"
467 l += [x for x in filters if x.startswith(t)]
465 l += [x for x in filters if x.startswith(t)]
468 return l
466 return l
469
467
470 modified, added, removed = map(filterfiles, (modified, added, removed))
468 modified, added, removed = map(filterfiles, (modified, added, removed))
471
469
472 if not modified and not added and not removed:
470 if not modified and not added and not removed:
473 return
471 return
474
472
475 def renamedbetween(f, n1, n2):
473 def renamedbetween(f, n1, n2):
476 r1, r2 = map(repo.changelog.rev, (n1, n2))
474 r1, r2 = map(repo.changelog.rev, (n1, n2))
477 src = None
475 src = None
478 while r2 > r1:
476 while r2 > r1:
479 cl = getchangelog(n2)[0]
477 cl = getchangelog(n2)[0]
480 m = getmanifest(cl)
478 m = getmanifest(cl)
481 try:
479 try:
482 src = getfile(f).renamed(m[f])
480 src = getfile(f).renamed(m[f])
483 except KeyError:
481 except KeyError:
484 return None
482 return None
485 if src:
483 if src:
486 f = src[0]
484 f = src[0]
487 n2 = repo.changelog.parents(n2)[0]
485 n2 = repo.changelog.parents(n2)[0]
488 r2 = repo.changelog.rev(n2)
486 r2 = repo.changelog.rev(n2)
489 return src
487 return src
490
488
491 if node2:
489 if node2:
492 change = getchangelog(node2)
490 change = getchangelog(node2)
493 mmap2 = getmanifest(change[0])
491 mmap2 = getmanifest(change[0])
494 _date2 = util.datestr(change[2])
492 _date2 = util.datestr(change[2])
495 def date2(f):
493 def date2(f):
496 return _date2
494 return _date2
497 def read(f):
495 def read(f):
498 return getfile(f).read(mmap2[f])
496 return getfile(f).read(mmap2[f])
499 def renamed(f):
497 def renamed(f):
500 return renamedbetween(f, node1, node2)
498 return renamedbetween(f, node1, node2)
501 else:
499 else:
502 tz = util.makedate()[1]
500 tz = util.makedate()[1]
503 _date2 = util.datestr()
501 _date2 = util.datestr()
504 def date2(f):
502 def date2(f):
505 try:
503 try:
506 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
504 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
507 except OSError, err:
505 except OSError, err:
508 if err.errno != errno.ENOENT: raise
506 if err.errno != errno.ENOENT: raise
509 return _date2
507 return _date2
510 def read(f):
508 def read(f):
511 return repo.wread(f)
509 return repo.wread(f)
512 def renamed(f):
510 def renamed(f):
513 src = repo.dirstate.copied(f)
511 src = repo.dirstate.copied(f)
514 parent = repo.dirstate.parents()[0]
512 parent = repo.dirstate.parents()[0]
515 if src:
513 if src:
516 f = src[0]
514 f = src[0]
517 of = renamedbetween(f, node1, parent)
515 of = renamedbetween(f, node1, parent)
518 if of:
516 if of:
519 return of
517 return of
520 elif src:
518 elif src:
521 cl = getchangelog(parent)[0]
519 cl = getchangelog(parent)[0]
522 return (src, getmanifest(cl)[src])
520 return (src, getmanifest(cl)[src])
523 else:
521 else:
524 return None
522 return None
525
523
526 if repo.ui.quiet:
524 if repo.ui.quiet:
527 r = None
525 r = None
528 else:
526 else:
529 hexfunc = repo.ui.debugflag and hex or short
527 hexfunc = repo.ui.debugflag and hex or short
530 r = [hexfunc(node) for node in [node1, node2] if node]
528 r = [hexfunc(node) for node in [node1, node2] if node]
531
529
532 if opts.git:
530 if opts.git:
533 copied = {}
531 copied = {}
534 for f in added:
532 for f in added:
535 src = renamed(f)
533 src = renamed(f)
536 if src:
534 if src:
537 copied[f] = src
535 copied[f] = src
538 srcs = [x[1][0] for x in copied.items()]
536 srcs = [x[1][0] for x in copied.items()]
539
537
540 all = modified + added + removed
538 all = modified + added + removed
541 all.sort()
539 all.sort()
542 for f in all:
540 for f in all:
543 to = None
541 to = None
544 tn = None
542 tn = None
545 dodiff = True
543 dodiff = True
546 header = []
544 header = []
547 if f in mmap:
545 if f in mmap:
548 to = getfile(f).read(mmap[f])
546 to = getfile(f).read(mmap[f])
549 if f not in removed:
547 if f not in removed:
550 tn = read(f)
548 tn = read(f)
551 if opts.git:
549 if opts.git:
552 def gitmode(x):
550 def gitmode(x):
553 return x and '100755' or '100644'
551 return x and '100755' or '100644'
554 def addmodehdr(header, omode, nmode):
552 def addmodehdr(header, omode, nmode):
555 if omode != nmode:
553 if omode != nmode:
556 header.append('old mode %s\n' % omode)
554 header.append('old mode %s\n' % omode)
557 header.append('new mode %s\n' % nmode)
555 header.append('new mode %s\n' % nmode)
558
556
559 a, b = f, f
557 a, b = f, f
560 if f in added:
558 if f in added:
561 if node2:
559 if node2:
562 mode = gitmode(mmap2.execf(f))
560 mode = gitmode(mmap2.execf(f))
563 else:
561 else:
564 mode = gitmode(util.is_exec(repo.wjoin(f), None))
562 mode = gitmode(util.is_exec(repo.wjoin(f), None))
565 if f in copied:
563 if f in copied:
566 a, arev = copied[f]
564 a, arev = copied[f]
567 omode = gitmode(mmap.execf(a))
565 omode = gitmode(mmap.execf(a))
568 addmodehdr(header, omode, mode)
566 addmodehdr(header, omode, mode)
569 op = a in removed and 'rename' or 'copy'
567 op = a in removed and 'rename' or 'copy'
570 header.append('%s from %s\n' % (op, a))
568 header.append('%s from %s\n' % (op, a))
571 header.append('%s to %s\n' % (op, f))
569 header.append('%s to %s\n' % (op, f))
572 to = getfile(a).read(arev)
570 to = getfile(a).read(arev)
573 else:
571 else:
574 header.append('new file mode %s\n' % mode)
572 header.append('new file mode %s\n' % mode)
575 if util.binary(tn):
573 if util.binary(tn):
576 dodiff = 'binary'
574 dodiff = 'binary'
577 elif f in removed:
575 elif f in removed:
578 if f in srcs:
576 if f in srcs:
579 dodiff = False
577 dodiff = False
580 else:
578 else:
581 mode = gitmode(mmap.execf(f))
579 mode = gitmode(mmap.execf(f))
582 header.append('deleted file mode %s\n' % mode)
580 header.append('deleted file mode %s\n' % mode)
583 else:
581 else:
584 omode = gitmode(mmap.execf(f))
582 omode = gitmode(mmap.execf(f))
585 if node2:
583 if node2:
586 nmode = gitmode(mmap2.execf(f))
584 nmode = gitmode(mmap2.execf(f))
587 else:
585 else:
588 nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f)))
586 nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f)))
589 addmodehdr(header, omode, nmode)
587 addmodehdr(header, omode, nmode)
590 if util.binary(to) or util.binary(tn):
588 if util.binary(to) or util.binary(tn):
591 dodiff = 'binary'
589 dodiff = 'binary'
592 r = None
590 r = None
593 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
591 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
594 if dodiff == 'binary':
592 if dodiff == 'binary':
595 fp.write(''.join(header))
593 fp.write(''.join(header))
596 b85diff(fp, to, tn)
594 b85diff(fp, to, tn)
597 elif dodiff:
595 elif dodiff:
598 text = mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts)
596 text = mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts)
599 if text or len(header) > 1:
597 if text or len(header) > 1:
600 fp.write(''.join(header))
598 fp.write(''.join(header))
601 fp.write(text)
599 fp.write(text)
602
600
603 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
601 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
604 opts=None):
602 opts=None):
605 '''export changesets as hg patches.'''
603 '''export changesets as hg patches.'''
606
604
607 total = len(revs)
605 total = len(revs)
608 revwidth = max(map(len, revs))
606 revwidth = max(map(len, revs))
609
607
610 def single(node, seqno, fp):
608 def single(node, seqno, fp):
611 parents = [p for p in repo.changelog.parents(node) if p != nullid]
609 parents = [p for p in repo.changelog.parents(node) if p != nullid]
612 if switch_parent:
610 if switch_parent:
613 parents.reverse()
611 parents.reverse()
614 prev = (parents and parents[0]) or nullid
612 prev = (parents and parents[0]) or nullid
615 change = repo.changelog.read(node)
613 change = repo.changelog.read(node)
616
614
617 if not fp:
615 if not fp:
618 fp = cmdutil.make_file(repo, template, node, total=total,
616 fp = cmdutil.make_file(repo, template, node, total=total,
619 seqno=seqno, revwidth=revwidth)
617 seqno=seqno, revwidth=revwidth)
620 if fp not in (sys.stdout, repo.ui):
618 if fp not in (sys.stdout, repo.ui):
621 repo.ui.note("%s\n" % fp.name)
619 repo.ui.note("%s\n" % fp.name)
622
620
623 fp.write("# HG changeset patch\n")
621 fp.write("# HG changeset patch\n")
624 fp.write("# User %s\n" % change[1])
622 fp.write("# User %s\n" % change[1])
625 fp.write("# Date %d %d\n" % change[2])
623 fp.write("# Date %d %d\n" % change[2])
626 fp.write("# Node ID %s\n" % hex(node))
624 fp.write("# Node ID %s\n" % hex(node))
627 fp.write("# Parent %s\n" % hex(prev))
625 fp.write("# Parent %s\n" % hex(prev))
628 if len(parents) > 1:
626 if len(parents) > 1:
629 fp.write("# Parent %s\n" % hex(parents[1]))
627 fp.write("# Parent %s\n" % hex(parents[1]))
630 fp.write(change[4].rstrip())
628 fp.write(change[4].rstrip())
631 fp.write("\n\n")
629 fp.write("\n\n")
632
630
633 diff(repo, prev, node, fp=fp, opts=opts)
631 diff(repo, prev, node, fp=fp, opts=opts)
634 if fp not in (sys.stdout, repo.ui):
632 if fp not in (sys.stdout, repo.ui):
635 fp.close()
633 fp.close()
636
634
637 for seqno, cset in enumerate(revs):
635 for seqno, cset in enumerate(revs):
638 single(cset, seqno, fp)
636 single(cset, seqno, fp)
639
637
640 def diffstat(patchlines):
638 def diffstat(patchlines):
641 fd, name = tempfile.mkstemp(prefix="hg-patchbomb-", suffix=".txt")
639 fd, name = tempfile.mkstemp(prefix="hg-patchbomb-", suffix=".txt")
642 try:
640 try:
643 p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name)
641 p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name)
644 try:
642 try:
645 for line in patchlines: print >> p.tochild, line
643 for line in patchlines: print >> p.tochild, line
646 p.tochild.close()
644 p.tochild.close()
647 if p.wait(): return
645 if p.wait(): return
648 fp = os.fdopen(fd, 'r')
646 fp = os.fdopen(fd, 'r')
649 stat = []
647 stat = []
650 for line in fp: stat.append(line.lstrip())
648 for line in fp: stat.append(line.lstrip())
651 last = stat.pop()
649 last = stat.pop()
652 stat.insert(0, last)
650 stat.insert(0, last)
653 stat = ''.join(stat)
651 stat = ''.join(stat)
654 if stat.startswith('0 files'): raise ValueError
652 if stat.startswith('0 files'): raise ValueError
655 return stat
653 return stat
656 except: raise
654 except: raise
657 finally:
655 finally:
658 try: os.unlink(name)
656 try: os.unlink(name)
659 except: pass
657 except: pass
General Comments 0
You need to be logged in to leave comments. Login now