##// END OF EJS Templates
diffstat: with --git, mark binary files with Bin...
Brodie Rao -
r9642:7d17794f default
parent child Browse files
Show More
@@ -1,2693 +1,2694 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 '''manage a stack of patches
8 '''manage a stack of patches
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
25
26 add known patch to applied stack qpush
26 add known patch to applied stack qpush
27 remove patch from applied stack qpop
27 remove patch from applied stack qpop
28 refresh contents of top applied patch qrefresh
28 refresh contents of top applied patch qrefresh
29 '''
29 '''
30
30
31 from mercurial.i18n import _
31 from mercurial.i18n import _
32 from mercurial.node import bin, hex, short, nullid, nullrev
32 from mercurial.node import bin, hex, short, nullid, nullrev
33 from mercurial.lock import release
33 from mercurial.lock import release
34 from mercurial import commands, cmdutil, hg, patch, util
34 from mercurial import commands, cmdutil, hg, patch, util
35 from mercurial import repair, extensions, url, error
35 from mercurial import repair, extensions, url, error
36 import os, sys, re, errno
36 import os, sys, re, errno
37
37
38 commands.norepo += " qclone"
38 commands.norepo += " qclone"
39
39
40 # Patch names looks like unix-file names.
40 # Patch names looks like unix-file names.
41 # They must be joinable with queue directory and result in the patch path.
41 # They must be joinable with queue directory and result in the patch path.
42 normname = util.normpath
42 normname = util.normpath
43
43
44 class statusentry(object):
44 class statusentry(object):
45 def __init__(self, rev, name=None):
45 def __init__(self, rev, name=None):
46 if not name:
46 if not name:
47 fields = rev.split(':', 1)
47 fields = rev.split(':', 1)
48 if len(fields) == 2:
48 if len(fields) == 2:
49 self.rev, self.name = fields
49 self.rev, self.name = fields
50 else:
50 else:
51 self.rev, self.name = None, None
51 self.rev, self.name = None, None
52 else:
52 else:
53 self.rev, self.name = rev, name
53 self.rev, self.name = rev, name
54
54
55 def __str__(self):
55 def __str__(self):
56 return self.rev + ':' + self.name
56 return self.rev + ':' + self.name
57
57
58 class patchheader(object):
58 class patchheader(object):
59 def __init__(self, pf):
59 def __init__(self, pf):
60 def eatdiff(lines):
60 def eatdiff(lines):
61 while lines:
61 while lines:
62 l = lines[-1]
62 l = lines[-1]
63 if (l.startswith("diff -") or
63 if (l.startswith("diff -") or
64 l.startswith("Index:") or
64 l.startswith("Index:") or
65 l.startswith("===========")):
65 l.startswith("===========")):
66 del lines[-1]
66 del lines[-1]
67 else:
67 else:
68 break
68 break
69 def eatempty(lines):
69 def eatempty(lines):
70 while lines:
70 while lines:
71 l = lines[-1]
71 l = lines[-1]
72 if re.match('\s*$', l):
72 if re.match('\s*$', l):
73 del lines[-1]
73 del lines[-1]
74 else:
74 else:
75 break
75 break
76
76
77 message = []
77 message = []
78 comments = []
78 comments = []
79 user = None
79 user = None
80 date = None
80 date = None
81 format = None
81 format = None
82 subject = None
82 subject = None
83 diffstart = 0
83 diffstart = 0
84
84
85 for line in file(pf):
85 for line in file(pf):
86 line = line.rstrip()
86 line = line.rstrip()
87 if line.startswith('diff --git'):
87 if line.startswith('diff --git'):
88 diffstart = 2
88 diffstart = 2
89 break
89 break
90 if diffstart:
90 if diffstart:
91 if line.startswith('+++ '):
91 if line.startswith('+++ '):
92 diffstart = 2
92 diffstart = 2
93 break
93 break
94 if line.startswith("--- "):
94 if line.startswith("--- "):
95 diffstart = 1
95 diffstart = 1
96 continue
96 continue
97 elif format == "hgpatch":
97 elif format == "hgpatch":
98 # parse values when importing the result of an hg export
98 # parse values when importing the result of an hg export
99 if line.startswith("# User "):
99 if line.startswith("# User "):
100 user = line[7:]
100 user = line[7:]
101 elif line.startswith("# Date "):
101 elif line.startswith("# Date "):
102 date = line[7:]
102 date = line[7:]
103 elif not line.startswith("# ") and line:
103 elif not line.startswith("# ") and line:
104 message.append(line)
104 message.append(line)
105 format = None
105 format = None
106 elif line == '# HG changeset patch':
106 elif line == '# HG changeset patch':
107 message = []
107 message = []
108 format = "hgpatch"
108 format = "hgpatch"
109 elif (format != "tagdone" and (line.startswith("Subject: ") or
109 elif (format != "tagdone" and (line.startswith("Subject: ") or
110 line.startswith("subject: "))):
110 line.startswith("subject: "))):
111 subject = line[9:]
111 subject = line[9:]
112 format = "tag"
112 format = "tag"
113 elif (format != "tagdone" and (line.startswith("From: ") or
113 elif (format != "tagdone" and (line.startswith("From: ") or
114 line.startswith("from: "))):
114 line.startswith("from: "))):
115 user = line[6:]
115 user = line[6:]
116 format = "tag"
116 format = "tag"
117 elif format == "tag" and line == "":
117 elif format == "tag" and line == "":
118 # when looking for tags (subject: from: etc) they
118 # when looking for tags (subject: from: etc) they
119 # end once you find a blank line in the source
119 # end once you find a blank line in the source
120 format = "tagdone"
120 format = "tagdone"
121 elif message or line:
121 elif message or line:
122 message.append(line)
122 message.append(line)
123 comments.append(line)
123 comments.append(line)
124
124
125 eatdiff(message)
125 eatdiff(message)
126 eatdiff(comments)
126 eatdiff(comments)
127 eatempty(message)
127 eatempty(message)
128 eatempty(comments)
128 eatempty(comments)
129
129
130 # make sure message isn't empty
130 # make sure message isn't empty
131 if format and format.startswith("tag") and subject:
131 if format and format.startswith("tag") and subject:
132 message.insert(0, "")
132 message.insert(0, "")
133 message.insert(0, subject)
133 message.insert(0, subject)
134
134
135 self.message = message
135 self.message = message
136 self.comments = comments
136 self.comments = comments
137 self.user = user
137 self.user = user
138 self.date = date
138 self.date = date
139 self.haspatch = diffstart > 1
139 self.haspatch = diffstart > 1
140
140
141 def setuser(self, user):
141 def setuser(self, user):
142 if not self.updateheader(['From: ', '# User '], user):
142 if not self.updateheader(['From: ', '# User '], user):
143 try:
143 try:
144 patchheaderat = self.comments.index('# HG changeset patch')
144 patchheaderat = self.comments.index('# HG changeset patch')
145 self.comments.insert(patchheaderat + 1, '# User ' + user)
145 self.comments.insert(patchheaderat + 1, '# User ' + user)
146 except ValueError:
146 except ValueError:
147 if self._hasheader(['Date: ']):
147 if self._hasheader(['Date: ']):
148 self.comments = ['From: ' + user] + self.comments
148 self.comments = ['From: ' + user] + self.comments
149 else:
149 else:
150 tmp = ['# HG changeset patch', '# User ' + user, '']
150 tmp = ['# HG changeset patch', '# User ' + user, '']
151 self.comments = tmp + self.comments
151 self.comments = tmp + self.comments
152 self.user = user
152 self.user = user
153
153
154 def setdate(self, date):
154 def setdate(self, date):
155 if not self.updateheader(['Date: ', '# Date '], date):
155 if not self.updateheader(['Date: ', '# Date '], date):
156 try:
156 try:
157 patchheaderat = self.comments.index('# HG changeset patch')
157 patchheaderat = self.comments.index('# HG changeset patch')
158 self.comments.insert(patchheaderat + 1, '# Date ' + date)
158 self.comments.insert(patchheaderat + 1, '# Date ' + date)
159 except ValueError:
159 except ValueError:
160 if self._hasheader(['From: ']):
160 if self._hasheader(['From: ']):
161 self.comments = ['Date: ' + date] + self.comments
161 self.comments = ['Date: ' + date] + self.comments
162 else:
162 else:
163 tmp = ['# HG changeset patch', '# Date ' + date, '']
163 tmp = ['# HG changeset patch', '# Date ' + date, '']
164 self.comments = tmp + self.comments
164 self.comments = tmp + self.comments
165 self.date = date
165 self.date = date
166
166
167 def setmessage(self, message):
167 def setmessage(self, message):
168 if self.comments:
168 if self.comments:
169 self._delmsg()
169 self._delmsg()
170 self.message = [message]
170 self.message = [message]
171 self.comments += self.message
171 self.comments += self.message
172
172
173 def updateheader(self, prefixes, new):
173 def updateheader(self, prefixes, new):
174 '''Update all references to a field in the patch header.
174 '''Update all references to a field in the patch header.
175 Return whether the field is present.'''
175 Return whether the field is present.'''
176 res = False
176 res = False
177 for prefix in prefixes:
177 for prefix in prefixes:
178 for i in xrange(len(self.comments)):
178 for i in xrange(len(self.comments)):
179 if self.comments[i].startswith(prefix):
179 if self.comments[i].startswith(prefix):
180 self.comments[i] = prefix + new
180 self.comments[i] = prefix + new
181 res = True
181 res = True
182 break
182 break
183 return res
183 return res
184
184
185 def _hasheader(self, prefixes):
185 def _hasheader(self, prefixes):
186 '''Check if a header starts with any of the given prefixes.'''
186 '''Check if a header starts with any of the given prefixes.'''
187 for prefix in prefixes:
187 for prefix in prefixes:
188 for comment in self.comments:
188 for comment in self.comments:
189 if comment.startswith(prefix):
189 if comment.startswith(prefix):
190 return True
190 return True
191 return False
191 return False
192
192
193 def __str__(self):
193 def __str__(self):
194 if not self.comments:
194 if not self.comments:
195 return ''
195 return ''
196 return '\n'.join(self.comments) + '\n\n'
196 return '\n'.join(self.comments) + '\n\n'
197
197
198 def _delmsg(self):
198 def _delmsg(self):
199 '''Remove existing message, keeping the rest of the comments fields.
199 '''Remove existing message, keeping the rest of the comments fields.
200 If comments contains 'subject: ', message will prepend
200 If comments contains 'subject: ', message will prepend
201 the field and a blank line.'''
201 the field and a blank line.'''
202 if self.message:
202 if self.message:
203 subj = 'subject: ' + self.message[0].lower()
203 subj = 'subject: ' + self.message[0].lower()
204 for i in xrange(len(self.comments)):
204 for i in xrange(len(self.comments)):
205 if subj == self.comments[i].lower():
205 if subj == self.comments[i].lower():
206 del self.comments[i]
206 del self.comments[i]
207 self.message = self.message[2:]
207 self.message = self.message[2:]
208 break
208 break
209 ci = 0
209 ci = 0
210 for mi in self.message:
210 for mi in self.message:
211 while mi != self.comments[ci]:
211 while mi != self.comments[ci]:
212 ci += 1
212 ci += 1
213 del self.comments[ci]
213 del self.comments[ci]
214
214
215 class queue(object):
215 class queue(object):
216 def __init__(self, ui, path, patchdir=None):
216 def __init__(self, ui, path, patchdir=None):
217 self.basepath = path
217 self.basepath = path
218 self.path = patchdir or os.path.join(path, "patches")
218 self.path = patchdir or os.path.join(path, "patches")
219 self.opener = util.opener(self.path)
219 self.opener = util.opener(self.path)
220 self.ui = ui
220 self.ui = ui
221 self.applied_dirty = 0
221 self.applied_dirty = 0
222 self.series_dirty = 0
222 self.series_dirty = 0
223 self.series_path = "series"
223 self.series_path = "series"
224 self.status_path = "status"
224 self.status_path = "status"
225 self.guards_path = "guards"
225 self.guards_path = "guards"
226 self.active_guards = None
226 self.active_guards = None
227 self.guards_dirty = False
227 self.guards_dirty = False
228 self._diffopts = None
228 self._diffopts = None
229
229
230 @util.propertycache
230 @util.propertycache
231 def applied(self):
231 def applied(self):
232 if os.path.exists(self.join(self.status_path)):
232 if os.path.exists(self.join(self.status_path)):
233 lines = self.opener(self.status_path).read().splitlines()
233 lines = self.opener(self.status_path).read().splitlines()
234 return [statusentry(l) for l in lines]
234 return [statusentry(l) for l in lines]
235 return []
235 return []
236
236
237 @util.propertycache
237 @util.propertycache
238 def full_series(self):
238 def full_series(self):
239 if os.path.exists(self.join(self.series_path)):
239 if os.path.exists(self.join(self.series_path)):
240 return self.opener(self.series_path).read().splitlines()
240 return self.opener(self.series_path).read().splitlines()
241 return []
241 return []
242
242
243 @util.propertycache
243 @util.propertycache
244 def series(self):
244 def series(self):
245 self.parse_series()
245 self.parse_series()
246 return self.series
246 return self.series
247
247
248 @util.propertycache
248 @util.propertycache
249 def series_guards(self):
249 def series_guards(self):
250 self.parse_series()
250 self.parse_series()
251 return self.series_guards
251 return self.series_guards
252
252
253 def invalidate(self):
253 def invalidate(self):
254 for a in 'applied full_series series series_guards'.split():
254 for a in 'applied full_series series series_guards'.split():
255 if a in self.__dict__:
255 if a in self.__dict__:
256 delattr(self, a)
256 delattr(self, a)
257 self.applied_dirty = 0
257 self.applied_dirty = 0
258 self.series_dirty = 0
258 self.series_dirty = 0
259 self.guards_dirty = False
259 self.guards_dirty = False
260 self.active_guards = None
260 self.active_guards = None
261
261
262 def diffopts(self):
262 def diffopts(self):
263 if self._diffopts is None:
263 if self._diffopts is None:
264 self._diffopts = patch.diffopts(self.ui)
264 self._diffopts = patch.diffopts(self.ui)
265 return self._diffopts
265 return self._diffopts
266
266
267 def join(self, *p):
267 def join(self, *p):
268 return os.path.join(self.path, *p)
268 return os.path.join(self.path, *p)
269
269
270 def find_series(self, patch):
270 def find_series(self, patch):
271 pre = re.compile("(\s*)([^#]+)")
271 pre = re.compile("(\s*)([^#]+)")
272 index = 0
272 index = 0
273 for l in self.full_series:
273 for l in self.full_series:
274 m = pre.match(l)
274 m = pre.match(l)
275 if m:
275 if m:
276 s = m.group(2)
276 s = m.group(2)
277 s = s.rstrip()
277 s = s.rstrip()
278 if s == patch:
278 if s == patch:
279 return index
279 return index
280 index += 1
280 index += 1
281 return None
281 return None
282
282
283 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
283 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
284
284
285 def parse_series(self):
285 def parse_series(self):
286 self.series = []
286 self.series = []
287 self.series_guards = []
287 self.series_guards = []
288 for l in self.full_series:
288 for l in self.full_series:
289 h = l.find('#')
289 h = l.find('#')
290 if h == -1:
290 if h == -1:
291 patch = l
291 patch = l
292 comment = ''
292 comment = ''
293 elif h == 0:
293 elif h == 0:
294 continue
294 continue
295 else:
295 else:
296 patch = l[:h]
296 patch = l[:h]
297 comment = l[h:]
297 comment = l[h:]
298 patch = patch.strip()
298 patch = patch.strip()
299 if patch:
299 if patch:
300 if patch in self.series:
300 if patch in self.series:
301 raise util.Abort(_('%s appears more than once in %s') %
301 raise util.Abort(_('%s appears more than once in %s') %
302 (patch, self.join(self.series_path)))
302 (patch, self.join(self.series_path)))
303 self.series.append(patch)
303 self.series.append(patch)
304 self.series_guards.append(self.guard_re.findall(comment))
304 self.series_guards.append(self.guard_re.findall(comment))
305
305
306 def check_guard(self, guard):
306 def check_guard(self, guard):
307 if not guard:
307 if not guard:
308 return _('guard cannot be an empty string')
308 return _('guard cannot be an empty string')
309 bad_chars = '# \t\r\n\f'
309 bad_chars = '# \t\r\n\f'
310 first = guard[0]
310 first = guard[0]
311 if first in '-+':
311 if first in '-+':
312 return (_('guard %r starts with invalid character: %r') %
312 return (_('guard %r starts with invalid character: %r') %
313 (guard, first))
313 (guard, first))
314 for c in bad_chars:
314 for c in bad_chars:
315 if c in guard:
315 if c in guard:
316 return _('invalid character in guard %r: %r') % (guard, c)
316 return _('invalid character in guard %r: %r') % (guard, c)
317
317
318 def set_active(self, guards):
318 def set_active(self, guards):
319 for guard in guards:
319 for guard in guards:
320 bad = self.check_guard(guard)
320 bad = self.check_guard(guard)
321 if bad:
321 if bad:
322 raise util.Abort(bad)
322 raise util.Abort(bad)
323 guards = sorted(set(guards))
323 guards = sorted(set(guards))
324 self.ui.debug('active guards: %s\n' % ' '.join(guards))
324 self.ui.debug('active guards: %s\n' % ' '.join(guards))
325 self.active_guards = guards
325 self.active_guards = guards
326 self.guards_dirty = True
326 self.guards_dirty = True
327
327
328 def active(self):
328 def active(self):
329 if self.active_guards is None:
329 if self.active_guards is None:
330 self.active_guards = []
330 self.active_guards = []
331 try:
331 try:
332 guards = self.opener(self.guards_path).read().split()
332 guards = self.opener(self.guards_path).read().split()
333 except IOError, err:
333 except IOError, err:
334 if err.errno != errno.ENOENT: raise
334 if err.errno != errno.ENOENT: raise
335 guards = []
335 guards = []
336 for i, guard in enumerate(guards):
336 for i, guard in enumerate(guards):
337 bad = self.check_guard(guard)
337 bad = self.check_guard(guard)
338 if bad:
338 if bad:
339 self.ui.warn('%s:%d: %s\n' %
339 self.ui.warn('%s:%d: %s\n' %
340 (self.join(self.guards_path), i + 1, bad))
340 (self.join(self.guards_path), i + 1, bad))
341 else:
341 else:
342 self.active_guards.append(guard)
342 self.active_guards.append(guard)
343 return self.active_guards
343 return self.active_guards
344
344
345 def set_guards(self, idx, guards):
345 def set_guards(self, idx, guards):
346 for g in guards:
346 for g in guards:
347 if len(g) < 2:
347 if len(g) < 2:
348 raise util.Abort(_('guard %r too short') % g)
348 raise util.Abort(_('guard %r too short') % g)
349 if g[0] not in '-+':
349 if g[0] not in '-+':
350 raise util.Abort(_('guard %r starts with invalid char') % g)
350 raise util.Abort(_('guard %r starts with invalid char') % g)
351 bad = self.check_guard(g[1:])
351 bad = self.check_guard(g[1:])
352 if bad:
352 if bad:
353 raise util.Abort(bad)
353 raise util.Abort(bad)
354 drop = self.guard_re.sub('', self.full_series[idx])
354 drop = self.guard_re.sub('', self.full_series[idx])
355 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
355 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
356 self.parse_series()
356 self.parse_series()
357 self.series_dirty = True
357 self.series_dirty = True
358
358
359 def pushable(self, idx):
359 def pushable(self, idx):
360 if isinstance(idx, str):
360 if isinstance(idx, str):
361 idx = self.series.index(idx)
361 idx = self.series.index(idx)
362 patchguards = self.series_guards[idx]
362 patchguards = self.series_guards[idx]
363 if not patchguards:
363 if not patchguards:
364 return True, None
364 return True, None
365 guards = self.active()
365 guards = self.active()
366 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
366 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
367 if exactneg:
367 if exactneg:
368 return False, exactneg[0]
368 return False, exactneg[0]
369 pos = [g for g in patchguards if g[0] == '+']
369 pos = [g for g in patchguards if g[0] == '+']
370 exactpos = [g for g in pos if g[1:] in guards]
370 exactpos = [g for g in pos if g[1:] in guards]
371 if pos:
371 if pos:
372 if exactpos:
372 if exactpos:
373 return True, exactpos[0]
373 return True, exactpos[0]
374 return False, pos
374 return False, pos
375 return True, ''
375 return True, ''
376
376
377 def explain_pushable(self, idx, all_patches=False):
377 def explain_pushable(self, idx, all_patches=False):
378 write = all_patches and self.ui.write or self.ui.warn
378 write = all_patches and self.ui.write or self.ui.warn
379 if all_patches or self.ui.verbose:
379 if all_patches or self.ui.verbose:
380 if isinstance(idx, str):
380 if isinstance(idx, str):
381 idx = self.series.index(idx)
381 idx = self.series.index(idx)
382 pushable, why = self.pushable(idx)
382 pushable, why = self.pushable(idx)
383 if all_patches and pushable:
383 if all_patches and pushable:
384 if why is None:
384 if why is None:
385 write(_('allowing %s - no guards in effect\n') %
385 write(_('allowing %s - no guards in effect\n') %
386 self.series[idx])
386 self.series[idx])
387 else:
387 else:
388 if not why:
388 if not why:
389 write(_('allowing %s - no matching negative guards\n') %
389 write(_('allowing %s - no matching negative guards\n') %
390 self.series[idx])
390 self.series[idx])
391 else:
391 else:
392 write(_('allowing %s - guarded by %r\n') %
392 write(_('allowing %s - guarded by %r\n') %
393 (self.series[idx], why))
393 (self.series[idx], why))
394 if not pushable:
394 if not pushable:
395 if why:
395 if why:
396 write(_('skipping %s - guarded by %r\n') %
396 write(_('skipping %s - guarded by %r\n') %
397 (self.series[idx], why))
397 (self.series[idx], why))
398 else:
398 else:
399 write(_('skipping %s - no matching guards\n') %
399 write(_('skipping %s - no matching guards\n') %
400 self.series[idx])
400 self.series[idx])
401
401
402 def save_dirty(self):
402 def save_dirty(self):
403 def write_list(items, path):
403 def write_list(items, path):
404 fp = self.opener(path, 'w')
404 fp = self.opener(path, 'w')
405 for i in items:
405 for i in items:
406 fp.write("%s\n" % i)
406 fp.write("%s\n" % i)
407 fp.close()
407 fp.close()
408 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
408 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
409 if self.series_dirty: write_list(self.full_series, self.series_path)
409 if self.series_dirty: write_list(self.full_series, self.series_path)
410 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
410 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
411
411
412 def removeundo(self, repo):
412 def removeundo(self, repo):
413 undo = repo.sjoin('undo')
413 undo = repo.sjoin('undo')
414 if not os.path.exists(undo):
414 if not os.path.exists(undo):
415 return
415 return
416 try:
416 try:
417 os.unlink(undo)
417 os.unlink(undo)
418 except OSError, inst:
418 except OSError, inst:
419 self.ui.warn(_('error removing undo: %s\n') % str(inst))
419 self.ui.warn(_('error removing undo: %s\n') % str(inst))
420
420
421 def printdiff(self, repo, node1, node2=None, files=None,
421 def printdiff(self, repo, node1, node2=None, files=None,
422 fp=None, changes=None, opts={}):
422 fp=None, changes=None, opts={}):
423 stat = opts.get('stat')
423 stat = opts.get('stat')
424 if stat:
424 if stat:
425 opts['unified'] = '0'
425 opts['unified'] = '0'
426
426
427 m = cmdutil.match(repo, files, opts)
427 m = cmdutil.match(repo, files, opts)
428 chunks = patch.diff(repo, node1, node2, m, changes, self.diffopts())
428 chunks = patch.diff(repo, node1, node2, m, changes, self.diffopts())
429 write = fp is None and repo.ui.write or fp.write
429 write = fp is None and repo.ui.write or fp.write
430 if stat:
430 if stat:
431 width = self.ui.interactive() and util.termwidth() or 80
431 width = self.ui.interactive() and util.termwidth() or 80
432 write(patch.diffstat(util.iterlines(chunks), width=width))
432 write(patch.diffstat(util.iterlines(chunks), width=width,
433 git=self.diffopts().git))
433 else:
434 else:
434 for chunk in chunks:
435 for chunk in chunks:
435 write(chunk)
436 write(chunk)
436
437
437 def mergeone(self, repo, mergeq, head, patch, rev):
438 def mergeone(self, repo, mergeq, head, patch, rev):
438 # first try just applying the patch
439 # first try just applying the patch
439 (err, n) = self.apply(repo, [ patch ], update_status=False,
440 (err, n) = self.apply(repo, [ patch ], update_status=False,
440 strict=True, merge=rev)
441 strict=True, merge=rev)
441
442
442 if err == 0:
443 if err == 0:
443 return (err, n)
444 return (err, n)
444
445
445 if n is None:
446 if n is None:
446 raise util.Abort(_("apply failed for patch %s") % patch)
447 raise util.Abort(_("apply failed for patch %s") % patch)
447
448
448 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
449 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
449
450
450 # apply failed, strip away that rev and merge.
451 # apply failed, strip away that rev and merge.
451 hg.clean(repo, head)
452 hg.clean(repo, head)
452 self.strip(repo, n, update=False, backup='strip')
453 self.strip(repo, n, update=False, backup='strip')
453
454
454 ctx = repo[rev]
455 ctx = repo[rev]
455 ret = hg.merge(repo, rev)
456 ret = hg.merge(repo, rev)
456 if ret:
457 if ret:
457 raise util.Abort(_("update returned %d") % ret)
458 raise util.Abort(_("update returned %d") % ret)
458 n = repo.commit(ctx.description(), ctx.user(), force=True)
459 n = repo.commit(ctx.description(), ctx.user(), force=True)
459 if n is None:
460 if n is None:
460 raise util.Abort(_("repo commit failed"))
461 raise util.Abort(_("repo commit failed"))
461 try:
462 try:
462 ph = patchheader(mergeq.join(patch))
463 ph = patchheader(mergeq.join(patch))
463 except:
464 except:
464 raise util.Abort(_("unable to read %s") % patch)
465 raise util.Abort(_("unable to read %s") % patch)
465
466
466 patchf = self.opener(patch, "w")
467 patchf = self.opener(patch, "w")
467 comments = str(ph)
468 comments = str(ph)
468 if comments:
469 if comments:
469 patchf.write(comments)
470 patchf.write(comments)
470 self.printdiff(repo, head, n, fp=patchf)
471 self.printdiff(repo, head, n, fp=patchf)
471 patchf.close()
472 patchf.close()
472 self.removeundo(repo)
473 self.removeundo(repo)
473 return (0, n)
474 return (0, n)
474
475
475 def qparents(self, repo, rev=None):
476 def qparents(self, repo, rev=None):
476 if rev is None:
477 if rev is None:
477 (p1, p2) = repo.dirstate.parents()
478 (p1, p2) = repo.dirstate.parents()
478 if p2 == nullid:
479 if p2 == nullid:
479 return p1
480 return p1
480 if len(self.applied) == 0:
481 if len(self.applied) == 0:
481 return None
482 return None
482 return bin(self.applied[-1].rev)
483 return bin(self.applied[-1].rev)
483 pp = repo.changelog.parents(rev)
484 pp = repo.changelog.parents(rev)
484 if pp[1] != nullid:
485 if pp[1] != nullid:
485 arevs = [ x.rev for x in self.applied ]
486 arevs = [ x.rev for x in self.applied ]
486 p0 = hex(pp[0])
487 p0 = hex(pp[0])
487 p1 = hex(pp[1])
488 p1 = hex(pp[1])
488 if p0 in arevs:
489 if p0 in arevs:
489 return pp[0]
490 return pp[0]
490 if p1 in arevs:
491 if p1 in arevs:
491 return pp[1]
492 return pp[1]
492 return pp[0]
493 return pp[0]
493
494
494 def mergepatch(self, repo, mergeq, series):
495 def mergepatch(self, repo, mergeq, series):
495 if len(self.applied) == 0:
496 if len(self.applied) == 0:
496 # each of the patches merged in will have two parents. This
497 # each of the patches merged in will have two parents. This
497 # can confuse the qrefresh, qdiff, and strip code because it
498 # can confuse the qrefresh, qdiff, and strip code because it
498 # needs to know which parent is actually in the patch queue.
499 # needs to know which parent is actually in the patch queue.
499 # so, we insert a merge marker with only one parent. This way
500 # so, we insert a merge marker with only one parent. This way
500 # the first patch in the queue is never a merge patch
501 # the first patch in the queue is never a merge patch
501 #
502 #
502 pname = ".hg.patches.merge.marker"
503 pname = ".hg.patches.merge.marker"
503 n = repo.commit('[mq]: merge marker', force=True)
504 n = repo.commit('[mq]: merge marker', force=True)
504 self.removeundo(repo)
505 self.removeundo(repo)
505 self.applied.append(statusentry(hex(n), pname))
506 self.applied.append(statusentry(hex(n), pname))
506 self.applied_dirty = 1
507 self.applied_dirty = 1
507
508
508 head = self.qparents(repo)
509 head = self.qparents(repo)
509
510
510 for patch in series:
511 for patch in series:
511 patch = mergeq.lookup(patch, strict=True)
512 patch = mergeq.lookup(patch, strict=True)
512 if not patch:
513 if not patch:
513 self.ui.warn(_("patch %s does not exist\n") % patch)
514 self.ui.warn(_("patch %s does not exist\n") % patch)
514 return (1, None)
515 return (1, None)
515 pushable, reason = self.pushable(patch)
516 pushable, reason = self.pushable(patch)
516 if not pushable:
517 if not pushable:
517 self.explain_pushable(patch, all_patches=True)
518 self.explain_pushable(patch, all_patches=True)
518 continue
519 continue
519 info = mergeq.isapplied(patch)
520 info = mergeq.isapplied(patch)
520 if not info:
521 if not info:
521 self.ui.warn(_("patch %s is not applied\n") % patch)
522 self.ui.warn(_("patch %s is not applied\n") % patch)
522 return (1, None)
523 return (1, None)
523 rev = bin(info[1])
524 rev = bin(info[1])
524 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
525 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
525 if head:
526 if head:
526 self.applied.append(statusentry(hex(head), patch))
527 self.applied.append(statusentry(hex(head), patch))
527 self.applied_dirty = 1
528 self.applied_dirty = 1
528 if err:
529 if err:
529 return (err, head)
530 return (err, head)
530 self.save_dirty()
531 self.save_dirty()
531 return (0, head)
532 return (0, head)
532
533
533 def patch(self, repo, patchfile):
534 def patch(self, repo, patchfile):
534 '''Apply patchfile to the working directory.
535 '''Apply patchfile to the working directory.
535 patchfile: name of patch file'''
536 patchfile: name of patch file'''
536 files = {}
537 files = {}
537 try:
538 try:
538 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
539 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
539 files=files, eolmode=None)
540 files=files, eolmode=None)
540 except Exception, inst:
541 except Exception, inst:
541 self.ui.note(str(inst) + '\n')
542 self.ui.note(str(inst) + '\n')
542 if not self.ui.verbose:
543 if not self.ui.verbose:
543 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
544 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
544 return (False, files, False)
545 return (False, files, False)
545
546
546 return (True, files, fuzz)
547 return (True, files, fuzz)
547
548
548 def apply(self, repo, series, list=False, update_status=True,
549 def apply(self, repo, series, list=False, update_status=True,
549 strict=False, patchdir=None, merge=None, all_files={}):
550 strict=False, patchdir=None, merge=None, all_files={}):
550 wlock = lock = tr = None
551 wlock = lock = tr = None
551 try:
552 try:
552 wlock = repo.wlock()
553 wlock = repo.wlock()
553 lock = repo.lock()
554 lock = repo.lock()
554 tr = repo.transaction()
555 tr = repo.transaction()
555 try:
556 try:
556 ret = self._apply(repo, series, list, update_status,
557 ret = self._apply(repo, series, list, update_status,
557 strict, patchdir, merge, all_files=all_files)
558 strict, patchdir, merge, all_files=all_files)
558 tr.close()
559 tr.close()
559 self.save_dirty()
560 self.save_dirty()
560 return ret
561 return ret
561 except:
562 except:
562 try:
563 try:
563 tr.abort()
564 tr.abort()
564 finally:
565 finally:
565 repo.invalidate()
566 repo.invalidate()
566 repo.dirstate.invalidate()
567 repo.dirstate.invalidate()
567 raise
568 raise
568 finally:
569 finally:
569 del tr
570 del tr
570 release(lock, wlock)
571 release(lock, wlock)
571 self.removeundo(repo)
572 self.removeundo(repo)
572
573
573 def _apply(self, repo, series, list=False, update_status=True,
574 def _apply(self, repo, series, list=False, update_status=True,
574 strict=False, patchdir=None, merge=None, all_files={}):
575 strict=False, patchdir=None, merge=None, all_files={}):
575 '''returns (error, hash)
576 '''returns (error, hash)
576 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
577 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
577 # TODO unify with commands.py
578 # TODO unify with commands.py
578 if not patchdir:
579 if not patchdir:
579 patchdir = self.path
580 patchdir = self.path
580 err = 0
581 err = 0
581 n = None
582 n = None
582 for patchname in series:
583 for patchname in series:
583 pushable, reason = self.pushable(patchname)
584 pushable, reason = self.pushable(patchname)
584 if not pushable:
585 if not pushable:
585 self.explain_pushable(patchname, all_patches=True)
586 self.explain_pushable(patchname, all_patches=True)
586 continue
587 continue
587 self.ui.status(_("applying %s\n") % patchname)
588 self.ui.status(_("applying %s\n") % patchname)
588 pf = os.path.join(patchdir, patchname)
589 pf = os.path.join(patchdir, patchname)
589
590
590 try:
591 try:
591 ph = patchheader(self.join(patchname))
592 ph = patchheader(self.join(patchname))
592 except:
593 except:
593 self.ui.warn(_("unable to read %s\n") % patchname)
594 self.ui.warn(_("unable to read %s\n") % patchname)
594 err = 1
595 err = 1
595 break
596 break
596
597
597 message = ph.message
598 message = ph.message
598 if not message:
599 if not message:
599 message = _("imported patch %s\n") % patchname
600 message = _("imported patch %s\n") % patchname
600 else:
601 else:
601 if list:
602 if list:
602 message.append(_("\nimported patch %s") % patchname)
603 message.append(_("\nimported patch %s") % patchname)
603 message = '\n'.join(message)
604 message = '\n'.join(message)
604
605
605 if ph.haspatch:
606 if ph.haspatch:
606 (patcherr, files, fuzz) = self.patch(repo, pf)
607 (patcherr, files, fuzz) = self.patch(repo, pf)
607 all_files.update(files)
608 all_files.update(files)
608 patcherr = not patcherr
609 patcherr = not patcherr
609 else:
610 else:
610 self.ui.warn(_("patch %s is empty\n") % patchname)
611 self.ui.warn(_("patch %s is empty\n") % patchname)
611 patcherr, files, fuzz = 0, [], 0
612 patcherr, files, fuzz = 0, [], 0
612
613
613 if merge and files:
614 if merge and files:
614 # Mark as removed/merged and update dirstate parent info
615 # Mark as removed/merged and update dirstate parent info
615 removed = []
616 removed = []
616 merged = []
617 merged = []
617 for f in files:
618 for f in files:
618 if os.path.exists(repo.wjoin(f)):
619 if os.path.exists(repo.wjoin(f)):
619 merged.append(f)
620 merged.append(f)
620 else:
621 else:
621 removed.append(f)
622 removed.append(f)
622 for f in removed:
623 for f in removed:
623 repo.dirstate.remove(f)
624 repo.dirstate.remove(f)
624 for f in merged:
625 for f in merged:
625 repo.dirstate.merge(f)
626 repo.dirstate.merge(f)
626 p1, p2 = repo.dirstate.parents()
627 p1, p2 = repo.dirstate.parents()
627 repo.dirstate.setparents(p1, merge)
628 repo.dirstate.setparents(p1, merge)
628
629
629 files = patch.updatedir(self.ui, repo, files)
630 files = patch.updatedir(self.ui, repo, files)
630 match = cmdutil.matchfiles(repo, files or [])
631 match = cmdutil.matchfiles(repo, files or [])
631 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
632 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
632
633
633 if n is None:
634 if n is None:
634 raise util.Abort(_("repo commit failed"))
635 raise util.Abort(_("repo commit failed"))
635
636
636 if update_status:
637 if update_status:
637 self.applied.append(statusentry(hex(n), patchname))
638 self.applied.append(statusentry(hex(n), patchname))
638
639
639 if patcherr:
640 if patcherr:
640 self.ui.warn(_("patch failed, rejects left in working dir\n"))
641 self.ui.warn(_("patch failed, rejects left in working dir\n"))
641 err = 2
642 err = 2
642 break
643 break
643
644
644 if fuzz and strict:
645 if fuzz and strict:
645 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
646 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
646 err = 3
647 err = 3
647 break
648 break
648 return (err, n)
649 return (err, n)
649
650
650 def _cleanup(self, patches, numrevs, keep=False):
651 def _cleanup(self, patches, numrevs, keep=False):
651 if not keep:
652 if not keep:
652 r = self.qrepo()
653 r = self.qrepo()
653 if r:
654 if r:
654 r.remove(patches, True)
655 r.remove(patches, True)
655 else:
656 else:
656 for p in patches:
657 for p in patches:
657 os.unlink(self.join(p))
658 os.unlink(self.join(p))
658
659
659 if numrevs:
660 if numrevs:
660 del self.applied[:numrevs]
661 del self.applied[:numrevs]
661 self.applied_dirty = 1
662 self.applied_dirty = 1
662
663
663 for i in sorted([self.find_series(p) for p in patches], reverse=True):
664 for i in sorted([self.find_series(p) for p in patches], reverse=True):
664 del self.full_series[i]
665 del self.full_series[i]
665 self.parse_series()
666 self.parse_series()
666 self.series_dirty = 1
667 self.series_dirty = 1
667
668
668 def _revpatches(self, repo, revs):
669 def _revpatches(self, repo, revs):
669 firstrev = repo[self.applied[0].rev].rev()
670 firstrev = repo[self.applied[0].rev].rev()
670 patches = []
671 patches = []
671 for i, rev in enumerate(revs):
672 for i, rev in enumerate(revs):
672
673
673 if rev < firstrev:
674 if rev < firstrev:
674 raise util.Abort(_('revision %d is not managed') % rev)
675 raise util.Abort(_('revision %d is not managed') % rev)
675
676
676 ctx = repo[rev]
677 ctx = repo[rev]
677 base = bin(self.applied[i].rev)
678 base = bin(self.applied[i].rev)
678 if ctx.node() != base:
679 if ctx.node() != base:
679 msg = _('cannot delete revision %d above applied patches')
680 msg = _('cannot delete revision %d above applied patches')
680 raise util.Abort(msg % rev)
681 raise util.Abort(msg % rev)
681
682
682 patch = self.applied[i].name
683 patch = self.applied[i].name
683 for fmt in ('[mq]: %s', 'imported patch %s'):
684 for fmt in ('[mq]: %s', 'imported patch %s'):
684 if ctx.description() == fmt % patch:
685 if ctx.description() == fmt % patch:
685 msg = _('patch %s finalized without changeset message\n')
686 msg = _('patch %s finalized without changeset message\n')
686 repo.ui.status(msg % patch)
687 repo.ui.status(msg % patch)
687 break
688 break
688
689
689 patches.append(patch)
690 patches.append(patch)
690 return patches
691 return patches
691
692
692 def finish(self, repo, revs):
693 def finish(self, repo, revs):
693 patches = self._revpatches(repo, sorted(revs))
694 patches = self._revpatches(repo, sorted(revs))
694 self._cleanup(patches, len(patches))
695 self._cleanup(patches, len(patches))
695
696
696 def delete(self, repo, patches, opts):
697 def delete(self, repo, patches, opts):
697 if not patches and not opts.get('rev'):
698 if not patches and not opts.get('rev'):
698 raise util.Abort(_('qdelete requires at least one revision or '
699 raise util.Abort(_('qdelete requires at least one revision or '
699 'patch name'))
700 'patch name'))
700
701
701 realpatches = []
702 realpatches = []
702 for patch in patches:
703 for patch in patches:
703 patch = self.lookup(patch, strict=True)
704 patch = self.lookup(patch, strict=True)
704 info = self.isapplied(patch)
705 info = self.isapplied(patch)
705 if info:
706 if info:
706 raise util.Abort(_("cannot delete applied patch %s") % patch)
707 raise util.Abort(_("cannot delete applied patch %s") % patch)
707 if patch not in self.series:
708 if patch not in self.series:
708 raise util.Abort(_("patch %s not in series file") % patch)
709 raise util.Abort(_("patch %s not in series file") % patch)
709 realpatches.append(patch)
710 realpatches.append(patch)
710
711
711 numrevs = 0
712 numrevs = 0
712 if opts.get('rev'):
713 if opts.get('rev'):
713 if not self.applied:
714 if not self.applied:
714 raise util.Abort(_('no patches applied'))
715 raise util.Abort(_('no patches applied'))
715 revs = cmdutil.revrange(repo, opts['rev'])
716 revs = cmdutil.revrange(repo, opts['rev'])
716 if len(revs) > 1 and revs[0] > revs[1]:
717 if len(revs) > 1 and revs[0] > revs[1]:
717 revs.reverse()
718 revs.reverse()
718 revpatches = self._revpatches(repo, revs)
719 revpatches = self._revpatches(repo, revs)
719 realpatches += revpatches
720 realpatches += revpatches
720 numrevs = len(revpatches)
721 numrevs = len(revpatches)
721
722
722 self._cleanup(realpatches, numrevs, opts.get('keep'))
723 self._cleanup(realpatches, numrevs, opts.get('keep'))
723
724
724 def check_toppatch(self, repo):
725 def check_toppatch(self, repo):
725 if len(self.applied) > 0:
726 if len(self.applied) > 0:
726 top = bin(self.applied[-1].rev)
727 top = bin(self.applied[-1].rev)
727 pp = repo.dirstate.parents()
728 pp = repo.dirstate.parents()
728 if top not in pp:
729 if top not in pp:
729 raise util.Abort(_("working directory revision is not qtip"))
730 raise util.Abort(_("working directory revision is not qtip"))
730 return top
731 return top
731 return None
732 return None
732 def check_localchanges(self, repo, force=False, refresh=True):
733 def check_localchanges(self, repo, force=False, refresh=True):
733 m, a, r, d = repo.status()[:4]
734 m, a, r, d = repo.status()[:4]
734 if m or a or r or d:
735 if m or a or r or d:
735 if not force:
736 if not force:
736 if refresh:
737 if refresh:
737 raise util.Abort(_("local changes found, refresh first"))
738 raise util.Abort(_("local changes found, refresh first"))
738 else:
739 else:
739 raise util.Abort(_("local changes found"))
740 raise util.Abort(_("local changes found"))
740 return m, a, r, d
741 return m, a, r, d
741
742
742 _reserved = ('series', 'status', 'guards')
743 _reserved = ('series', 'status', 'guards')
743 def check_reserved_name(self, name):
744 def check_reserved_name(self, name):
744 if (name in self._reserved or name.startswith('.hg')
745 if (name in self._reserved or name.startswith('.hg')
745 or name.startswith('.mq')):
746 or name.startswith('.mq')):
746 raise util.Abort(_('"%s" cannot be used as the name of a patch')
747 raise util.Abort(_('"%s" cannot be used as the name of a patch')
747 % name)
748 % name)
748
749
749 def new(self, repo, patchfn, *pats, **opts):
750 def new(self, repo, patchfn, *pats, **opts):
750 """options:
751 """options:
751 msg: a string or a no-argument function returning a string
752 msg: a string or a no-argument function returning a string
752 """
753 """
753 msg = opts.get('msg')
754 msg = opts.get('msg')
754 force = opts.get('force')
755 force = opts.get('force')
755 user = opts.get('user')
756 user = opts.get('user')
756 date = opts.get('date')
757 date = opts.get('date')
757 if date:
758 if date:
758 date = util.parsedate(date)
759 date = util.parsedate(date)
759 self.check_reserved_name(patchfn)
760 self.check_reserved_name(patchfn)
760 if os.path.exists(self.join(patchfn)):
761 if os.path.exists(self.join(patchfn)):
761 raise util.Abort(_('patch "%s" already exists') % patchfn)
762 raise util.Abort(_('patch "%s" already exists') % patchfn)
762 if opts.get('include') or opts.get('exclude') or pats:
763 if opts.get('include') or opts.get('exclude') or pats:
763 match = cmdutil.match(repo, pats, opts)
764 match = cmdutil.match(repo, pats, opts)
764 # detect missing files in pats
765 # detect missing files in pats
765 def badfn(f, msg):
766 def badfn(f, msg):
766 raise util.Abort('%s: %s' % (f, msg))
767 raise util.Abort('%s: %s' % (f, msg))
767 match.bad = badfn
768 match.bad = badfn
768 m, a, r, d = repo.status(match=match)[:4]
769 m, a, r, d = repo.status(match=match)[:4]
769 else:
770 else:
770 m, a, r, d = self.check_localchanges(repo, force)
771 m, a, r, d = self.check_localchanges(repo, force)
771 match = cmdutil.matchfiles(repo, m + a + r)
772 match = cmdutil.matchfiles(repo, m + a + r)
772 commitfiles = m + a + r
773 commitfiles = m + a + r
773 self.check_toppatch(repo)
774 self.check_toppatch(repo)
774 insert = self.full_series_end()
775 insert = self.full_series_end()
775 wlock = repo.wlock()
776 wlock = repo.wlock()
776 try:
777 try:
777 # if patch file write fails, abort early
778 # if patch file write fails, abort early
778 p = self.opener(patchfn, "w")
779 p = self.opener(patchfn, "w")
779 try:
780 try:
780 if date:
781 if date:
781 p.write("# HG changeset patch\n")
782 p.write("# HG changeset patch\n")
782 if user:
783 if user:
783 p.write("# User " + user + "\n")
784 p.write("# User " + user + "\n")
784 p.write("# Date %d %d\n\n" % date)
785 p.write("# Date %d %d\n\n" % date)
785 elif user:
786 elif user:
786 p.write("From: " + user + "\n\n")
787 p.write("From: " + user + "\n\n")
787
788
788 if hasattr(msg, '__call__'):
789 if hasattr(msg, '__call__'):
789 msg = msg()
790 msg = msg()
790 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
791 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
791 n = repo.commit(commitmsg, user, date, match=match, force=True)
792 n = repo.commit(commitmsg, user, date, match=match, force=True)
792 if n is None:
793 if n is None:
793 raise util.Abort(_("repo commit failed"))
794 raise util.Abort(_("repo commit failed"))
794 try:
795 try:
795 self.full_series[insert:insert] = [patchfn]
796 self.full_series[insert:insert] = [patchfn]
796 self.applied.append(statusentry(hex(n), patchfn))
797 self.applied.append(statusentry(hex(n), patchfn))
797 self.parse_series()
798 self.parse_series()
798 self.series_dirty = 1
799 self.series_dirty = 1
799 self.applied_dirty = 1
800 self.applied_dirty = 1
800 if msg:
801 if msg:
801 msg = msg + "\n\n"
802 msg = msg + "\n\n"
802 p.write(msg)
803 p.write(msg)
803 if commitfiles:
804 if commitfiles:
804 diffopts = self.diffopts()
805 diffopts = self.diffopts()
805 if opts.get('git'): diffopts.git = True
806 if opts.get('git'): diffopts.git = True
806 parent = self.qparents(repo, n)
807 parent = self.qparents(repo, n)
807 chunks = patch.diff(repo, node1=parent, node2=n,
808 chunks = patch.diff(repo, node1=parent, node2=n,
808 match=match, opts=diffopts)
809 match=match, opts=diffopts)
809 for chunk in chunks:
810 for chunk in chunks:
810 p.write(chunk)
811 p.write(chunk)
811 p.close()
812 p.close()
812 wlock.release()
813 wlock.release()
813 wlock = None
814 wlock = None
814 r = self.qrepo()
815 r = self.qrepo()
815 if r: r.add([patchfn])
816 if r: r.add([patchfn])
816 except:
817 except:
817 repo.rollback()
818 repo.rollback()
818 raise
819 raise
819 except Exception:
820 except Exception:
820 patchpath = self.join(patchfn)
821 patchpath = self.join(patchfn)
821 try:
822 try:
822 os.unlink(patchpath)
823 os.unlink(patchpath)
823 except:
824 except:
824 self.ui.warn(_('error unlinking %s\n') % patchpath)
825 self.ui.warn(_('error unlinking %s\n') % patchpath)
825 raise
826 raise
826 self.removeundo(repo)
827 self.removeundo(repo)
827 finally:
828 finally:
828 release(wlock)
829 release(wlock)
829
830
830 def strip(self, repo, rev, update=True, backup="all", force=None):
831 def strip(self, repo, rev, update=True, backup="all", force=None):
831 wlock = lock = None
832 wlock = lock = None
832 try:
833 try:
833 wlock = repo.wlock()
834 wlock = repo.wlock()
834 lock = repo.lock()
835 lock = repo.lock()
835
836
836 if update:
837 if update:
837 self.check_localchanges(repo, force=force, refresh=False)
838 self.check_localchanges(repo, force=force, refresh=False)
838 urev = self.qparents(repo, rev)
839 urev = self.qparents(repo, rev)
839 hg.clean(repo, urev)
840 hg.clean(repo, urev)
840 repo.dirstate.write()
841 repo.dirstate.write()
841
842
842 self.removeundo(repo)
843 self.removeundo(repo)
843 repair.strip(self.ui, repo, rev, backup)
844 repair.strip(self.ui, repo, rev, backup)
844 # strip may have unbundled a set of backed up revisions after
845 # strip may have unbundled a set of backed up revisions after
845 # the actual strip
846 # the actual strip
846 self.removeundo(repo)
847 self.removeundo(repo)
847 finally:
848 finally:
848 release(lock, wlock)
849 release(lock, wlock)
849
850
850 def isapplied(self, patch):
851 def isapplied(self, patch):
851 """returns (index, rev, patch)"""
852 """returns (index, rev, patch)"""
852 for i, a in enumerate(self.applied):
853 for i, a in enumerate(self.applied):
853 if a.name == patch:
854 if a.name == patch:
854 return (i, a.rev, a.name)
855 return (i, a.rev, a.name)
855 return None
856 return None
856
857
857 # if the exact patch name does not exist, we try a few
858 # if the exact patch name does not exist, we try a few
858 # variations. If strict is passed, we try only #1
859 # variations. If strict is passed, we try only #1
859 #
860 #
860 # 1) a number to indicate an offset in the series file
861 # 1) a number to indicate an offset in the series file
861 # 2) a unique substring of the patch name was given
862 # 2) a unique substring of the patch name was given
862 # 3) patchname[-+]num to indicate an offset in the series file
863 # 3) patchname[-+]num to indicate an offset in the series file
863 def lookup(self, patch, strict=False):
864 def lookup(self, patch, strict=False):
864 patch = patch and str(patch)
865 patch = patch and str(patch)
865
866
866 def partial_name(s):
867 def partial_name(s):
867 if s in self.series:
868 if s in self.series:
868 return s
869 return s
869 matches = [x for x in self.series if s in x]
870 matches = [x for x in self.series if s in x]
870 if len(matches) > 1:
871 if len(matches) > 1:
871 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
872 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
872 for m in matches:
873 for m in matches:
873 self.ui.warn(' %s\n' % m)
874 self.ui.warn(' %s\n' % m)
874 return None
875 return None
875 if matches:
876 if matches:
876 return matches[0]
877 return matches[0]
877 if len(self.series) > 0 and len(self.applied) > 0:
878 if len(self.series) > 0 and len(self.applied) > 0:
878 if s == 'qtip':
879 if s == 'qtip':
879 return self.series[self.series_end(True)-1]
880 return self.series[self.series_end(True)-1]
880 if s == 'qbase':
881 if s == 'qbase':
881 return self.series[0]
882 return self.series[0]
882 return None
883 return None
883
884
884 if patch is None:
885 if patch is None:
885 return None
886 return None
886 if patch in self.series:
887 if patch in self.series:
887 return patch
888 return patch
888
889
889 if not os.path.isfile(self.join(patch)):
890 if not os.path.isfile(self.join(patch)):
890 try:
891 try:
891 sno = int(patch)
892 sno = int(patch)
892 except(ValueError, OverflowError):
893 except(ValueError, OverflowError):
893 pass
894 pass
894 else:
895 else:
895 if -len(self.series) <= sno < len(self.series):
896 if -len(self.series) <= sno < len(self.series):
896 return self.series[sno]
897 return self.series[sno]
897
898
898 if not strict:
899 if not strict:
899 res = partial_name(patch)
900 res = partial_name(patch)
900 if res:
901 if res:
901 return res
902 return res
902 minus = patch.rfind('-')
903 minus = patch.rfind('-')
903 if minus >= 0:
904 if minus >= 0:
904 res = partial_name(patch[:minus])
905 res = partial_name(patch[:minus])
905 if res:
906 if res:
906 i = self.series.index(res)
907 i = self.series.index(res)
907 try:
908 try:
908 off = int(patch[minus+1:] or 1)
909 off = int(patch[minus+1:] or 1)
909 except(ValueError, OverflowError):
910 except(ValueError, OverflowError):
910 pass
911 pass
911 else:
912 else:
912 if i - off >= 0:
913 if i - off >= 0:
913 return self.series[i - off]
914 return self.series[i - off]
914 plus = patch.rfind('+')
915 plus = patch.rfind('+')
915 if plus >= 0:
916 if plus >= 0:
916 res = partial_name(patch[:plus])
917 res = partial_name(patch[:plus])
917 if res:
918 if res:
918 i = self.series.index(res)
919 i = self.series.index(res)
919 try:
920 try:
920 off = int(patch[plus+1:] or 1)
921 off = int(patch[plus+1:] or 1)
921 except(ValueError, OverflowError):
922 except(ValueError, OverflowError):
922 pass
923 pass
923 else:
924 else:
924 if i + off < len(self.series):
925 if i + off < len(self.series):
925 return self.series[i + off]
926 return self.series[i + off]
926 raise util.Abort(_("patch %s not in series") % patch)
927 raise util.Abort(_("patch %s not in series") % patch)
927
928
928 def push(self, repo, patch=None, force=False, list=False,
929 def push(self, repo, patch=None, force=False, list=False,
929 mergeq=None, all=False):
930 mergeq=None, all=False):
930 wlock = repo.wlock()
931 wlock = repo.wlock()
931 try:
932 try:
932 if repo.dirstate.parents()[0] not in repo.heads():
933 if repo.dirstate.parents()[0] not in repo.heads():
933 self.ui.status(_("(working directory not at a head)\n"))
934 self.ui.status(_("(working directory not at a head)\n"))
934
935
935 if not self.series:
936 if not self.series:
936 self.ui.warn(_('no patches in series\n'))
937 self.ui.warn(_('no patches in series\n'))
937 return 0
938 return 0
938
939
939 patch = self.lookup(patch)
940 patch = self.lookup(patch)
940 # Suppose our series file is: A B C and the current 'top'
941 # Suppose our series file is: A B C and the current 'top'
941 # patch is B. qpush C should be performed (moving forward)
942 # patch is B. qpush C should be performed (moving forward)
942 # qpush B is a NOP (no change) qpush A is an error (can't
943 # qpush B is a NOP (no change) qpush A is an error (can't
943 # go backwards with qpush)
944 # go backwards with qpush)
944 if patch:
945 if patch:
945 info = self.isapplied(patch)
946 info = self.isapplied(patch)
946 if info:
947 if info:
947 if info[0] < len(self.applied) - 1:
948 if info[0] < len(self.applied) - 1:
948 raise util.Abort(
949 raise util.Abort(
949 _("cannot push to a previous patch: %s") % patch)
950 _("cannot push to a previous patch: %s") % patch)
950 self.ui.warn(
951 self.ui.warn(
951 _('qpush: %s is already at the top\n') % patch)
952 _('qpush: %s is already at the top\n') % patch)
952 return
953 return
953 pushable, reason = self.pushable(patch)
954 pushable, reason = self.pushable(patch)
954 if not pushable:
955 if not pushable:
955 if reason:
956 if reason:
956 reason = _('guarded by %r') % reason
957 reason = _('guarded by %r') % reason
957 else:
958 else:
958 reason = _('no matching guards')
959 reason = _('no matching guards')
959 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
960 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
960 return 1
961 return 1
961 elif all:
962 elif all:
962 patch = self.series[-1]
963 patch = self.series[-1]
963 if self.isapplied(patch):
964 if self.isapplied(patch):
964 self.ui.warn(_('all patches are currently applied\n'))
965 self.ui.warn(_('all patches are currently applied\n'))
965 return 0
966 return 0
966
967
967 # Following the above example, starting at 'top' of B:
968 # Following the above example, starting at 'top' of B:
968 # qpush should be performed (pushes C), but a subsequent
969 # qpush should be performed (pushes C), but a subsequent
969 # qpush without an argument is an error (nothing to
970 # qpush without an argument is an error (nothing to
970 # apply). This allows a loop of "...while hg qpush..." to
971 # apply). This allows a loop of "...while hg qpush..." to
971 # work as it detects an error when done
972 # work as it detects an error when done
972 start = self.series_end()
973 start = self.series_end()
973 if start == len(self.series):
974 if start == len(self.series):
974 self.ui.warn(_('patch series already fully applied\n'))
975 self.ui.warn(_('patch series already fully applied\n'))
975 return 1
976 return 1
976 if not force:
977 if not force:
977 self.check_localchanges(repo)
978 self.check_localchanges(repo)
978
979
979 self.applied_dirty = 1
980 self.applied_dirty = 1
980 if start > 0:
981 if start > 0:
981 self.check_toppatch(repo)
982 self.check_toppatch(repo)
982 if not patch:
983 if not patch:
983 patch = self.series[start]
984 patch = self.series[start]
984 end = start + 1
985 end = start + 1
985 else:
986 else:
986 end = self.series.index(patch, start) + 1
987 end = self.series.index(patch, start) + 1
987
988
988 s = self.series[start:end]
989 s = self.series[start:end]
989 all_files = {}
990 all_files = {}
990 try:
991 try:
991 if mergeq:
992 if mergeq:
992 ret = self.mergepatch(repo, mergeq, s)
993 ret = self.mergepatch(repo, mergeq, s)
993 else:
994 else:
994 ret = self.apply(repo, s, list, all_files=all_files)
995 ret = self.apply(repo, s, list, all_files=all_files)
995 except:
996 except:
996 self.ui.warn(_('cleaning up working directory...'))
997 self.ui.warn(_('cleaning up working directory...'))
997 node = repo.dirstate.parents()[0]
998 node = repo.dirstate.parents()[0]
998 hg.revert(repo, node, None)
999 hg.revert(repo, node, None)
999 unknown = repo.status(unknown=True)[4]
1000 unknown = repo.status(unknown=True)[4]
1000 # only remove unknown files that we know we touched or
1001 # only remove unknown files that we know we touched or
1001 # created while patching
1002 # created while patching
1002 for f in unknown:
1003 for f in unknown:
1003 if f in all_files:
1004 if f in all_files:
1004 util.unlink(repo.wjoin(f))
1005 util.unlink(repo.wjoin(f))
1005 self.ui.warn(_('done\n'))
1006 self.ui.warn(_('done\n'))
1006 raise
1007 raise
1007
1008
1008 if not self.applied:
1009 if not self.applied:
1009 return ret[0]
1010 return ret[0]
1010 top = self.applied[-1].name
1011 top = self.applied[-1].name
1011 if ret[0] and ret[0] > 1:
1012 if ret[0] and ret[0] > 1:
1012 msg = _("errors during apply, please fix and refresh %s\n")
1013 msg = _("errors during apply, please fix and refresh %s\n")
1013 self.ui.write(msg % top)
1014 self.ui.write(msg % top)
1014 else:
1015 else:
1015 self.ui.write(_("now at: %s\n") % top)
1016 self.ui.write(_("now at: %s\n") % top)
1016 return ret[0]
1017 return ret[0]
1017
1018
1018 finally:
1019 finally:
1019 wlock.release()
1020 wlock.release()
1020
1021
1021 def pop(self, repo, patch=None, force=False, update=True, all=False):
1022 def pop(self, repo, patch=None, force=False, update=True, all=False):
1022 def getfile(f, rev, flags):
1023 def getfile(f, rev, flags):
1023 t = repo.file(f).read(rev)
1024 t = repo.file(f).read(rev)
1024 repo.wwrite(f, t, flags)
1025 repo.wwrite(f, t, flags)
1025
1026
1026 wlock = repo.wlock()
1027 wlock = repo.wlock()
1027 try:
1028 try:
1028 if patch:
1029 if patch:
1029 # index, rev, patch
1030 # index, rev, patch
1030 info = self.isapplied(patch)
1031 info = self.isapplied(patch)
1031 if not info:
1032 if not info:
1032 patch = self.lookup(patch)
1033 patch = self.lookup(patch)
1033 info = self.isapplied(patch)
1034 info = self.isapplied(patch)
1034 if not info:
1035 if not info:
1035 raise util.Abort(_("patch %s is not applied") % patch)
1036 raise util.Abort(_("patch %s is not applied") % patch)
1036
1037
1037 if len(self.applied) == 0:
1038 if len(self.applied) == 0:
1038 # Allow qpop -a to work repeatedly,
1039 # Allow qpop -a to work repeatedly,
1039 # but not qpop without an argument
1040 # but not qpop without an argument
1040 self.ui.warn(_("no patches applied\n"))
1041 self.ui.warn(_("no patches applied\n"))
1041 return not all
1042 return not all
1042
1043
1043 if all:
1044 if all:
1044 start = 0
1045 start = 0
1045 elif patch:
1046 elif patch:
1046 start = info[0] + 1
1047 start = info[0] + 1
1047 else:
1048 else:
1048 start = len(self.applied) - 1
1049 start = len(self.applied) - 1
1049
1050
1050 if start >= len(self.applied):
1051 if start >= len(self.applied):
1051 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1052 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1052 return
1053 return
1053
1054
1054 if not update:
1055 if not update:
1055 parents = repo.dirstate.parents()
1056 parents = repo.dirstate.parents()
1056 rr = [ bin(x.rev) for x in self.applied ]
1057 rr = [ bin(x.rev) for x in self.applied ]
1057 for p in parents:
1058 for p in parents:
1058 if p in rr:
1059 if p in rr:
1059 self.ui.warn(_("qpop: forcing dirstate update\n"))
1060 self.ui.warn(_("qpop: forcing dirstate update\n"))
1060 update = True
1061 update = True
1061 else:
1062 else:
1062 parents = [p.hex() for p in repo[None].parents()]
1063 parents = [p.hex() for p in repo[None].parents()]
1063 needupdate = False
1064 needupdate = False
1064 for entry in self.applied[start:]:
1065 for entry in self.applied[start:]:
1065 if entry.rev in parents:
1066 if entry.rev in parents:
1066 needupdate = True
1067 needupdate = True
1067 break
1068 break
1068 update = needupdate
1069 update = needupdate
1069
1070
1070 if not force and update:
1071 if not force and update:
1071 self.check_localchanges(repo)
1072 self.check_localchanges(repo)
1072
1073
1073 self.applied_dirty = 1
1074 self.applied_dirty = 1
1074 end = len(self.applied)
1075 end = len(self.applied)
1075 rev = bin(self.applied[start].rev)
1076 rev = bin(self.applied[start].rev)
1076 if update:
1077 if update:
1077 top = self.check_toppatch(repo)
1078 top = self.check_toppatch(repo)
1078
1079
1079 try:
1080 try:
1080 heads = repo.changelog.heads(rev)
1081 heads = repo.changelog.heads(rev)
1081 except error.LookupError:
1082 except error.LookupError:
1082 node = short(rev)
1083 node = short(rev)
1083 raise util.Abort(_('trying to pop unknown node %s') % node)
1084 raise util.Abort(_('trying to pop unknown node %s') % node)
1084
1085
1085 if heads != [bin(self.applied[-1].rev)]:
1086 if heads != [bin(self.applied[-1].rev)]:
1086 raise util.Abort(_("popping would remove a revision not "
1087 raise util.Abort(_("popping would remove a revision not "
1087 "managed by this patch queue"))
1088 "managed by this patch queue"))
1088
1089
1089 # we know there are no local changes, so we can make a simplified
1090 # we know there are no local changes, so we can make a simplified
1090 # form of hg.update.
1091 # form of hg.update.
1091 if update:
1092 if update:
1092 qp = self.qparents(repo, rev)
1093 qp = self.qparents(repo, rev)
1093 changes = repo.changelog.read(qp)
1094 changes = repo.changelog.read(qp)
1094 mmap = repo.manifest.read(changes[0])
1095 mmap = repo.manifest.read(changes[0])
1095 m, a, r, d = repo.status(qp, top)[:4]
1096 m, a, r, d = repo.status(qp, top)[:4]
1096 if d:
1097 if d:
1097 raise util.Abort(_("deletions found between repo revs"))
1098 raise util.Abort(_("deletions found between repo revs"))
1098 for f in m:
1099 for f in m:
1099 getfile(f, mmap[f], mmap.flags(f))
1100 getfile(f, mmap[f], mmap.flags(f))
1100 for f in r:
1101 for f in r:
1101 getfile(f, mmap[f], mmap.flags(f))
1102 getfile(f, mmap[f], mmap.flags(f))
1102 for f in m + r:
1103 for f in m + r:
1103 repo.dirstate.normal(f)
1104 repo.dirstate.normal(f)
1104 for f in a:
1105 for f in a:
1105 try:
1106 try:
1106 os.unlink(repo.wjoin(f))
1107 os.unlink(repo.wjoin(f))
1107 except OSError, e:
1108 except OSError, e:
1108 if e.errno != errno.ENOENT:
1109 if e.errno != errno.ENOENT:
1109 raise
1110 raise
1110 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1111 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1111 except: pass
1112 except: pass
1112 repo.dirstate.forget(f)
1113 repo.dirstate.forget(f)
1113 repo.dirstate.setparents(qp, nullid)
1114 repo.dirstate.setparents(qp, nullid)
1114 for patch in reversed(self.applied[start:end]):
1115 for patch in reversed(self.applied[start:end]):
1115 self.ui.status(_("popping %s\n") % patch.name)
1116 self.ui.status(_("popping %s\n") % patch.name)
1116 del self.applied[start:end]
1117 del self.applied[start:end]
1117 self.strip(repo, rev, update=False, backup='strip')
1118 self.strip(repo, rev, update=False, backup='strip')
1118 if len(self.applied):
1119 if len(self.applied):
1119 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1120 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1120 else:
1121 else:
1121 self.ui.write(_("patch queue now empty\n"))
1122 self.ui.write(_("patch queue now empty\n"))
1122 finally:
1123 finally:
1123 wlock.release()
1124 wlock.release()
1124
1125
1125 def diff(self, repo, pats, opts):
1126 def diff(self, repo, pats, opts):
1126 top = self.check_toppatch(repo)
1127 top = self.check_toppatch(repo)
1127 if not top:
1128 if not top:
1128 self.ui.write(_("no patches applied\n"))
1129 self.ui.write(_("no patches applied\n"))
1129 return
1130 return
1130 qp = self.qparents(repo, top)
1131 qp = self.qparents(repo, top)
1131 self._diffopts = patch.diffopts(self.ui, opts)
1132 self._diffopts = patch.diffopts(self.ui, opts)
1132 self.printdiff(repo, qp, files=pats, opts=opts)
1133 self.printdiff(repo, qp, files=pats, opts=opts)
1133
1134
1134 def refresh(self, repo, pats=None, **opts):
1135 def refresh(self, repo, pats=None, **opts):
1135 if len(self.applied) == 0:
1136 if len(self.applied) == 0:
1136 self.ui.write(_("no patches applied\n"))
1137 self.ui.write(_("no patches applied\n"))
1137 return 1
1138 return 1
1138 msg = opts.get('msg', '').rstrip()
1139 msg = opts.get('msg', '').rstrip()
1139 newuser = opts.get('user')
1140 newuser = opts.get('user')
1140 newdate = opts.get('date')
1141 newdate = opts.get('date')
1141 if newdate:
1142 if newdate:
1142 newdate = '%d %d' % util.parsedate(newdate)
1143 newdate = '%d %d' % util.parsedate(newdate)
1143 wlock = repo.wlock()
1144 wlock = repo.wlock()
1144 try:
1145 try:
1145 self.check_toppatch(repo)
1146 self.check_toppatch(repo)
1146 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1147 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1147 top = bin(top)
1148 top = bin(top)
1148 if repo.changelog.heads(top) != [top]:
1149 if repo.changelog.heads(top) != [top]:
1149 raise util.Abort(_("cannot refresh a revision with children"))
1150 raise util.Abort(_("cannot refresh a revision with children"))
1150 cparents = repo.changelog.parents(top)
1151 cparents = repo.changelog.parents(top)
1151 patchparent = self.qparents(repo, top)
1152 patchparent = self.qparents(repo, top)
1152 ph = patchheader(self.join(patchfn))
1153 ph = patchheader(self.join(patchfn))
1153
1154
1154 patchf = self.opener(patchfn, 'r')
1155 patchf = self.opener(patchfn, 'r')
1155
1156
1156 # if the patch was a git patch, refresh it as a git patch
1157 # if the patch was a git patch, refresh it as a git patch
1157 for line in patchf:
1158 for line in patchf:
1158 if line.startswith('diff --git'):
1159 if line.startswith('diff --git'):
1159 self.diffopts().git = True
1160 self.diffopts().git = True
1160 break
1161 break
1161
1162
1162 if msg:
1163 if msg:
1163 ph.setmessage(msg)
1164 ph.setmessage(msg)
1164 if newuser:
1165 if newuser:
1165 ph.setuser(newuser)
1166 ph.setuser(newuser)
1166 if newdate:
1167 if newdate:
1167 ph.setdate(newdate)
1168 ph.setdate(newdate)
1168
1169
1169 # only commit new patch when write is complete
1170 # only commit new patch when write is complete
1170 patchf = self.opener(patchfn, 'w', atomictemp=True)
1171 patchf = self.opener(patchfn, 'w', atomictemp=True)
1171
1172
1172 patchf.seek(0)
1173 patchf.seek(0)
1173 patchf.truncate()
1174 patchf.truncate()
1174
1175
1175 comments = str(ph)
1176 comments = str(ph)
1176 if comments:
1177 if comments:
1177 patchf.write(comments)
1178 patchf.write(comments)
1178
1179
1179 if opts.get('git'):
1180 if opts.get('git'):
1180 self.diffopts().git = True
1181 self.diffopts().git = True
1181 tip = repo.changelog.tip()
1182 tip = repo.changelog.tip()
1182 if top == tip:
1183 if top == tip:
1183 # if the top of our patch queue is also the tip, there is an
1184 # if the top of our patch queue is also the tip, there is an
1184 # optimization here. We update the dirstate in place and strip
1185 # optimization here. We update the dirstate in place and strip
1185 # off the tip commit. Then just commit the current directory
1186 # off the tip commit. Then just commit the current directory
1186 # tree. We can also send repo.commit the list of files
1187 # tree. We can also send repo.commit the list of files
1187 # changed to speed up the diff
1188 # changed to speed up the diff
1188 #
1189 #
1189 # in short mode, we only diff the files included in the
1190 # in short mode, we only diff the files included in the
1190 # patch already plus specified files
1191 # patch already plus specified files
1191 #
1192 #
1192 # this should really read:
1193 # this should really read:
1193 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1194 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1194 # but we do it backwards to take advantage of manifest/chlog
1195 # but we do it backwards to take advantage of manifest/chlog
1195 # caching against the next repo.status call
1196 # caching against the next repo.status call
1196 #
1197 #
1197 mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
1198 mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
1198 changes = repo.changelog.read(tip)
1199 changes = repo.changelog.read(tip)
1199 man = repo.manifest.read(changes[0])
1200 man = repo.manifest.read(changes[0])
1200 aaa = aa[:]
1201 aaa = aa[:]
1201 matchfn = cmdutil.match(repo, pats, opts)
1202 matchfn = cmdutil.match(repo, pats, opts)
1202 if opts.get('short'):
1203 if opts.get('short'):
1203 # if amending a patch, we start with existing
1204 # if amending a patch, we start with existing
1204 # files plus specified files - unfiltered
1205 # files plus specified files - unfiltered
1205 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1206 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1206 # filter with inc/exl options
1207 # filter with inc/exl options
1207 matchfn = cmdutil.match(repo, opts=opts)
1208 matchfn = cmdutil.match(repo, opts=opts)
1208 else:
1209 else:
1209 match = cmdutil.matchall(repo)
1210 match = cmdutil.matchall(repo)
1210 m, a, r, d = repo.status(match=match)[:4]
1211 m, a, r, d = repo.status(match=match)[:4]
1211
1212
1212 # we might end up with files that were added between
1213 # we might end up with files that were added between
1213 # tip and the dirstate parent, but then changed in the
1214 # tip and the dirstate parent, but then changed in the
1214 # local dirstate. in this case, we want them to only
1215 # local dirstate. in this case, we want them to only
1215 # show up in the added section
1216 # show up in the added section
1216 for x in m:
1217 for x in m:
1217 if x not in aa:
1218 if x not in aa:
1218 mm.append(x)
1219 mm.append(x)
1219 # we might end up with files added by the local dirstate that
1220 # we might end up with files added by the local dirstate that
1220 # were deleted by the patch. In this case, they should only
1221 # were deleted by the patch. In this case, they should only
1221 # show up in the changed section.
1222 # show up in the changed section.
1222 for x in a:
1223 for x in a:
1223 if x in dd:
1224 if x in dd:
1224 del dd[dd.index(x)]
1225 del dd[dd.index(x)]
1225 mm.append(x)
1226 mm.append(x)
1226 else:
1227 else:
1227 aa.append(x)
1228 aa.append(x)
1228 # make sure any files deleted in the local dirstate
1229 # make sure any files deleted in the local dirstate
1229 # are not in the add or change column of the patch
1230 # are not in the add or change column of the patch
1230 forget = []
1231 forget = []
1231 for x in d + r:
1232 for x in d + r:
1232 if x in aa:
1233 if x in aa:
1233 del aa[aa.index(x)]
1234 del aa[aa.index(x)]
1234 forget.append(x)
1235 forget.append(x)
1235 continue
1236 continue
1236 elif x in mm:
1237 elif x in mm:
1237 del mm[mm.index(x)]
1238 del mm[mm.index(x)]
1238 dd.append(x)
1239 dd.append(x)
1239
1240
1240 m = list(set(mm))
1241 m = list(set(mm))
1241 r = list(set(dd))
1242 r = list(set(dd))
1242 a = list(set(aa))
1243 a = list(set(aa))
1243 c = [filter(matchfn, l) for l in (m, a, r)]
1244 c = [filter(matchfn, l) for l in (m, a, r)]
1244 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1245 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1245 chunks = patch.diff(repo, patchparent, match=match,
1246 chunks = patch.diff(repo, patchparent, match=match,
1246 changes=c, opts=self.diffopts())
1247 changes=c, opts=self.diffopts())
1247 for chunk in chunks:
1248 for chunk in chunks:
1248 patchf.write(chunk)
1249 patchf.write(chunk)
1249
1250
1250 try:
1251 try:
1251 if self.diffopts().git:
1252 if self.diffopts().git:
1252 copies = {}
1253 copies = {}
1253 for dst in a:
1254 for dst in a:
1254 src = repo.dirstate.copied(dst)
1255 src = repo.dirstate.copied(dst)
1255 # during qfold, the source file for copies may
1256 # during qfold, the source file for copies may
1256 # be removed. Treat this as a simple add.
1257 # be removed. Treat this as a simple add.
1257 if src is not None and src in repo.dirstate:
1258 if src is not None and src in repo.dirstate:
1258 copies.setdefault(src, []).append(dst)
1259 copies.setdefault(src, []).append(dst)
1259 repo.dirstate.add(dst)
1260 repo.dirstate.add(dst)
1260 # remember the copies between patchparent and tip
1261 # remember the copies between patchparent and tip
1261 for dst in aaa:
1262 for dst in aaa:
1262 f = repo.file(dst)
1263 f = repo.file(dst)
1263 src = f.renamed(man[dst])
1264 src = f.renamed(man[dst])
1264 if src:
1265 if src:
1265 copies.setdefault(src[0], []).extend(copies.get(dst, []))
1266 copies.setdefault(src[0], []).extend(copies.get(dst, []))
1266 if dst in a:
1267 if dst in a:
1267 copies[src[0]].append(dst)
1268 copies[src[0]].append(dst)
1268 # we can't copy a file created by the patch itself
1269 # we can't copy a file created by the patch itself
1269 if dst in copies:
1270 if dst in copies:
1270 del copies[dst]
1271 del copies[dst]
1271 for src, dsts in copies.iteritems():
1272 for src, dsts in copies.iteritems():
1272 for dst in dsts:
1273 for dst in dsts:
1273 repo.dirstate.copy(src, dst)
1274 repo.dirstate.copy(src, dst)
1274 else:
1275 else:
1275 for dst in a:
1276 for dst in a:
1276 repo.dirstate.add(dst)
1277 repo.dirstate.add(dst)
1277 # Drop useless copy information
1278 # Drop useless copy information
1278 for f in list(repo.dirstate.copies()):
1279 for f in list(repo.dirstate.copies()):
1279 repo.dirstate.copy(None, f)
1280 repo.dirstate.copy(None, f)
1280 for f in r:
1281 for f in r:
1281 repo.dirstate.remove(f)
1282 repo.dirstate.remove(f)
1282 # if the patch excludes a modified file, mark that
1283 # if the patch excludes a modified file, mark that
1283 # file with mtime=0 so status can see it.
1284 # file with mtime=0 so status can see it.
1284 mm = []
1285 mm = []
1285 for i in xrange(len(m)-1, -1, -1):
1286 for i in xrange(len(m)-1, -1, -1):
1286 if not matchfn(m[i]):
1287 if not matchfn(m[i]):
1287 mm.append(m[i])
1288 mm.append(m[i])
1288 del m[i]
1289 del m[i]
1289 for f in m:
1290 for f in m:
1290 repo.dirstate.normal(f)
1291 repo.dirstate.normal(f)
1291 for f in mm:
1292 for f in mm:
1292 repo.dirstate.normallookup(f)
1293 repo.dirstate.normallookup(f)
1293 for f in forget:
1294 for f in forget:
1294 repo.dirstate.forget(f)
1295 repo.dirstate.forget(f)
1295
1296
1296 if not msg:
1297 if not msg:
1297 if not ph.message:
1298 if not ph.message:
1298 message = "[mq]: %s\n" % patchfn
1299 message = "[mq]: %s\n" % patchfn
1299 else:
1300 else:
1300 message = "\n".join(ph.message)
1301 message = "\n".join(ph.message)
1301 else:
1302 else:
1302 message = msg
1303 message = msg
1303
1304
1304 user = ph.user or changes[1]
1305 user = ph.user or changes[1]
1305
1306
1306 # assumes strip can roll itself back if interrupted
1307 # assumes strip can roll itself back if interrupted
1307 repo.dirstate.setparents(*cparents)
1308 repo.dirstate.setparents(*cparents)
1308 self.applied.pop()
1309 self.applied.pop()
1309 self.applied_dirty = 1
1310 self.applied_dirty = 1
1310 self.strip(repo, top, update=False,
1311 self.strip(repo, top, update=False,
1311 backup='strip')
1312 backup='strip')
1312 except:
1313 except:
1313 repo.dirstate.invalidate()
1314 repo.dirstate.invalidate()
1314 raise
1315 raise
1315
1316
1316 try:
1317 try:
1317 # might be nice to attempt to roll back strip after this
1318 # might be nice to attempt to roll back strip after this
1318 patchf.rename()
1319 patchf.rename()
1319 n = repo.commit(message, user, ph.date, match=match,
1320 n = repo.commit(message, user, ph.date, match=match,
1320 force=True)
1321 force=True)
1321 self.applied.append(statusentry(hex(n), patchfn))
1322 self.applied.append(statusentry(hex(n), patchfn))
1322 except:
1323 except:
1323 ctx = repo[cparents[0]]
1324 ctx = repo[cparents[0]]
1324 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1325 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1325 self.save_dirty()
1326 self.save_dirty()
1326 self.ui.warn(_('refresh interrupted while patch was popped! '
1327 self.ui.warn(_('refresh interrupted while patch was popped! '
1327 '(revert --all, qpush to recover)\n'))
1328 '(revert --all, qpush to recover)\n'))
1328 raise
1329 raise
1329 else:
1330 else:
1330 self.printdiff(repo, patchparent, fp=patchf)
1331 self.printdiff(repo, patchparent, fp=patchf)
1331 patchf.rename()
1332 patchf.rename()
1332 added = repo.status()[1]
1333 added = repo.status()[1]
1333 for a in added:
1334 for a in added:
1334 f = repo.wjoin(a)
1335 f = repo.wjoin(a)
1335 try:
1336 try:
1336 os.unlink(f)
1337 os.unlink(f)
1337 except OSError, e:
1338 except OSError, e:
1338 if e.errno != errno.ENOENT:
1339 if e.errno != errno.ENOENT:
1339 raise
1340 raise
1340 try: os.removedirs(os.path.dirname(f))
1341 try: os.removedirs(os.path.dirname(f))
1341 except: pass
1342 except: pass
1342 # forget the file copies in the dirstate
1343 # forget the file copies in the dirstate
1343 # push should readd the files later on
1344 # push should readd the files later on
1344 repo.dirstate.forget(a)
1345 repo.dirstate.forget(a)
1345 self.pop(repo, force=True)
1346 self.pop(repo, force=True)
1346 self.push(repo, force=True)
1347 self.push(repo, force=True)
1347 finally:
1348 finally:
1348 wlock.release()
1349 wlock.release()
1349 self.removeundo(repo)
1350 self.removeundo(repo)
1350
1351
1351 def init(self, repo, create=False):
1352 def init(self, repo, create=False):
1352 if not create and os.path.isdir(self.path):
1353 if not create and os.path.isdir(self.path):
1353 raise util.Abort(_("patch queue directory already exists"))
1354 raise util.Abort(_("patch queue directory already exists"))
1354 try:
1355 try:
1355 os.mkdir(self.path)
1356 os.mkdir(self.path)
1356 except OSError, inst:
1357 except OSError, inst:
1357 if inst.errno != errno.EEXIST or not create:
1358 if inst.errno != errno.EEXIST or not create:
1358 raise
1359 raise
1359 if create:
1360 if create:
1360 return self.qrepo(create=True)
1361 return self.qrepo(create=True)
1361
1362
1362 def unapplied(self, repo, patch=None):
1363 def unapplied(self, repo, patch=None):
1363 if patch and patch not in self.series:
1364 if patch and patch not in self.series:
1364 raise util.Abort(_("patch %s is not in series file") % patch)
1365 raise util.Abort(_("patch %s is not in series file") % patch)
1365 if not patch:
1366 if not patch:
1366 start = self.series_end()
1367 start = self.series_end()
1367 else:
1368 else:
1368 start = self.series.index(patch) + 1
1369 start = self.series.index(patch) + 1
1369 unapplied = []
1370 unapplied = []
1370 for i in xrange(start, len(self.series)):
1371 for i in xrange(start, len(self.series)):
1371 pushable, reason = self.pushable(i)
1372 pushable, reason = self.pushable(i)
1372 if pushable:
1373 if pushable:
1373 unapplied.append((i, self.series[i]))
1374 unapplied.append((i, self.series[i]))
1374 self.explain_pushable(i)
1375 self.explain_pushable(i)
1375 return unapplied
1376 return unapplied
1376
1377
1377 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1378 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1378 summary=False):
1379 summary=False):
1379 def displayname(pfx, patchname):
1380 def displayname(pfx, patchname):
1380 if summary:
1381 if summary:
1381 ph = patchheader(self.join(patchname))
1382 ph = patchheader(self.join(patchname))
1382 msg = ph.message
1383 msg = ph.message
1383 msg = msg and ': ' + msg[0] or ': '
1384 msg = msg and ': ' + msg[0] or ': '
1384 else:
1385 else:
1385 msg = ''
1386 msg = ''
1386 msg = "%s%s%s" % (pfx, patchname, msg)
1387 msg = "%s%s%s" % (pfx, patchname, msg)
1387 if self.ui.interactive():
1388 if self.ui.interactive():
1388 msg = util.ellipsis(msg, util.termwidth())
1389 msg = util.ellipsis(msg, util.termwidth())
1389 self.ui.write(msg + '\n')
1390 self.ui.write(msg + '\n')
1390
1391
1391 applied = set([p.name for p in self.applied])
1392 applied = set([p.name for p in self.applied])
1392 if length is None:
1393 if length is None:
1393 length = len(self.series) - start
1394 length = len(self.series) - start
1394 if not missing:
1395 if not missing:
1395 if self.ui.verbose:
1396 if self.ui.verbose:
1396 idxwidth = len(str(start+length - 1))
1397 idxwidth = len(str(start+length - 1))
1397 for i in xrange(start, start+length):
1398 for i in xrange(start, start+length):
1398 patch = self.series[i]
1399 patch = self.series[i]
1399 if patch in applied:
1400 if patch in applied:
1400 stat = 'A'
1401 stat = 'A'
1401 elif self.pushable(i)[0]:
1402 elif self.pushable(i)[0]:
1402 stat = 'U'
1403 stat = 'U'
1403 else:
1404 else:
1404 stat = 'G'
1405 stat = 'G'
1405 pfx = ''
1406 pfx = ''
1406 if self.ui.verbose:
1407 if self.ui.verbose:
1407 pfx = '%*d %s ' % (idxwidth, i, stat)
1408 pfx = '%*d %s ' % (idxwidth, i, stat)
1408 elif status and status != stat:
1409 elif status and status != stat:
1409 continue
1410 continue
1410 displayname(pfx, patch)
1411 displayname(pfx, patch)
1411 else:
1412 else:
1412 msng_list = []
1413 msng_list = []
1413 for root, dirs, files in os.walk(self.path):
1414 for root, dirs, files in os.walk(self.path):
1414 d = root[len(self.path) + 1:]
1415 d = root[len(self.path) + 1:]
1415 for f in files:
1416 for f in files:
1416 fl = os.path.join(d, f)
1417 fl = os.path.join(d, f)
1417 if (fl not in self.series and
1418 if (fl not in self.series and
1418 fl not in (self.status_path, self.series_path,
1419 fl not in (self.status_path, self.series_path,
1419 self.guards_path)
1420 self.guards_path)
1420 and not fl.startswith('.')):
1421 and not fl.startswith('.')):
1421 msng_list.append(fl)
1422 msng_list.append(fl)
1422 for x in sorted(msng_list):
1423 for x in sorted(msng_list):
1423 pfx = self.ui.verbose and ('D ') or ''
1424 pfx = self.ui.verbose and ('D ') or ''
1424 displayname(pfx, x)
1425 displayname(pfx, x)
1425
1426
1426 def issaveline(self, l):
1427 def issaveline(self, l):
1427 if l.name == '.hg.patches.save.line':
1428 if l.name == '.hg.patches.save.line':
1428 return True
1429 return True
1429
1430
1430 def qrepo(self, create=False):
1431 def qrepo(self, create=False):
1431 if create or os.path.isdir(self.join(".hg")):
1432 if create or os.path.isdir(self.join(".hg")):
1432 return hg.repository(self.ui, path=self.path, create=create)
1433 return hg.repository(self.ui, path=self.path, create=create)
1433
1434
1434 def restore(self, repo, rev, delete=None, qupdate=None):
1435 def restore(self, repo, rev, delete=None, qupdate=None):
1435 c = repo.changelog.read(rev)
1436 c = repo.changelog.read(rev)
1436 desc = c[4].strip()
1437 desc = c[4].strip()
1437 lines = desc.splitlines()
1438 lines = desc.splitlines()
1438 i = 0
1439 i = 0
1439 datastart = None
1440 datastart = None
1440 series = []
1441 series = []
1441 applied = []
1442 applied = []
1442 qpp = None
1443 qpp = None
1443 for i, line in enumerate(lines):
1444 for i, line in enumerate(lines):
1444 if line == 'Patch Data:':
1445 if line == 'Patch Data:':
1445 datastart = i + 1
1446 datastart = i + 1
1446 elif line.startswith('Dirstate:'):
1447 elif line.startswith('Dirstate:'):
1447 l = line.rstrip()
1448 l = line.rstrip()
1448 l = l[10:].split(' ')
1449 l = l[10:].split(' ')
1449 qpp = [ bin(x) for x in l ]
1450 qpp = [ bin(x) for x in l ]
1450 elif datastart != None:
1451 elif datastart != None:
1451 l = line.rstrip()
1452 l = line.rstrip()
1452 se = statusentry(l)
1453 se = statusentry(l)
1453 file_ = se.name
1454 file_ = se.name
1454 if se.rev:
1455 if se.rev:
1455 applied.append(se)
1456 applied.append(se)
1456 else:
1457 else:
1457 series.append(file_)
1458 series.append(file_)
1458 if datastart is None:
1459 if datastart is None:
1459 self.ui.warn(_("No saved patch data found\n"))
1460 self.ui.warn(_("No saved patch data found\n"))
1460 return 1
1461 return 1
1461 self.ui.warn(_("restoring status: %s\n") % lines[0])
1462 self.ui.warn(_("restoring status: %s\n") % lines[0])
1462 self.full_series = series
1463 self.full_series = series
1463 self.applied = applied
1464 self.applied = applied
1464 self.parse_series()
1465 self.parse_series()
1465 self.series_dirty = 1
1466 self.series_dirty = 1
1466 self.applied_dirty = 1
1467 self.applied_dirty = 1
1467 heads = repo.changelog.heads()
1468 heads = repo.changelog.heads()
1468 if delete:
1469 if delete:
1469 if rev not in heads:
1470 if rev not in heads:
1470 self.ui.warn(_("save entry has children, leaving it alone\n"))
1471 self.ui.warn(_("save entry has children, leaving it alone\n"))
1471 else:
1472 else:
1472 self.ui.warn(_("removing save entry %s\n") % short(rev))
1473 self.ui.warn(_("removing save entry %s\n") % short(rev))
1473 pp = repo.dirstate.parents()
1474 pp = repo.dirstate.parents()
1474 if rev in pp:
1475 if rev in pp:
1475 update = True
1476 update = True
1476 else:
1477 else:
1477 update = False
1478 update = False
1478 self.strip(repo, rev, update=update, backup='strip')
1479 self.strip(repo, rev, update=update, backup='strip')
1479 if qpp:
1480 if qpp:
1480 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1481 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1481 (short(qpp[0]), short(qpp[1])))
1482 (short(qpp[0]), short(qpp[1])))
1482 if qupdate:
1483 if qupdate:
1483 self.ui.status(_("queue directory updating\n"))
1484 self.ui.status(_("queue directory updating\n"))
1484 r = self.qrepo()
1485 r = self.qrepo()
1485 if not r:
1486 if not r:
1486 self.ui.warn(_("Unable to load queue repository\n"))
1487 self.ui.warn(_("Unable to load queue repository\n"))
1487 return 1
1488 return 1
1488 hg.clean(r, qpp[0])
1489 hg.clean(r, qpp[0])
1489
1490
1490 def save(self, repo, msg=None):
1491 def save(self, repo, msg=None):
1491 if len(self.applied) == 0:
1492 if len(self.applied) == 0:
1492 self.ui.warn(_("save: no patches applied, exiting\n"))
1493 self.ui.warn(_("save: no patches applied, exiting\n"))
1493 return 1
1494 return 1
1494 if self.issaveline(self.applied[-1]):
1495 if self.issaveline(self.applied[-1]):
1495 self.ui.warn(_("status is already saved\n"))
1496 self.ui.warn(_("status is already saved\n"))
1496 return 1
1497 return 1
1497
1498
1498 ar = [ ':' + x for x in self.full_series ]
1499 ar = [ ':' + x for x in self.full_series ]
1499 if not msg:
1500 if not msg:
1500 msg = _("hg patches saved state")
1501 msg = _("hg patches saved state")
1501 else:
1502 else:
1502 msg = "hg patches: " + msg.rstrip('\r\n')
1503 msg = "hg patches: " + msg.rstrip('\r\n')
1503 r = self.qrepo()
1504 r = self.qrepo()
1504 if r:
1505 if r:
1505 pp = r.dirstate.parents()
1506 pp = r.dirstate.parents()
1506 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1507 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1507 msg += "\n\nPatch Data:\n"
1508 msg += "\n\nPatch Data:\n"
1508 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1509 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1509 "\n".join(ar) + '\n' or "")
1510 "\n".join(ar) + '\n' or "")
1510 n = repo.commit(text, force=True)
1511 n = repo.commit(text, force=True)
1511 if not n:
1512 if not n:
1512 self.ui.warn(_("repo commit failed\n"))
1513 self.ui.warn(_("repo commit failed\n"))
1513 return 1
1514 return 1
1514 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1515 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1515 self.applied_dirty = 1
1516 self.applied_dirty = 1
1516 self.removeundo(repo)
1517 self.removeundo(repo)
1517
1518
1518 def full_series_end(self):
1519 def full_series_end(self):
1519 if len(self.applied) > 0:
1520 if len(self.applied) > 0:
1520 p = self.applied[-1].name
1521 p = self.applied[-1].name
1521 end = self.find_series(p)
1522 end = self.find_series(p)
1522 if end is None:
1523 if end is None:
1523 return len(self.full_series)
1524 return len(self.full_series)
1524 return end + 1
1525 return end + 1
1525 return 0
1526 return 0
1526
1527
1527 def series_end(self, all_patches=False):
1528 def series_end(self, all_patches=False):
1528 """If all_patches is False, return the index of the next pushable patch
1529 """If all_patches is False, return the index of the next pushable patch
1529 in the series, or the series length. If all_patches is True, return the
1530 in the series, or the series length. If all_patches is True, return the
1530 index of the first patch past the last applied one.
1531 index of the first patch past the last applied one.
1531 """
1532 """
1532 end = 0
1533 end = 0
1533 def next(start):
1534 def next(start):
1534 if all_patches:
1535 if all_patches:
1535 return start
1536 return start
1536 i = start
1537 i = start
1537 while i < len(self.series):
1538 while i < len(self.series):
1538 p, reason = self.pushable(i)
1539 p, reason = self.pushable(i)
1539 if p:
1540 if p:
1540 break
1541 break
1541 self.explain_pushable(i)
1542 self.explain_pushable(i)
1542 i += 1
1543 i += 1
1543 return i
1544 return i
1544 if len(self.applied) > 0:
1545 if len(self.applied) > 0:
1545 p = self.applied[-1].name
1546 p = self.applied[-1].name
1546 try:
1547 try:
1547 end = self.series.index(p)
1548 end = self.series.index(p)
1548 except ValueError:
1549 except ValueError:
1549 return 0
1550 return 0
1550 return next(end + 1)
1551 return next(end + 1)
1551 return next(end)
1552 return next(end)
1552
1553
1553 def appliedname(self, index):
1554 def appliedname(self, index):
1554 pname = self.applied[index].name
1555 pname = self.applied[index].name
1555 if not self.ui.verbose:
1556 if not self.ui.verbose:
1556 p = pname
1557 p = pname
1557 else:
1558 else:
1558 p = str(self.series.index(pname)) + " " + pname
1559 p = str(self.series.index(pname)) + " " + pname
1559 return p
1560 return p
1560
1561
1561 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1562 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1562 force=None, git=False):
1563 force=None, git=False):
1563 def checkseries(patchname):
1564 def checkseries(patchname):
1564 if patchname in self.series:
1565 if patchname in self.series:
1565 raise util.Abort(_('patch %s is already in the series file')
1566 raise util.Abort(_('patch %s is already in the series file')
1566 % patchname)
1567 % patchname)
1567 def checkfile(patchname):
1568 def checkfile(patchname):
1568 if not force and os.path.exists(self.join(patchname)):
1569 if not force and os.path.exists(self.join(patchname)):
1569 raise util.Abort(_('patch "%s" already exists')
1570 raise util.Abort(_('patch "%s" already exists')
1570 % patchname)
1571 % patchname)
1571
1572
1572 if rev:
1573 if rev:
1573 if files:
1574 if files:
1574 raise util.Abort(_('option "-r" not valid when importing '
1575 raise util.Abort(_('option "-r" not valid when importing '
1575 'files'))
1576 'files'))
1576 rev = cmdutil.revrange(repo, rev)
1577 rev = cmdutil.revrange(repo, rev)
1577 rev.sort(reverse=True)
1578 rev.sort(reverse=True)
1578 if (len(files) > 1 or len(rev) > 1) and patchname:
1579 if (len(files) > 1 or len(rev) > 1) and patchname:
1579 raise util.Abort(_('option "-n" not valid when importing multiple '
1580 raise util.Abort(_('option "-n" not valid when importing multiple '
1580 'patches'))
1581 'patches'))
1581 i = 0
1582 i = 0
1582 added = []
1583 added = []
1583 if rev:
1584 if rev:
1584 # If mq patches are applied, we can only import revisions
1585 # If mq patches are applied, we can only import revisions
1585 # that form a linear path to qbase.
1586 # that form a linear path to qbase.
1586 # Otherwise, they should form a linear path to a head.
1587 # Otherwise, they should form a linear path to a head.
1587 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1588 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1588 if len(heads) > 1:
1589 if len(heads) > 1:
1589 raise util.Abort(_('revision %d is the root of more than one '
1590 raise util.Abort(_('revision %d is the root of more than one '
1590 'branch') % rev[-1])
1591 'branch') % rev[-1])
1591 if self.applied:
1592 if self.applied:
1592 base = hex(repo.changelog.node(rev[0]))
1593 base = hex(repo.changelog.node(rev[0]))
1593 if base in [n.rev for n in self.applied]:
1594 if base in [n.rev for n in self.applied]:
1594 raise util.Abort(_('revision %d is already managed')
1595 raise util.Abort(_('revision %d is already managed')
1595 % rev[0])
1596 % rev[0])
1596 if heads != [bin(self.applied[-1].rev)]:
1597 if heads != [bin(self.applied[-1].rev)]:
1597 raise util.Abort(_('revision %d is not the parent of '
1598 raise util.Abort(_('revision %d is not the parent of '
1598 'the queue') % rev[0])
1599 'the queue') % rev[0])
1599 base = repo.changelog.rev(bin(self.applied[0].rev))
1600 base = repo.changelog.rev(bin(self.applied[0].rev))
1600 lastparent = repo.changelog.parentrevs(base)[0]
1601 lastparent = repo.changelog.parentrevs(base)[0]
1601 else:
1602 else:
1602 if heads != [repo.changelog.node(rev[0])]:
1603 if heads != [repo.changelog.node(rev[0])]:
1603 raise util.Abort(_('revision %d has unmanaged children')
1604 raise util.Abort(_('revision %d has unmanaged children')
1604 % rev[0])
1605 % rev[0])
1605 lastparent = None
1606 lastparent = None
1606
1607
1607 if git:
1608 if git:
1608 self.diffopts().git = True
1609 self.diffopts().git = True
1609
1610
1610 for r in rev:
1611 for r in rev:
1611 p1, p2 = repo.changelog.parentrevs(r)
1612 p1, p2 = repo.changelog.parentrevs(r)
1612 n = repo.changelog.node(r)
1613 n = repo.changelog.node(r)
1613 if p2 != nullrev:
1614 if p2 != nullrev:
1614 raise util.Abort(_('cannot import merge revision %d') % r)
1615 raise util.Abort(_('cannot import merge revision %d') % r)
1615 if lastparent and lastparent != r:
1616 if lastparent and lastparent != r:
1616 raise util.Abort(_('revision %d is not the parent of %d')
1617 raise util.Abort(_('revision %d is not the parent of %d')
1617 % (r, lastparent))
1618 % (r, lastparent))
1618 lastparent = p1
1619 lastparent = p1
1619
1620
1620 if not patchname:
1621 if not patchname:
1621 patchname = normname('%d.diff' % r)
1622 patchname = normname('%d.diff' % r)
1622 self.check_reserved_name(patchname)
1623 self.check_reserved_name(patchname)
1623 checkseries(patchname)
1624 checkseries(patchname)
1624 checkfile(patchname)
1625 checkfile(patchname)
1625 self.full_series.insert(0, patchname)
1626 self.full_series.insert(0, patchname)
1626
1627
1627 patchf = self.opener(patchname, "w")
1628 patchf = self.opener(patchname, "w")
1628 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1629 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1629 patchf.close()
1630 patchf.close()
1630
1631
1631 se = statusentry(hex(n), patchname)
1632 se = statusentry(hex(n), patchname)
1632 self.applied.insert(0, se)
1633 self.applied.insert(0, se)
1633
1634
1634 added.append(patchname)
1635 added.append(patchname)
1635 patchname = None
1636 patchname = None
1636 self.parse_series()
1637 self.parse_series()
1637 self.applied_dirty = 1
1638 self.applied_dirty = 1
1638
1639
1639 for filename in files:
1640 for filename in files:
1640 if existing:
1641 if existing:
1641 if filename == '-':
1642 if filename == '-':
1642 raise util.Abort(_('-e is incompatible with import from -'))
1643 raise util.Abort(_('-e is incompatible with import from -'))
1643 if not patchname:
1644 if not patchname:
1644 patchname = normname(filename)
1645 patchname = normname(filename)
1645 self.check_reserved_name(patchname)
1646 self.check_reserved_name(patchname)
1646 if not os.path.isfile(self.join(patchname)):
1647 if not os.path.isfile(self.join(patchname)):
1647 raise util.Abort(_("patch %s does not exist") % patchname)
1648 raise util.Abort(_("patch %s does not exist") % patchname)
1648 else:
1649 else:
1649 try:
1650 try:
1650 if filename == '-':
1651 if filename == '-':
1651 if not patchname:
1652 if not patchname:
1652 raise util.Abort(_('need --name to import a patch from -'))
1653 raise util.Abort(_('need --name to import a patch from -'))
1653 text = sys.stdin.read()
1654 text = sys.stdin.read()
1654 else:
1655 else:
1655 text = url.open(self.ui, filename).read()
1656 text = url.open(self.ui, filename).read()
1656 except (OSError, IOError):
1657 except (OSError, IOError):
1657 raise util.Abort(_("unable to read %s") % filename)
1658 raise util.Abort(_("unable to read %s") % filename)
1658 if not patchname:
1659 if not patchname:
1659 patchname = normname(os.path.basename(filename))
1660 patchname = normname(os.path.basename(filename))
1660 self.check_reserved_name(patchname)
1661 self.check_reserved_name(patchname)
1661 checkfile(patchname)
1662 checkfile(patchname)
1662 patchf = self.opener(patchname, "w")
1663 patchf = self.opener(patchname, "w")
1663 patchf.write(text)
1664 patchf.write(text)
1664 if not force:
1665 if not force:
1665 checkseries(patchname)
1666 checkseries(patchname)
1666 if patchname not in self.series:
1667 if patchname not in self.series:
1667 index = self.full_series_end() + i
1668 index = self.full_series_end() + i
1668 self.full_series[index:index] = [patchname]
1669 self.full_series[index:index] = [patchname]
1669 self.parse_series()
1670 self.parse_series()
1670 self.ui.warn(_("adding %s to series file\n") % patchname)
1671 self.ui.warn(_("adding %s to series file\n") % patchname)
1671 i += 1
1672 i += 1
1672 added.append(patchname)
1673 added.append(patchname)
1673 patchname = None
1674 patchname = None
1674 self.series_dirty = 1
1675 self.series_dirty = 1
1675 qrepo = self.qrepo()
1676 qrepo = self.qrepo()
1676 if qrepo:
1677 if qrepo:
1677 qrepo.add(added)
1678 qrepo.add(added)
1678
1679
1679 def delete(ui, repo, *patches, **opts):
1680 def delete(ui, repo, *patches, **opts):
1680 """remove patches from queue
1681 """remove patches from queue
1681
1682
1682 The patches must not be applied, and at least one patch is required. With
1683 The patches must not be applied, and at least one patch is required. With
1683 -k/--keep, the patch files are preserved in the patch directory.
1684 -k/--keep, the patch files are preserved in the patch directory.
1684
1685
1685 To stop managing a patch and move it into permanent history,
1686 To stop managing a patch and move it into permanent history,
1686 use the qfinish command."""
1687 use the qfinish command."""
1687 q = repo.mq
1688 q = repo.mq
1688 q.delete(repo, patches, opts)
1689 q.delete(repo, patches, opts)
1689 q.save_dirty()
1690 q.save_dirty()
1690 return 0
1691 return 0
1691
1692
1692 def applied(ui, repo, patch=None, **opts):
1693 def applied(ui, repo, patch=None, **opts):
1693 """print the patches already applied"""
1694 """print the patches already applied"""
1694
1695
1695 q = repo.mq
1696 q = repo.mq
1696 l = len(q.applied)
1697 l = len(q.applied)
1697
1698
1698 if patch:
1699 if patch:
1699 if patch not in q.series:
1700 if patch not in q.series:
1700 raise util.Abort(_("patch %s is not in series file") % patch)
1701 raise util.Abort(_("patch %s is not in series file") % patch)
1701 end = q.series.index(patch) + 1
1702 end = q.series.index(patch) + 1
1702 else:
1703 else:
1703 end = q.series_end(True)
1704 end = q.series_end(True)
1704
1705
1705 if opts.get('last') and not end:
1706 if opts.get('last') and not end:
1706 ui.write(_("no patches applied\n"))
1707 ui.write(_("no patches applied\n"))
1707 return 1
1708 return 1
1708 elif opts.get('last') and end == 1:
1709 elif opts.get('last') and end == 1:
1709 ui.write(_("only one patch applied\n"))
1710 ui.write(_("only one patch applied\n"))
1710 return 1
1711 return 1
1711 elif opts.get('last'):
1712 elif opts.get('last'):
1712 start = end - 2
1713 start = end - 2
1713 end = 1
1714 end = 1
1714 else:
1715 else:
1715 start = 0
1716 start = 0
1716
1717
1717 return q.qseries(repo, length=end, start=start, status='A',
1718 return q.qseries(repo, length=end, start=start, status='A',
1718 summary=opts.get('summary'))
1719 summary=opts.get('summary'))
1719
1720
1720 def unapplied(ui, repo, patch=None, **opts):
1721 def unapplied(ui, repo, patch=None, **opts):
1721 """print the patches not yet applied"""
1722 """print the patches not yet applied"""
1722
1723
1723 q = repo.mq
1724 q = repo.mq
1724 if patch:
1725 if patch:
1725 if patch not in q.series:
1726 if patch not in q.series:
1726 raise util.Abort(_("patch %s is not in series file") % patch)
1727 raise util.Abort(_("patch %s is not in series file") % patch)
1727 start = q.series.index(patch) + 1
1728 start = q.series.index(patch) + 1
1728 else:
1729 else:
1729 start = q.series_end(True)
1730 start = q.series_end(True)
1730
1731
1731 if start == len(q.series) and opts.get('first'):
1732 if start == len(q.series) and opts.get('first'):
1732 ui.write(_("all patches applied\n"))
1733 ui.write(_("all patches applied\n"))
1733 return 1
1734 return 1
1734
1735
1735 length = opts.get('first') and 1 or None
1736 length = opts.get('first') and 1 or None
1736 return q.qseries(repo, start=start, length=length, status='U',
1737 return q.qseries(repo, start=start, length=length, status='U',
1737 summary=opts.get('summary'))
1738 summary=opts.get('summary'))
1738
1739
1739 def qimport(ui, repo, *filename, **opts):
1740 def qimport(ui, repo, *filename, **opts):
1740 """import a patch
1741 """import a patch
1741
1742
1742 The patch is inserted into the series after the last applied
1743 The patch is inserted into the series after the last applied
1743 patch. If no patches have been applied, qimport prepends the patch
1744 patch. If no patches have been applied, qimport prepends the patch
1744 to the series.
1745 to the series.
1745
1746
1746 The patch will have the same name as its source file unless you
1747 The patch will have the same name as its source file unless you
1747 give it a new one with -n/--name.
1748 give it a new one with -n/--name.
1748
1749
1749 You can register an existing patch inside the patch directory with
1750 You can register an existing patch inside the patch directory with
1750 the -e/--existing flag.
1751 the -e/--existing flag.
1751
1752
1752 With -f/--force, an existing patch of the same name will be
1753 With -f/--force, an existing patch of the same name will be
1753 overwritten.
1754 overwritten.
1754
1755
1755 An existing changeset may be placed under mq control with -r/--rev
1756 An existing changeset may be placed under mq control with -r/--rev
1756 (e.g. qimport --rev tip -n patch will place tip under mq control).
1757 (e.g. qimport --rev tip -n patch will place tip under mq control).
1757 With -g/--git, patches imported with --rev will use the git diff
1758 With -g/--git, patches imported with --rev will use the git diff
1758 format. See the diffs help topic for information on why this is
1759 format. See the diffs help topic for information on why this is
1759 important for preserving rename/copy information and permission
1760 important for preserving rename/copy information and permission
1760 changes.
1761 changes.
1761
1762
1762 To import a patch from standard input, pass - as the patch file.
1763 To import a patch from standard input, pass - as the patch file.
1763 When importing from standard input, a patch name must be specified
1764 When importing from standard input, a patch name must be specified
1764 using the --name flag.
1765 using the --name flag.
1765 """
1766 """
1766 q = repo.mq
1767 q = repo.mq
1767 q.qimport(repo, filename, patchname=opts['name'],
1768 q.qimport(repo, filename, patchname=opts['name'],
1768 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1769 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1769 git=opts['git'])
1770 git=opts['git'])
1770 q.save_dirty()
1771 q.save_dirty()
1771
1772
1772 if opts.get('push') and not opts.get('rev'):
1773 if opts.get('push') and not opts.get('rev'):
1773 return q.push(repo, None)
1774 return q.push(repo, None)
1774 return 0
1775 return 0
1775
1776
1776 def init(ui, repo, **opts):
1777 def init(ui, repo, **opts):
1777 """init a new queue repository
1778 """init a new queue repository
1778
1779
1779 The queue repository is unversioned by default. If
1780 The queue repository is unversioned by default. If
1780 -c/--create-repo is specified, qinit will create a separate nested
1781 -c/--create-repo is specified, qinit will create a separate nested
1781 repository for patches (qinit -c may also be run later to convert
1782 repository for patches (qinit -c may also be run later to convert
1782 an unversioned patch repository into a versioned one). You can use
1783 an unversioned patch repository into a versioned one). You can use
1783 qcommit to commit changes to this queue repository."""
1784 qcommit to commit changes to this queue repository."""
1784 q = repo.mq
1785 q = repo.mq
1785 r = q.init(repo, create=opts['create_repo'])
1786 r = q.init(repo, create=opts['create_repo'])
1786 q.save_dirty()
1787 q.save_dirty()
1787 if r:
1788 if r:
1788 if not os.path.exists(r.wjoin('.hgignore')):
1789 if not os.path.exists(r.wjoin('.hgignore')):
1789 fp = r.wopener('.hgignore', 'w')
1790 fp = r.wopener('.hgignore', 'w')
1790 fp.write('^\\.hg\n')
1791 fp.write('^\\.hg\n')
1791 fp.write('^\\.mq\n')
1792 fp.write('^\\.mq\n')
1792 fp.write('syntax: glob\n')
1793 fp.write('syntax: glob\n')
1793 fp.write('status\n')
1794 fp.write('status\n')
1794 fp.write('guards\n')
1795 fp.write('guards\n')
1795 fp.close()
1796 fp.close()
1796 if not os.path.exists(r.wjoin('series')):
1797 if not os.path.exists(r.wjoin('series')):
1797 r.wopener('series', 'w').close()
1798 r.wopener('series', 'w').close()
1798 r.add(['.hgignore', 'series'])
1799 r.add(['.hgignore', 'series'])
1799 commands.add(ui, r)
1800 commands.add(ui, r)
1800 return 0
1801 return 0
1801
1802
1802 def clone(ui, source, dest=None, **opts):
1803 def clone(ui, source, dest=None, **opts):
1803 '''clone main and patch repository at same time
1804 '''clone main and patch repository at same time
1804
1805
1805 If source is local, destination will have no patches applied. If
1806 If source is local, destination will have no patches applied. If
1806 source is remote, this command can not check if patches are
1807 source is remote, this command can not check if patches are
1807 applied in source, so cannot guarantee that patches are not
1808 applied in source, so cannot guarantee that patches are not
1808 applied in destination. If you clone remote repository, be sure
1809 applied in destination. If you clone remote repository, be sure
1809 before that it has no patches applied.
1810 before that it has no patches applied.
1810
1811
1811 Source patch repository is looked for in <src>/.hg/patches by
1812 Source patch repository is looked for in <src>/.hg/patches by
1812 default. Use -p <url> to change.
1813 default. Use -p <url> to change.
1813
1814
1814 The patch directory must be a nested Mercurial repository, as
1815 The patch directory must be a nested Mercurial repository, as
1815 would be created by qinit -c.
1816 would be created by qinit -c.
1816 '''
1817 '''
1817 def patchdir(repo):
1818 def patchdir(repo):
1818 url = repo.url()
1819 url = repo.url()
1819 if url.endswith('/'):
1820 if url.endswith('/'):
1820 url = url[:-1]
1821 url = url[:-1]
1821 return url + '/.hg/patches'
1822 return url + '/.hg/patches'
1822 if dest is None:
1823 if dest is None:
1823 dest = hg.defaultdest(source)
1824 dest = hg.defaultdest(source)
1824 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1825 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1825 if opts['patches']:
1826 if opts['patches']:
1826 patchespath = ui.expandpath(opts['patches'])
1827 patchespath = ui.expandpath(opts['patches'])
1827 else:
1828 else:
1828 patchespath = patchdir(sr)
1829 patchespath = patchdir(sr)
1829 try:
1830 try:
1830 hg.repository(ui, patchespath)
1831 hg.repository(ui, patchespath)
1831 except error.RepoError:
1832 except error.RepoError:
1832 raise util.Abort(_('versioned patch repository not found'
1833 raise util.Abort(_('versioned patch repository not found'
1833 ' (see qinit -c)'))
1834 ' (see qinit -c)'))
1834 qbase, destrev = None, None
1835 qbase, destrev = None, None
1835 if sr.local():
1836 if sr.local():
1836 if sr.mq.applied:
1837 if sr.mq.applied:
1837 qbase = bin(sr.mq.applied[0].rev)
1838 qbase = bin(sr.mq.applied[0].rev)
1838 if not hg.islocal(dest):
1839 if not hg.islocal(dest):
1839 heads = set(sr.heads())
1840 heads = set(sr.heads())
1840 destrev = list(heads.difference(sr.heads(qbase)))
1841 destrev = list(heads.difference(sr.heads(qbase)))
1841 destrev.append(sr.changelog.parents(qbase)[0])
1842 destrev.append(sr.changelog.parents(qbase)[0])
1842 elif sr.capable('lookup'):
1843 elif sr.capable('lookup'):
1843 try:
1844 try:
1844 qbase = sr.lookup('qbase')
1845 qbase = sr.lookup('qbase')
1845 except error.RepoError:
1846 except error.RepoError:
1846 pass
1847 pass
1847 ui.note(_('cloning main repository\n'))
1848 ui.note(_('cloning main repository\n'))
1848 sr, dr = hg.clone(ui, sr.url(), dest,
1849 sr, dr = hg.clone(ui, sr.url(), dest,
1849 pull=opts['pull'],
1850 pull=opts['pull'],
1850 rev=destrev,
1851 rev=destrev,
1851 update=False,
1852 update=False,
1852 stream=opts['uncompressed'])
1853 stream=opts['uncompressed'])
1853 ui.note(_('cloning patch repository\n'))
1854 ui.note(_('cloning patch repository\n'))
1854 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1855 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1855 pull=opts['pull'], update=not opts['noupdate'],
1856 pull=opts['pull'], update=not opts['noupdate'],
1856 stream=opts['uncompressed'])
1857 stream=opts['uncompressed'])
1857 if dr.local():
1858 if dr.local():
1858 if qbase:
1859 if qbase:
1859 ui.note(_('stripping applied patches from destination '
1860 ui.note(_('stripping applied patches from destination '
1860 'repository\n'))
1861 'repository\n'))
1861 dr.mq.strip(dr, qbase, update=False, backup=None)
1862 dr.mq.strip(dr, qbase, update=False, backup=None)
1862 if not opts['noupdate']:
1863 if not opts['noupdate']:
1863 ui.note(_('updating destination repository\n'))
1864 ui.note(_('updating destination repository\n'))
1864 hg.update(dr, dr.changelog.tip())
1865 hg.update(dr, dr.changelog.tip())
1865
1866
1866 def commit(ui, repo, *pats, **opts):
1867 def commit(ui, repo, *pats, **opts):
1867 """commit changes in the queue repository"""
1868 """commit changes in the queue repository"""
1868 q = repo.mq
1869 q = repo.mq
1869 r = q.qrepo()
1870 r = q.qrepo()
1870 if not r: raise util.Abort('no queue repository')
1871 if not r: raise util.Abort('no queue repository')
1871 commands.commit(r.ui, r, *pats, **opts)
1872 commands.commit(r.ui, r, *pats, **opts)
1872
1873
1873 def series(ui, repo, **opts):
1874 def series(ui, repo, **opts):
1874 """print the entire series file"""
1875 """print the entire series file"""
1875 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1876 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1876 return 0
1877 return 0
1877
1878
1878 def top(ui, repo, **opts):
1879 def top(ui, repo, **opts):
1879 """print the name of the current patch"""
1880 """print the name of the current patch"""
1880 q = repo.mq
1881 q = repo.mq
1881 t = q.applied and q.series_end(True) or 0
1882 t = q.applied and q.series_end(True) or 0
1882 if t:
1883 if t:
1883 return q.qseries(repo, start=t-1, length=1, status='A',
1884 return q.qseries(repo, start=t-1, length=1, status='A',
1884 summary=opts.get('summary'))
1885 summary=opts.get('summary'))
1885 else:
1886 else:
1886 ui.write(_("no patches applied\n"))
1887 ui.write(_("no patches applied\n"))
1887 return 1
1888 return 1
1888
1889
1889 def next(ui, repo, **opts):
1890 def next(ui, repo, **opts):
1890 """print the name of the next patch"""
1891 """print the name of the next patch"""
1891 q = repo.mq
1892 q = repo.mq
1892 end = q.series_end()
1893 end = q.series_end()
1893 if end == len(q.series):
1894 if end == len(q.series):
1894 ui.write(_("all patches applied\n"))
1895 ui.write(_("all patches applied\n"))
1895 return 1
1896 return 1
1896 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1897 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1897
1898
1898 def prev(ui, repo, **opts):
1899 def prev(ui, repo, **opts):
1899 """print the name of the previous patch"""
1900 """print the name of the previous patch"""
1900 q = repo.mq
1901 q = repo.mq
1901 l = len(q.applied)
1902 l = len(q.applied)
1902 if l == 1:
1903 if l == 1:
1903 ui.write(_("only one patch applied\n"))
1904 ui.write(_("only one patch applied\n"))
1904 return 1
1905 return 1
1905 if not l:
1906 if not l:
1906 ui.write(_("no patches applied\n"))
1907 ui.write(_("no patches applied\n"))
1907 return 1
1908 return 1
1908 return q.qseries(repo, start=l-2, length=1, status='A',
1909 return q.qseries(repo, start=l-2, length=1, status='A',
1909 summary=opts.get('summary'))
1910 summary=opts.get('summary'))
1910
1911
1911 def setupheaderopts(ui, opts):
1912 def setupheaderopts(ui, opts):
1912 def do(opt, val):
1913 def do(opt, val):
1913 if not opts[opt] and opts['current' + opt]:
1914 if not opts[opt] and opts['current' + opt]:
1914 opts[opt] = val
1915 opts[opt] = val
1915 do('user', ui.username())
1916 do('user', ui.username())
1916 do('date', "%d %d" % util.makedate())
1917 do('date', "%d %d" % util.makedate())
1917
1918
1918 def new(ui, repo, patch, *args, **opts):
1919 def new(ui, repo, patch, *args, **opts):
1919 """create a new patch
1920 """create a new patch
1920
1921
1921 qnew creates a new patch on top of the currently-applied patch (if
1922 qnew creates a new patch on top of the currently-applied patch (if
1922 any). It will refuse to run if there are any outstanding changes
1923 any). It will refuse to run if there are any outstanding changes
1923 unless -f/--force is specified, in which case the patch will be
1924 unless -f/--force is specified, in which case the patch will be
1924 initialized with them. You may also use -I/--include,
1925 initialized with them. You may also use -I/--include,
1925 -X/--exclude, and/or a list of files after the patch name to add
1926 -X/--exclude, and/or a list of files after the patch name to add
1926 only changes to matching files to the new patch, leaving the rest
1927 only changes to matching files to the new patch, leaving the rest
1927 as uncommitted modifications.
1928 as uncommitted modifications.
1928
1929
1929 -u/--user and -d/--date can be used to set the (given) user and
1930 -u/--user and -d/--date can be used to set the (given) user and
1930 date, respectively. -U/--currentuser and -D/--currentdate set user
1931 date, respectively. -U/--currentuser and -D/--currentdate set user
1931 to current user and date to current date.
1932 to current user and date to current date.
1932
1933
1933 -e/--edit, -m/--message or -l/--logfile set the patch header as
1934 -e/--edit, -m/--message or -l/--logfile set the patch header as
1934 well as the commit message. If none is specified, the header is
1935 well as the commit message. If none is specified, the header is
1935 empty and the commit message is '[mq]: PATCH'.
1936 empty and the commit message is '[mq]: PATCH'.
1936
1937
1937 Use the -g/--git option to keep the patch in the git extended diff
1938 Use the -g/--git option to keep the patch in the git extended diff
1938 format. Read the diffs help topic for more information on why this
1939 format. Read the diffs help topic for more information on why this
1939 is important for preserving permission changes and copy/rename
1940 is important for preserving permission changes and copy/rename
1940 information.
1941 information.
1941 """
1942 """
1942 msg = cmdutil.logmessage(opts)
1943 msg = cmdutil.logmessage(opts)
1943 def getmsg(): return ui.edit(msg, ui.username())
1944 def getmsg(): return ui.edit(msg, ui.username())
1944 q = repo.mq
1945 q = repo.mq
1945 opts['msg'] = msg
1946 opts['msg'] = msg
1946 if opts.get('edit'):
1947 if opts.get('edit'):
1947 opts['msg'] = getmsg
1948 opts['msg'] = getmsg
1948 else:
1949 else:
1949 opts['msg'] = msg
1950 opts['msg'] = msg
1950 setupheaderopts(ui, opts)
1951 setupheaderopts(ui, opts)
1951 q.new(repo, patch, *args, **opts)
1952 q.new(repo, patch, *args, **opts)
1952 q.save_dirty()
1953 q.save_dirty()
1953 return 0
1954 return 0
1954
1955
1955 def refresh(ui, repo, *pats, **opts):
1956 def refresh(ui, repo, *pats, **opts):
1956 """update the current patch
1957 """update the current patch
1957
1958
1958 If any file patterns are provided, the refreshed patch will
1959 If any file patterns are provided, the refreshed patch will
1959 contain only the modifications that match those patterns; the
1960 contain only the modifications that match those patterns; the
1960 remaining modifications will remain in the working directory.
1961 remaining modifications will remain in the working directory.
1961
1962
1962 If -s/--short is specified, files currently included in the patch
1963 If -s/--short is specified, files currently included in the patch
1963 will be refreshed just like matched files and remain in the patch.
1964 will be refreshed just like matched files and remain in the patch.
1964
1965
1965 hg add/remove/copy/rename work as usual, though you might want to
1966 hg add/remove/copy/rename work as usual, though you might want to
1966 use git-style patches (-g/--git or [diff] git=1) to track copies
1967 use git-style patches (-g/--git or [diff] git=1) to track copies
1967 and renames. See the diffs help topic for more information on the
1968 and renames. See the diffs help topic for more information on the
1968 git diff format.
1969 git diff format.
1969 """
1970 """
1970 q = repo.mq
1971 q = repo.mq
1971 message = cmdutil.logmessage(opts)
1972 message = cmdutil.logmessage(opts)
1972 if opts['edit']:
1973 if opts['edit']:
1973 if not q.applied:
1974 if not q.applied:
1974 ui.write(_("no patches applied\n"))
1975 ui.write(_("no patches applied\n"))
1975 return 1
1976 return 1
1976 if message:
1977 if message:
1977 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1978 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1978 patch = q.applied[-1].name
1979 patch = q.applied[-1].name
1979 ph = patchheader(q.join(patch))
1980 ph = patchheader(q.join(patch))
1980 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
1981 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
1981 setupheaderopts(ui, opts)
1982 setupheaderopts(ui, opts)
1982 ret = q.refresh(repo, pats, msg=message, **opts)
1983 ret = q.refresh(repo, pats, msg=message, **opts)
1983 q.save_dirty()
1984 q.save_dirty()
1984 return ret
1985 return ret
1985
1986
1986 def diff(ui, repo, *pats, **opts):
1987 def diff(ui, repo, *pats, **opts):
1987 """diff of the current patch and subsequent modifications
1988 """diff of the current patch and subsequent modifications
1988
1989
1989 Shows a diff which includes the current patch as well as any
1990 Shows a diff which includes the current patch as well as any
1990 changes which have been made in the working directory since the
1991 changes which have been made in the working directory since the
1991 last refresh (thus showing what the current patch would become
1992 last refresh (thus showing what the current patch would become
1992 after a qrefresh).
1993 after a qrefresh).
1993
1994
1994 Use 'hg diff' if you only want to see the changes made since the
1995 Use 'hg diff' if you only want to see the changes made since the
1995 last qrefresh, or 'hg export qtip' if you want to see changes made
1996 last qrefresh, or 'hg export qtip' if you want to see changes made
1996 by the current patch without including changes made since the
1997 by the current patch without including changes made since the
1997 qrefresh.
1998 qrefresh.
1998 """
1999 """
1999 repo.mq.diff(repo, pats, opts)
2000 repo.mq.diff(repo, pats, opts)
2000 return 0
2001 return 0
2001
2002
2002 def fold(ui, repo, *files, **opts):
2003 def fold(ui, repo, *files, **opts):
2003 """fold the named patches into the current patch
2004 """fold the named patches into the current patch
2004
2005
2005 Patches must not yet be applied. Each patch will be successively
2006 Patches must not yet be applied. Each patch will be successively
2006 applied to the current patch in the order given. If all the
2007 applied to the current patch in the order given. If all the
2007 patches apply successfully, the current patch will be refreshed
2008 patches apply successfully, the current patch will be refreshed
2008 with the new cumulative patch, and the folded patches will be
2009 with the new cumulative patch, and the folded patches will be
2009 deleted. With -k/--keep, the folded patch files will not be
2010 deleted. With -k/--keep, the folded patch files will not be
2010 removed afterwards.
2011 removed afterwards.
2011
2012
2012 The header for each folded patch will be concatenated with the
2013 The header for each folded patch will be concatenated with the
2013 current patch header, separated by a line of '* * *'."""
2014 current patch header, separated by a line of '* * *'."""
2014
2015
2015 q = repo.mq
2016 q = repo.mq
2016
2017
2017 if not files:
2018 if not files:
2018 raise util.Abort(_('qfold requires at least one patch name'))
2019 raise util.Abort(_('qfold requires at least one patch name'))
2019 if not q.check_toppatch(repo):
2020 if not q.check_toppatch(repo):
2020 raise util.Abort(_('No patches applied'))
2021 raise util.Abort(_('No patches applied'))
2021 q.check_localchanges(repo)
2022 q.check_localchanges(repo)
2022
2023
2023 message = cmdutil.logmessage(opts)
2024 message = cmdutil.logmessage(opts)
2024 if opts['edit']:
2025 if opts['edit']:
2025 if message:
2026 if message:
2026 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2027 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2027
2028
2028 parent = q.lookup('qtip')
2029 parent = q.lookup('qtip')
2029 patches = []
2030 patches = []
2030 messages = []
2031 messages = []
2031 for f in files:
2032 for f in files:
2032 p = q.lookup(f)
2033 p = q.lookup(f)
2033 if p in patches or p == parent:
2034 if p in patches or p == parent:
2034 ui.warn(_('Skipping already folded patch %s') % p)
2035 ui.warn(_('Skipping already folded patch %s') % p)
2035 if q.isapplied(p):
2036 if q.isapplied(p):
2036 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2037 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2037 patches.append(p)
2038 patches.append(p)
2038
2039
2039 for p in patches:
2040 for p in patches:
2040 if not message:
2041 if not message:
2041 ph = patchheader(q.join(p))
2042 ph = patchheader(q.join(p))
2042 if ph.message:
2043 if ph.message:
2043 messages.append(ph.message)
2044 messages.append(ph.message)
2044 pf = q.join(p)
2045 pf = q.join(p)
2045 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2046 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2046 if not patchsuccess:
2047 if not patchsuccess:
2047 raise util.Abort(_('Error folding patch %s') % p)
2048 raise util.Abort(_('Error folding patch %s') % p)
2048 patch.updatedir(ui, repo, files)
2049 patch.updatedir(ui, repo, files)
2049
2050
2050 if not message:
2051 if not message:
2051 ph = patchheader(q.join(parent))
2052 ph = patchheader(q.join(parent))
2052 message, user = ph.message, ph.user
2053 message, user = ph.message, ph.user
2053 for msg in messages:
2054 for msg in messages:
2054 message.append('* * *')
2055 message.append('* * *')
2055 message.extend(msg)
2056 message.extend(msg)
2056 message = '\n'.join(message)
2057 message = '\n'.join(message)
2057
2058
2058 if opts['edit']:
2059 if opts['edit']:
2059 message = ui.edit(message, user or ui.username())
2060 message = ui.edit(message, user or ui.username())
2060
2061
2061 q.refresh(repo, msg=message)
2062 q.refresh(repo, msg=message)
2062 q.delete(repo, patches, opts)
2063 q.delete(repo, patches, opts)
2063 q.save_dirty()
2064 q.save_dirty()
2064
2065
2065 def goto(ui, repo, patch, **opts):
2066 def goto(ui, repo, patch, **opts):
2066 '''push or pop patches until named patch is at top of stack'''
2067 '''push or pop patches until named patch is at top of stack'''
2067 q = repo.mq
2068 q = repo.mq
2068 patch = q.lookup(patch)
2069 patch = q.lookup(patch)
2069 if q.isapplied(patch):
2070 if q.isapplied(patch):
2070 ret = q.pop(repo, patch, force=opts['force'])
2071 ret = q.pop(repo, patch, force=opts['force'])
2071 else:
2072 else:
2072 ret = q.push(repo, patch, force=opts['force'])
2073 ret = q.push(repo, patch, force=opts['force'])
2073 q.save_dirty()
2074 q.save_dirty()
2074 return ret
2075 return ret
2075
2076
2076 def guard(ui, repo, *args, **opts):
2077 def guard(ui, repo, *args, **opts):
2077 '''set or print guards for a patch
2078 '''set or print guards for a patch
2078
2079
2079 Guards control whether a patch can be pushed. A patch with no
2080 Guards control whether a patch can be pushed. A patch with no
2080 guards is always pushed. A patch with a positive guard ("+foo") is
2081 guards is always pushed. A patch with a positive guard ("+foo") is
2081 pushed only if the qselect command has activated it. A patch with
2082 pushed only if the qselect command has activated it. A patch with
2082 a negative guard ("-foo") is never pushed if the qselect command
2083 a negative guard ("-foo") is never pushed if the qselect command
2083 has activated it.
2084 has activated it.
2084
2085
2085 With no arguments, print the currently active guards.
2086 With no arguments, print the currently active guards.
2086 With arguments, set guards for the named patch.
2087 With arguments, set guards for the named patch.
2087 NOTE: Specifying negative guards now requires '--'.
2088 NOTE: Specifying negative guards now requires '--'.
2088
2089
2089 To set guards on another patch:
2090 To set guards on another patch:
2090 hg qguard -- other.patch +2.6.17 -stable
2091 hg qguard -- other.patch +2.6.17 -stable
2091 '''
2092 '''
2092 def status(idx):
2093 def status(idx):
2093 guards = q.series_guards[idx] or ['unguarded']
2094 guards = q.series_guards[idx] or ['unguarded']
2094 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2095 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2095 q = repo.mq
2096 q = repo.mq
2096 patch = None
2097 patch = None
2097 args = list(args)
2098 args = list(args)
2098 if opts['list']:
2099 if opts['list']:
2099 if args or opts['none']:
2100 if args or opts['none']:
2100 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2101 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2101 for i in xrange(len(q.series)):
2102 for i in xrange(len(q.series)):
2102 status(i)
2103 status(i)
2103 return
2104 return
2104 if not args or args[0][0:1] in '-+':
2105 if not args or args[0][0:1] in '-+':
2105 if not q.applied:
2106 if not q.applied:
2106 raise util.Abort(_('no patches applied'))
2107 raise util.Abort(_('no patches applied'))
2107 patch = q.applied[-1].name
2108 patch = q.applied[-1].name
2108 if patch is None and args[0][0:1] not in '-+':
2109 if patch is None and args[0][0:1] not in '-+':
2109 patch = args.pop(0)
2110 patch = args.pop(0)
2110 if patch is None:
2111 if patch is None:
2111 raise util.Abort(_('no patch to work with'))
2112 raise util.Abort(_('no patch to work with'))
2112 if args or opts['none']:
2113 if args or opts['none']:
2113 idx = q.find_series(patch)
2114 idx = q.find_series(patch)
2114 if idx is None:
2115 if idx is None:
2115 raise util.Abort(_('no patch named %s') % patch)
2116 raise util.Abort(_('no patch named %s') % patch)
2116 q.set_guards(idx, args)
2117 q.set_guards(idx, args)
2117 q.save_dirty()
2118 q.save_dirty()
2118 else:
2119 else:
2119 status(q.series.index(q.lookup(patch)))
2120 status(q.series.index(q.lookup(patch)))
2120
2121
2121 def header(ui, repo, patch=None):
2122 def header(ui, repo, patch=None):
2122 """print the header of the topmost or specified patch"""
2123 """print the header of the topmost or specified patch"""
2123 q = repo.mq
2124 q = repo.mq
2124
2125
2125 if patch:
2126 if patch:
2126 patch = q.lookup(patch)
2127 patch = q.lookup(patch)
2127 else:
2128 else:
2128 if not q.applied:
2129 if not q.applied:
2129 ui.write('no patches applied\n')
2130 ui.write('no patches applied\n')
2130 return 1
2131 return 1
2131 patch = q.lookup('qtip')
2132 patch = q.lookup('qtip')
2132 ph = patchheader(repo.mq.join(patch))
2133 ph = patchheader(repo.mq.join(patch))
2133
2134
2134 ui.write('\n'.join(ph.message) + '\n')
2135 ui.write('\n'.join(ph.message) + '\n')
2135
2136
2136 def lastsavename(path):
2137 def lastsavename(path):
2137 (directory, base) = os.path.split(path)
2138 (directory, base) = os.path.split(path)
2138 names = os.listdir(directory)
2139 names = os.listdir(directory)
2139 namere = re.compile("%s.([0-9]+)" % base)
2140 namere = re.compile("%s.([0-9]+)" % base)
2140 maxindex = None
2141 maxindex = None
2141 maxname = None
2142 maxname = None
2142 for f in names:
2143 for f in names:
2143 m = namere.match(f)
2144 m = namere.match(f)
2144 if m:
2145 if m:
2145 index = int(m.group(1))
2146 index = int(m.group(1))
2146 if maxindex is None or index > maxindex:
2147 if maxindex is None or index > maxindex:
2147 maxindex = index
2148 maxindex = index
2148 maxname = f
2149 maxname = f
2149 if maxname:
2150 if maxname:
2150 return (os.path.join(directory, maxname), maxindex)
2151 return (os.path.join(directory, maxname), maxindex)
2151 return (None, None)
2152 return (None, None)
2152
2153
2153 def savename(path):
2154 def savename(path):
2154 (last, index) = lastsavename(path)
2155 (last, index) = lastsavename(path)
2155 if last is None:
2156 if last is None:
2156 index = 0
2157 index = 0
2157 newpath = path + ".%d" % (index + 1)
2158 newpath = path + ".%d" % (index + 1)
2158 return newpath
2159 return newpath
2159
2160
2160 def push(ui, repo, patch=None, **opts):
2161 def push(ui, repo, patch=None, **opts):
2161 """push the next patch onto the stack
2162 """push the next patch onto the stack
2162
2163
2163 When -f/--force is applied, all local changes in patched files
2164 When -f/--force is applied, all local changes in patched files
2164 will be lost.
2165 will be lost.
2165 """
2166 """
2166 q = repo.mq
2167 q = repo.mq
2167 mergeq = None
2168 mergeq = None
2168
2169
2169 if opts['merge']:
2170 if opts['merge']:
2170 if opts['name']:
2171 if opts['name']:
2171 newpath = repo.join(opts['name'])
2172 newpath = repo.join(opts['name'])
2172 else:
2173 else:
2173 newpath, i = lastsavename(q.path)
2174 newpath, i = lastsavename(q.path)
2174 if not newpath:
2175 if not newpath:
2175 ui.warn(_("no saved queues found, please use -n\n"))
2176 ui.warn(_("no saved queues found, please use -n\n"))
2176 return 1
2177 return 1
2177 mergeq = queue(ui, repo.join(""), newpath)
2178 mergeq = queue(ui, repo.join(""), newpath)
2178 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2179 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2179 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2180 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2180 mergeq=mergeq, all=opts.get('all'))
2181 mergeq=mergeq, all=opts.get('all'))
2181 return ret
2182 return ret
2182
2183
2183 def pop(ui, repo, patch=None, **opts):
2184 def pop(ui, repo, patch=None, **opts):
2184 """pop the current patch off the stack
2185 """pop the current patch off the stack
2185
2186
2186 By default, pops off the top of the patch stack. If given a patch
2187 By default, pops off the top of the patch stack. If given a patch
2187 name, keeps popping off patches until the named patch is at the
2188 name, keeps popping off patches until the named patch is at the
2188 top of the stack.
2189 top of the stack.
2189 """
2190 """
2190 localupdate = True
2191 localupdate = True
2191 if opts['name']:
2192 if opts['name']:
2192 q = queue(ui, repo.join(""), repo.join(opts['name']))
2193 q = queue(ui, repo.join(""), repo.join(opts['name']))
2193 ui.warn(_('using patch queue: %s\n') % q.path)
2194 ui.warn(_('using patch queue: %s\n') % q.path)
2194 localupdate = False
2195 localupdate = False
2195 else:
2196 else:
2196 q = repo.mq
2197 q = repo.mq
2197 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2198 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2198 all=opts['all'])
2199 all=opts['all'])
2199 q.save_dirty()
2200 q.save_dirty()
2200 return ret
2201 return ret
2201
2202
2202 def rename(ui, repo, patch, name=None, **opts):
2203 def rename(ui, repo, patch, name=None, **opts):
2203 """rename a patch
2204 """rename a patch
2204
2205
2205 With one argument, renames the current patch to PATCH1.
2206 With one argument, renames the current patch to PATCH1.
2206 With two arguments, renames PATCH1 to PATCH2."""
2207 With two arguments, renames PATCH1 to PATCH2."""
2207
2208
2208 q = repo.mq
2209 q = repo.mq
2209
2210
2210 if not name:
2211 if not name:
2211 name = patch
2212 name = patch
2212 patch = None
2213 patch = None
2213
2214
2214 if patch:
2215 if patch:
2215 patch = q.lookup(patch)
2216 patch = q.lookup(patch)
2216 else:
2217 else:
2217 if not q.applied:
2218 if not q.applied:
2218 ui.write(_('no patches applied\n'))
2219 ui.write(_('no patches applied\n'))
2219 return
2220 return
2220 patch = q.lookup('qtip')
2221 patch = q.lookup('qtip')
2221 absdest = q.join(name)
2222 absdest = q.join(name)
2222 if os.path.isdir(absdest):
2223 if os.path.isdir(absdest):
2223 name = normname(os.path.join(name, os.path.basename(patch)))
2224 name = normname(os.path.join(name, os.path.basename(patch)))
2224 absdest = q.join(name)
2225 absdest = q.join(name)
2225 if os.path.exists(absdest):
2226 if os.path.exists(absdest):
2226 raise util.Abort(_('%s already exists') % absdest)
2227 raise util.Abort(_('%s already exists') % absdest)
2227
2228
2228 if name in q.series:
2229 if name in q.series:
2229 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2230 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2230
2231
2231 if ui.verbose:
2232 if ui.verbose:
2232 ui.write('renaming %s to %s\n' % (patch, name))
2233 ui.write('renaming %s to %s\n' % (patch, name))
2233 i = q.find_series(patch)
2234 i = q.find_series(patch)
2234 guards = q.guard_re.findall(q.full_series[i])
2235 guards = q.guard_re.findall(q.full_series[i])
2235 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2236 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2236 q.parse_series()
2237 q.parse_series()
2237 q.series_dirty = 1
2238 q.series_dirty = 1
2238
2239
2239 info = q.isapplied(patch)
2240 info = q.isapplied(patch)
2240 if info:
2241 if info:
2241 q.applied[info[0]] = statusentry(info[1], name)
2242 q.applied[info[0]] = statusentry(info[1], name)
2242 q.applied_dirty = 1
2243 q.applied_dirty = 1
2243
2244
2244 util.rename(q.join(patch), absdest)
2245 util.rename(q.join(patch), absdest)
2245 r = q.qrepo()
2246 r = q.qrepo()
2246 if r:
2247 if r:
2247 wlock = r.wlock()
2248 wlock = r.wlock()
2248 try:
2249 try:
2249 if r.dirstate[patch] == 'a':
2250 if r.dirstate[patch] == 'a':
2250 r.dirstate.forget(patch)
2251 r.dirstate.forget(patch)
2251 r.dirstate.add(name)
2252 r.dirstate.add(name)
2252 else:
2253 else:
2253 if r.dirstate[name] == 'r':
2254 if r.dirstate[name] == 'r':
2254 r.undelete([name])
2255 r.undelete([name])
2255 r.copy(patch, name)
2256 r.copy(patch, name)
2256 r.remove([patch], False)
2257 r.remove([patch], False)
2257 finally:
2258 finally:
2258 wlock.release()
2259 wlock.release()
2259
2260
2260 q.save_dirty()
2261 q.save_dirty()
2261
2262
2262 def restore(ui, repo, rev, **opts):
2263 def restore(ui, repo, rev, **opts):
2263 """restore the queue state saved by a revision"""
2264 """restore the queue state saved by a revision"""
2264 rev = repo.lookup(rev)
2265 rev = repo.lookup(rev)
2265 q = repo.mq
2266 q = repo.mq
2266 q.restore(repo, rev, delete=opts['delete'],
2267 q.restore(repo, rev, delete=opts['delete'],
2267 qupdate=opts['update'])
2268 qupdate=opts['update'])
2268 q.save_dirty()
2269 q.save_dirty()
2269 return 0
2270 return 0
2270
2271
2271 def save(ui, repo, **opts):
2272 def save(ui, repo, **opts):
2272 """save current queue state"""
2273 """save current queue state"""
2273 q = repo.mq
2274 q = repo.mq
2274 message = cmdutil.logmessage(opts)
2275 message = cmdutil.logmessage(opts)
2275 ret = q.save(repo, msg=message)
2276 ret = q.save(repo, msg=message)
2276 if ret:
2277 if ret:
2277 return ret
2278 return ret
2278 q.save_dirty()
2279 q.save_dirty()
2279 if opts['copy']:
2280 if opts['copy']:
2280 path = q.path
2281 path = q.path
2281 if opts['name']:
2282 if opts['name']:
2282 newpath = os.path.join(q.basepath, opts['name'])
2283 newpath = os.path.join(q.basepath, opts['name'])
2283 if os.path.exists(newpath):
2284 if os.path.exists(newpath):
2284 if not os.path.isdir(newpath):
2285 if not os.path.isdir(newpath):
2285 raise util.Abort(_('destination %s exists and is not '
2286 raise util.Abort(_('destination %s exists and is not '
2286 'a directory') % newpath)
2287 'a directory') % newpath)
2287 if not opts['force']:
2288 if not opts['force']:
2288 raise util.Abort(_('destination %s exists, '
2289 raise util.Abort(_('destination %s exists, '
2289 'use -f to force') % newpath)
2290 'use -f to force') % newpath)
2290 else:
2291 else:
2291 newpath = savename(path)
2292 newpath = savename(path)
2292 ui.warn(_("copy %s to %s\n") % (path, newpath))
2293 ui.warn(_("copy %s to %s\n") % (path, newpath))
2293 util.copyfiles(path, newpath)
2294 util.copyfiles(path, newpath)
2294 if opts['empty']:
2295 if opts['empty']:
2295 try:
2296 try:
2296 os.unlink(q.join(q.status_path))
2297 os.unlink(q.join(q.status_path))
2297 except:
2298 except:
2298 pass
2299 pass
2299 return 0
2300 return 0
2300
2301
2301 def strip(ui, repo, rev, **opts):
2302 def strip(ui, repo, rev, **opts):
2302 """strip a revision and all its descendants from the repository
2303 """strip a revision and all its descendants from the repository
2303
2304
2304 If one of the working directory's parent revisions is stripped, the
2305 If one of the working directory's parent revisions is stripped, the
2305 working directory will be updated to the parent of the stripped
2306 working directory will be updated to the parent of the stripped
2306 revision.
2307 revision.
2307 """
2308 """
2308 backup = 'all'
2309 backup = 'all'
2309 if opts['backup']:
2310 if opts['backup']:
2310 backup = 'strip'
2311 backup = 'strip'
2311 elif opts['nobackup']:
2312 elif opts['nobackup']:
2312 backup = 'none'
2313 backup = 'none'
2313
2314
2314 rev = repo.lookup(rev)
2315 rev = repo.lookup(rev)
2315 p = repo.dirstate.parents()
2316 p = repo.dirstate.parents()
2316 cl = repo.changelog
2317 cl = repo.changelog
2317 update = True
2318 update = True
2318 if p[0] == nullid:
2319 if p[0] == nullid:
2319 update = False
2320 update = False
2320 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2321 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2321 update = False
2322 update = False
2322 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2323 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2323 update = False
2324 update = False
2324
2325
2325 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2326 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2326 return 0
2327 return 0
2327
2328
2328 def select(ui, repo, *args, **opts):
2329 def select(ui, repo, *args, **opts):
2329 '''set or print guarded patches to push
2330 '''set or print guarded patches to push
2330
2331
2331 Use the qguard command to set or print guards on patch, then use
2332 Use the qguard command to set or print guards on patch, then use
2332 qselect to tell mq which guards to use. A patch will be pushed if
2333 qselect to tell mq which guards to use. A patch will be pushed if
2333 it has no guards or any positive guards match the currently
2334 it has no guards or any positive guards match the currently
2334 selected guard, but will not be pushed if any negative guards
2335 selected guard, but will not be pushed if any negative guards
2335 match the current guard. For example:
2336 match the current guard. For example:
2336
2337
2337 qguard foo.patch -stable (negative guard)
2338 qguard foo.patch -stable (negative guard)
2338 qguard bar.patch +stable (positive guard)
2339 qguard bar.patch +stable (positive guard)
2339 qselect stable
2340 qselect stable
2340
2341
2341 This activates the "stable" guard. mq will skip foo.patch (because
2342 This activates the "stable" guard. mq will skip foo.patch (because
2342 it has a negative match) but push bar.patch (because it has a
2343 it has a negative match) but push bar.patch (because it has a
2343 positive match).
2344 positive match).
2344
2345
2345 With no arguments, prints the currently active guards.
2346 With no arguments, prints the currently active guards.
2346 With one argument, sets the active guard.
2347 With one argument, sets the active guard.
2347
2348
2348 Use -n/--none to deactivate guards (no other arguments needed).
2349 Use -n/--none to deactivate guards (no other arguments needed).
2349 When no guards are active, patches with positive guards are
2350 When no guards are active, patches with positive guards are
2350 skipped and patches with negative guards are pushed.
2351 skipped and patches with negative guards are pushed.
2351
2352
2352 qselect can change the guards on applied patches. It does not pop
2353 qselect can change the guards on applied patches. It does not pop
2353 guarded patches by default. Use --pop to pop back to the last
2354 guarded patches by default. Use --pop to pop back to the last
2354 applied patch that is not guarded. Use --reapply (which implies
2355 applied patch that is not guarded. Use --reapply (which implies
2355 --pop) to push back to the current patch afterwards, but skip
2356 --pop) to push back to the current patch afterwards, but skip
2356 guarded patches.
2357 guarded patches.
2357
2358
2358 Use -s/--series to print a list of all guards in the series file
2359 Use -s/--series to print a list of all guards in the series file
2359 (no other arguments needed). Use -v for more information.'''
2360 (no other arguments needed). Use -v for more information.'''
2360
2361
2361 q = repo.mq
2362 q = repo.mq
2362 guards = q.active()
2363 guards = q.active()
2363 if args or opts['none']:
2364 if args or opts['none']:
2364 old_unapplied = q.unapplied(repo)
2365 old_unapplied = q.unapplied(repo)
2365 old_guarded = [i for i in xrange(len(q.applied)) if
2366 old_guarded = [i for i in xrange(len(q.applied)) if
2366 not q.pushable(i)[0]]
2367 not q.pushable(i)[0]]
2367 q.set_active(args)
2368 q.set_active(args)
2368 q.save_dirty()
2369 q.save_dirty()
2369 if not args:
2370 if not args:
2370 ui.status(_('guards deactivated\n'))
2371 ui.status(_('guards deactivated\n'))
2371 if not opts['pop'] and not opts['reapply']:
2372 if not opts['pop'] and not opts['reapply']:
2372 unapplied = q.unapplied(repo)
2373 unapplied = q.unapplied(repo)
2373 guarded = [i for i in xrange(len(q.applied))
2374 guarded = [i for i in xrange(len(q.applied))
2374 if not q.pushable(i)[0]]
2375 if not q.pushable(i)[0]]
2375 if len(unapplied) != len(old_unapplied):
2376 if len(unapplied) != len(old_unapplied):
2376 ui.status(_('number of unguarded, unapplied patches has '
2377 ui.status(_('number of unguarded, unapplied patches has '
2377 'changed from %d to %d\n') %
2378 'changed from %d to %d\n') %
2378 (len(old_unapplied), len(unapplied)))
2379 (len(old_unapplied), len(unapplied)))
2379 if len(guarded) != len(old_guarded):
2380 if len(guarded) != len(old_guarded):
2380 ui.status(_('number of guarded, applied patches has changed '
2381 ui.status(_('number of guarded, applied patches has changed '
2381 'from %d to %d\n') %
2382 'from %d to %d\n') %
2382 (len(old_guarded), len(guarded)))
2383 (len(old_guarded), len(guarded)))
2383 elif opts['series']:
2384 elif opts['series']:
2384 guards = {}
2385 guards = {}
2385 noguards = 0
2386 noguards = 0
2386 for gs in q.series_guards:
2387 for gs in q.series_guards:
2387 if not gs:
2388 if not gs:
2388 noguards += 1
2389 noguards += 1
2389 for g in gs:
2390 for g in gs:
2390 guards.setdefault(g, 0)
2391 guards.setdefault(g, 0)
2391 guards[g] += 1
2392 guards[g] += 1
2392 if ui.verbose:
2393 if ui.verbose:
2393 guards['NONE'] = noguards
2394 guards['NONE'] = noguards
2394 guards = guards.items()
2395 guards = guards.items()
2395 guards.sort(key=lambda x: x[0][1:])
2396 guards.sort(key=lambda x: x[0][1:])
2396 if guards:
2397 if guards:
2397 ui.note(_('guards in series file:\n'))
2398 ui.note(_('guards in series file:\n'))
2398 for guard, count in guards:
2399 for guard, count in guards:
2399 ui.note('%2d ' % count)
2400 ui.note('%2d ' % count)
2400 ui.write(guard, '\n')
2401 ui.write(guard, '\n')
2401 else:
2402 else:
2402 ui.note(_('no guards in series file\n'))
2403 ui.note(_('no guards in series file\n'))
2403 else:
2404 else:
2404 if guards:
2405 if guards:
2405 ui.note(_('active guards:\n'))
2406 ui.note(_('active guards:\n'))
2406 for g in guards:
2407 for g in guards:
2407 ui.write(g, '\n')
2408 ui.write(g, '\n')
2408 else:
2409 else:
2409 ui.write(_('no active guards\n'))
2410 ui.write(_('no active guards\n'))
2410 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2411 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2411 popped = False
2412 popped = False
2412 if opts['pop'] or opts['reapply']:
2413 if opts['pop'] or opts['reapply']:
2413 for i in xrange(len(q.applied)):
2414 for i in xrange(len(q.applied)):
2414 pushable, reason = q.pushable(i)
2415 pushable, reason = q.pushable(i)
2415 if not pushable:
2416 if not pushable:
2416 ui.status(_('popping guarded patches\n'))
2417 ui.status(_('popping guarded patches\n'))
2417 popped = True
2418 popped = True
2418 if i == 0:
2419 if i == 0:
2419 q.pop(repo, all=True)
2420 q.pop(repo, all=True)
2420 else:
2421 else:
2421 q.pop(repo, i-1)
2422 q.pop(repo, i-1)
2422 break
2423 break
2423 if popped:
2424 if popped:
2424 try:
2425 try:
2425 if reapply:
2426 if reapply:
2426 ui.status(_('reapplying unguarded patches\n'))
2427 ui.status(_('reapplying unguarded patches\n'))
2427 q.push(repo, reapply)
2428 q.push(repo, reapply)
2428 finally:
2429 finally:
2429 q.save_dirty()
2430 q.save_dirty()
2430
2431
2431 def finish(ui, repo, *revrange, **opts):
2432 def finish(ui, repo, *revrange, **opts):
2432 """move applied patches into repository history
2433 """move applied patches into repository history
2433
2434
2434 Finishes the specified revisions (corresponding to applied
2435 Finishes the specified revisions (corresponding to applied
2435 patches) by moving them out of mq control into regular repository
2436 patches) by moving them out of mq control into regular repository
2436 history.
2437 history.
2437
2438
2438 Accepts a revision range or the -a/--applied option. If --applied
2439 Accepts a revision range or the -a/--applied option. If --applied
2439 is specified, all applied mq revisions are removed from mq
2440 is specified, all applied mq revisions are removed from mq
2440 control. Otherwise, the given revisions must be at the base of the
2441 control. Otherwise, the given revisions must be at the base of the
2441 stack of applied patches.
2442 stack of applied patches.
2442
2443
2443 This can be especially useful if your changes have been applied to
2444 This can be especially useful if your changes have been applied to
2444 an upstream repository, or if you are about to push your changes
2445 an upstream repository, or if you are about to push your changes
2445 to upstream.
2446 to upstream.
2446 """
2447 """
2447 if not opts['applied'] and not revrange:
2448 if not opts['applied'] and not revrange:
2448 raise util.Abort(_('no revisions specified'))
2449 raise util.Abort(_('no revisions specified'))
2449 elif opts['applied']:
2450 elif opts['applied']:
2450 revrange = ('qbase:qtip',) + revrange
2451 revrange = ('qbase:qtip',) + revrange
2451
2452
2452 q = repo.mq
2453 q = repo.mq
2453 if not q.applied:
2454 if not q.applied:
2454 ui.status(_('no patches applied\n'))
2455 ui.status(_('no patches applied\n'))
2455 return 0
2456 return 0
2456
2457
2457 revs = cmdutil.revrange(repo, revrange)
2458 revs = cmdutil.revrange(repo, revrange)
2458 q.finish(repo, revs)
2459 q.finish(repo, revs)
2459 q.save_dirty()
2460 q.save_dirty()
2460 return 0
2461 return 0
2461
2462
2462 def reposetup(ui, repo):
2463 def reposetup(ui, repo):
2463 class mqrepo(repo.__class__):
2464 class mqrepo(repo.__class__):
2464 @util.propertycache
2465 @util.propertycache
2465 def mq(self):
2466 def mq(self):
2466 return queue(self.ui, self.join(""))
2467 return queue(self.ui, self.join(""))
2467
2468
2468 def abort_if_wdir_patched(self, errmsg, force=False):
2469 def abort_if_wdir_patched(self, errmsg, force=False):
2469 if self.mq.applied and not force:
2470 if self.mq.applied and not force:
2470 parent = hex(self.dirstate.parents()[0])
2471 parent = hex(self.dirstate.parents()[0])
2471 if parent in [s.rev for s in self.mq.applied]:
2472 if parent in [s.rev for s in self.mq.applied]:
2472 raise util.Abort(errmsg)
2473 raise util.Abort(errmsg)
2473
2474
2474 def commit(self, text="", user=None, date=None, match=None,
2475 def commit(self, text="", user=None, date=None, match=None,
2475 force=False, editor=False, extra={}):
2476 force=False, editor=False, extra={}):
2476 self.abort_if_wdir_patched(
2477 self.abort_if_wdir_patched(
2477 _('cannot commit over an applied mq patch'),
2478 _('cannot commit over an applied mq patch'),
2478 force)
2479 force)
2479
2480
2480 return super(mqrepo, self).commit(text, user, date, match, force,
2481 return super(mqrepo, self).commit(text, user, date, match, force,
2481 editor, extra)
2482 editor, extra)
2482
2483
2483 def push(self, remote, force=False, revs=None):
2484 def push(self, remote, force=False, revs=None):
2484 if self.mq.applied and not force and not revs:
2485 if self.mq.applied and not force and not revs:
2485 raise util.Abort(_('source has mq patches applied'))
2486 raise util.Abort(_('source has mq patches applied'))
2486 return super(mqrepo, self).push(remote, force, revs)
2487 return super(mqrepo, self).push(remote, force, revs)
2487
2488
2488 def _findtags(self):
2489 def _findtags(self):
2489 '''augment tags from base class with patch tags'''
2490 '''augment tags from base class with patch tags'''
2490 result = super(mqrepo, self)._findtags()
2491 result = super(mqrepo, self)._findtags()
2491
2492
2492 q = self.mq
2493 q = self.mq
2493 if not q.applied:
2494 if not q.applied:
2494 return result
2495 return result
2495
2496
2496 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2497 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2497
2498
2498 if mqtags[-1][0] not in self.changelog.nodemap:
2499 if mqtags[-1][0] not in self.changelog.nodemap:
2499 self.ui.warn(_('mq status file refers to unknown node %s\n')
2500 self.ui.warn(_('mq status file refers to unknown node %s\n')
2500 % short(mqtags[-1][0]))
2501 % short(mqtags[-1][0]))
2501 return result
2502 return result
2502
2503
2503 mqtags.append((mqtags[-1][0], 'qtip'))
2504 mqtags.append((mqtags[-1][0], 'qtip'))
2504 mqtags.append((mqtags[0][0], 'qbase'))
2505 mqtags.append((mqtags[0][0], 'qbase'))
2505 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2506 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2506 tags = result[0]
2507 tags = result[0]
2507 for patch in mqtags:
2508 for patch in mqtags:
2508 if patch[1] in tags:
2509 if patch[1] in tags:
2509 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2510 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2510 % patch[1])
2511 % patch[1])
2511 else:
2512 else:
2512 tags[patch[1]] = patch[0]
2513 tags[patch[1]] = patch[0]
2513
2514
2514 return result
2515 return result
2515
2516
2516 def _branchtags(self, partial, lrev):
2517 def _branchtags(self, partial, lrev):
2517 q = self.mq
2518 q = self.mq
2518 if not q.applied:
2519 if not q.applied:
2519 return super(mqrepo, self)._branchtags(partial, lrev)
2520 return super(mqrepo, self)._branchtags(partial, lrev)
2520
2521
2521 cl = self.changelog
2522 cl = self.changelog
2522 qbasenode = bin(q.applied[0].rev)
2523 qbasenode = bin(q.applied[0].rev)
2523 if qbasenode not in cl.nodemap:
2524 if qbasenode not in cl.nodemap:
2524 self.ui.warn(_('mq status file refers to unknown node %s\n')
2525 self.ui.warn(_('mq status file refers to unknown node %s\n')
2525 % short(qbasenode))
2526 % short(qbasenode))
2526 return super(mqrepo, self)._branchtags(partial, lrev)
2527 return super(mqrepo, self)._branchtags(partial, lrev)
2527
2528
2528 qbase = cl.rev(qbasenode)
2529 qbase = cl.rev(qbasenode)
2529 start = lrev + 1
2530 start = lrev + 1
2530 if start < qbase:
2531 if start < qbase:
2531 # update the cache (excluding the patches) and save it
2532 # update the cache (excluding the patches) and save it
2532 self._updatebranchcache(partial, lrev+1, qbase)
2533 self._updatebranchcache(partial, lrev+1, qbase)
2533 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2534 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2534 start = qbase
2535 start = qbase
2535 # if start = qbase, the cache is as updated as it should be.
2536 # if start = qbase, the cache is as updated as it should be.
2536 # if start > qbase, the cache includes (part of) the patches.
2537 # if start > qbase, the cache includes (part of) the patches.
2537 # we might as well use it, but we won't save it.
2538 # we might as well use it, but we won't save it.
2538
2539
2539 # update the cache up to the tip
2540 # update the cache up to the tip
2540 self._updatebranchcache(partial, start, len(cl))
2541 self._updatebranchcache(partial, start, len(cl))
2541
2542
2542 return partial
2543 return partial
2543
2544
2544 if repo.local():
2545 if repo.local():
2545 repo.__class__ = mqrepo
2546 repo.__class__ = mqrepo
2546
2547
2547 def mqimport(orig, ui, repo, *args, **kwargs):
2548 def mqimport(orig, ui, repo, *args, **kwargs):
2548 if hasattr(repo, 'abort_if_wdir_patched'):
2549 if hasattr(repo, 'abort_if_wdir_patched'):
2549 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2550 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2550 kwargs.get('force'))
2551 kwargs.get('force'))
2551 return orig(ui, repo, *args, **kwargs)
2552 return orig(ui, repo, *args, **kwargs)
2552
2553
2553 def uisetup(ui):
2554 def uisetup(ui):
2554 extensions.wrapcommand(commands.table, 'import', mqimport)
2555 extensions.wrapcommand(commands.table, 'import', mqimport)
2555
2556
2556 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2557 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2557
2558
2558 cmdtable = {
2559 cmdtable = {
2559 "qapplied":
2560 "qapplied":
2560 (applied,
2561 (applied,
2561 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
2562 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
2562 _('hg qapplied [-1] [-s] [PATCH]')),
2563 _('hg qapplied [-1] [-s] [PATCH]')),
2563 "qclone":
2564 "qclone":
2564 (clone,
2565 (clone,
2565 [('', 'pull', None, _('use pull protocol to copy metadata')),
2566 [('', 'pull', None, _('use pull protocol to copy metadata')),
2566 ('U', 'noupdate', None, _('do not update the new working directories')),
2567 ('U', 'noupdate', None, _('do not update the new working directories')),
2567 ('', 'uncompressed', None,
2568 ('', 'uncompressed', None,
2568 _('use uncompressed transfer (fast over LAN)')),
2569 _('use uncompressed transfer (fast over LAN)')),
2569 ('p', 'patches', '', _('location of source patch repository')),
2570 ('p', 'patches', '', _('location of source patch repository')),
2570 ] + commands.remoteopts,
2571 ] + commands.remoteopts,
2571 _('hg qclone [OPTION]... SOURCE [DEST]')),
2572 _('hg qclone [OPTION]... SOURCE [DEST]')),
2572 "qcommit|qci":
2573 "qcommit|qci":
2573 (commit,
2574 (commit,
2574 commands.table["^commit|ci"][1],
2575 commands.table["^commit|ci"][1],
2575 _('hg qcommit [OPTION]... [FILE]...')),
2576 _('hg qcommit [OPTION]... [FILE]...')),
2576 "^qdiff":
2577 "^qdiff":
2577 (diff,
2578 (diff,
2578 commands.diffopts + commands.diffopts2 + commands.walkopts,
2579 commands.diffopts + commands.diffopts2 + commands.walkopts,
2579 _('hg qdiff [OPTION]... [FILE]...')),
2580 _('hg qdiff [OPTION]... [FILE]...')),
2580 "qdelete|qremove|qrm":
2581 "qdelete|qremove|qrm":
2581 (delete,
2582 (delete,
2582 [('k', 'keep', None, _('keep patch file')),
2583 [('k', 'keep', None, _('keep patch file')),
2583 ('r', 'rev', [], _('stop managing a revision (DEPRECATED)'))],
2584 ('r', 'rev', [], _('stop managing a revision (DEPRECATED)'))],
2584 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2585 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2585 'qfold':
2586 'qfold':
2586 (fold,
2587 (fold,
2587 [('e', 'edit', None, _('edit patch header')),
2588 [('e', 'edit', None, _('edit patch header')),
2588 ('k', 'keep', None, _('keep folded patch files')),
2589 ('k', 'keep', None, _('keep folded patch files')),
2589 ] + commands.commitopts,
2590 ] + commands.commitopts,
2590 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2591 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2591 'qgoto':
2592 'qgoto':
2592 (goto,
2593 (goto,
2593 [('f', 'force', None, _('overwrite any local changes'))],
2594 [('f', 'force', None, _('overwrite any local changes'))],
2594 _('hg qgoto [OPTION]... PATCH')),
2595 _('hg qgoto [OPTION]... PATCH')),
2595 'qguard':
2596 'qguard':
2596 (guard,
2597 (guard,
2597 [('l', 'list', None, _('list all patches and guards')),
2598 [('l', 'list', None, _('list all patches and guards')),
2598 ('n', 'none', None, _('drop all guards'))],
2599 ('n', 'none', None, _('drop all guards'))],
2599 _('hg qguard [-l] [-n] -- [PATCH] [+GUARD]... [-GUARD]...')),
2600 _('hg qguard [-l] [-n] -- [PATCH] [+GUARD]... [-GUARD]...')),
2600 'qheader': (header, [], _('hg qheader [PATCH]')),
2601 'qheader': (header, [], _('hg qheader [PATCH]')),
2601 "^qimport":
2602 "^qimport":
2602 (qimport,
2603 (qimport,
2603 [('e', 'existing', None, _('import file in patch directory')),
2604 [('e', 'existing', None, _('import file in patch directory')),
2604 ('n', 'name', '', _('name of patch file')),
2605 ('n', 'name', '', _('name of patch file')),
2605 ('f', 'force', None, _('overwrite existing files')),
2606 ('f', 'force', None, _('overwrite existing files')),
2606 ('r', 'rev', [], _('place existing revisions under mq control')),
2607 ('r', 'rev', [], _('place existing revisions under mq control')),
2607 ('g', 'git', None, _('use git extended diff format')),
2608 ('g', 'git', None, _('use git extended diff format')),
2608 ('P', 'push', None, _('qpush after importing'))],
2609 ('P', 'push', None, _('qpush after importing'))],
2609 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2610 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2610 "^qinit":
2611 "^qinit":
2611 (init,
2612 (init,
2612 [('c', 'create-repo', None, _('create queue repository'))],
2613 [('c', 'create-repo', None, _('create queue repository'))],
2613 _('hg qinit [-c]')),
2614 _('hg qinit [-c]')),
2614 "qnew":
2615 "qnew":
2615 (new,
2616 (new,
2616 [('e', 'edit', None, _('edit commit message')),
2617 [('e', 'edit', None, _('edit commit message')),
2617 ('f', 'force', None, _('import uncommitted changes into patch')),
2618 ('f', 'force', None, _('import uncommitted changes into patch')),
2618 ('g', 'git', None, _('use git extended diff format')),
2619 ('g', 'git', None, _('use git extended diff format')),
2619 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2620 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2620 ('u', 'user', '', _('add "From: <given user>" to patch')),
2621 ('u', 'user', '', _('add "From: <given user>" to patch')),
2621 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2622 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2622 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2623 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2623 ] + commands.walkopts + commands.commitopts,
2624 ] + commands.walkopts + commands.commitopts,
2624 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2625 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2625 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2626 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2626 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2627 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2627 "^qpop":
2628 "^qpop":
2628 (pop,
2629 (pop,
2629 [('a', 'all', None, _('pop all patches')),
2630 [('a', 'all', None, _('pop all patches')),
2630 ('n', 'name', '', _('queue name to pop')),
2631 ('n', 'name', '', _('queue name to pop')),
2631 ('f', 'force', None, _('forget any local changes to patched files'))],
2632 ('f', 'force', None, _('forget any local changes to patched files'))],
2632 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2633 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2633 "^qpush":
2634 "^qpush":
2634 (push,
2635 (push,
2635 [('f', 'force', None, _('apply if the patch has rejects')),
2636 [('f', 'force', None, _('apply if the patch has rejects')),
2636 ('l', 'list', None, _('list patch name in commit text')),
2637 ('l', 'list', None, _('list patch name in commit text')),
2637 ('a', 'all', None, _('apply all patches')),
2638 ('a', 'all', None, _('apply all patches')),
2638 ('m', 'merge', None, _('merge from another queue')),
2639 ('m', 'merge', None, _('merge from another queue')),
2639 ('n', 'name', '', _('merge queue name'))],
2640 ('n', 'name', '', _('merge queue name'))],
2640 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2641 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2641 "^qrefresh":
2642 "^qrefresh":
2642 (refresh,
2643 (refresh,
2643 [('e', 'edit', None, _('edit commit message')),
2644 [('e', 'edit', None, _('edit commit message')),
2644 ('g', 'git', None, _('use git extended diff format')),
2645 ('g', 'git', None, _('use git extended diff format')),
2645 ('s', 'short', None, _('refresh only files already in the patch and specified files')),
2646 ('s', 'short', None, _('refresh only files already in the patch and specified files')),
2646 ('U', 'currentuser', None, _('add/update author field in patch with current user')),
2647 ('U', 'currentuser', None, _('add/update author field in patch with current user')),
2647 ('u', 'user', '', _('add/update author field in patch with given user')),
2648 ('u', 'user', '', _('add/update author field in patch with given user')),
2648 ('D', 'currentdate', None, _('add/update date field in patch with current date')),
2649 ('D', 'currentdate', None, _('add/update date field in patch with current date')),
2649 ('d', 'date', '', _('add/update date field in patch with given date'))
2650 ('d', 'date', '', _('add/update date field in patch with given date'))
2650 ] + commands.walkopts + commands.commitopts,
2651 ] + commands.walkopts + commands.commitopts,
2651 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2652 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2652 'qrename|qmv':
2653 'qrename|qmv':
2653 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2654 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2654 "qrestore":
2655 "qrestore":
2655 (restore,
2656 (restore,
2656 [('d', 'delete', None, _('delete save entry')),
2657 [('d', 'delete', None, _('delete save entry')),
2657 ('u', 'update', None, _('update queue working directory'))],
2658 ('u', 'update', None, _('update queue working directory'))],
2658 _('hg qrestore [-d] [-u] REV')),
2659 _('hg qrestore [-d] [-u] REV')),
2659 "qsave":
2660 "qsave":
2660 (save,
2661 (save,
2661 [('c', 'copy', None, _('copy patch directory')),
2662 [('c', 'copy', None, _('copy patch directory')),
2662 ('n', 'name', '', _('copy directory name')),
2663 ('n', 'name', '', _('copy directory name')),
2663 ('e', 'empty', None, _('clear queue status file')),
2664 ('e', 'empty', None, _('clear queue status file')),
2664 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2665 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2665 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2666 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2666 "qselect":
2667 "qselect":
2667 (select,
2668 (select,
2668 [('n', 'none', None, _('disable all guards')),
2669 [('n', 'none', None, _('disable all guards')),
2669 ('s', 'series', None, _('list all guards in series file')),
2670 ('s', 'series', None, _('list all guards in series file')),
2670 ('', 'pop', None, _('pop to before first guarded applied patch')),
2671 ('', 'pop', None, _('pop to before first guarded applied patch')),
2671 ('', 'reapply', None, _('pop, then reapply patches'))],
2672 ('', 'reapply', None, _('pop, then reapply patches'))],
2672 _('hg qselect [OPTION]... [GUARD]...')),
2673 _('hg qselect [OPTION]... [GUARD]...')),
2673 "qseries":
2674 "qseries":
2674 (series,
2675 (series,
2675 [('m', 'missing', None, _('print patches not in series')),
2676 [('m', 'missing', None, _('print patches not in series')),
2676 ] + seriesopts,
2677 ] + seriesopts,
2677 _('hg qseries [-ms]')),
2678 _('hg qseries [-ms]')),
2678 "^strip":
2679 "^strip":
2679 (strip,
2680 (strip,
2680 [('f', 'force', None, _('force removal with local changes')),
2681 [('f', 'force', None, _('force removal with local changes')),
2681 ('b', 'backup', None, _('bundle unrelated changesets')),
2682 ('b', 'backup', None, _('bundle unrelated changesets')),
2682 ('n', 'nobackup', None, _('no backups'))],
2683 ('n', 'nobackup', None, _('no backups'))],
2683 _('hg strip [-f] [-b] [-n] REV')),
2684 _('hg strip [-f] [-b] [-n] REV')),
2684 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2685 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2685 "qunapplied":
2686 "qunapplied":
2686 (unapplied,
2687 (unapplied,
2687 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2688 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2688 _('hg qunapplied [-1] [-s] [PATCH]')),
2689 _('hg qunapplied [-1] [-s] [PATCH]')),
2689 "qfinish":
2690 "qfinish":
2690 (finish,
2691 (finish,
2691 [('a', 'applied', None, _('finish all applied changesets'))],
2692 [('a', 'applied', None, _('finish all applied changesets'))],
2692 _('hg qfinish [-a] [REV]...')),
2693 _('hg qfinish [-a] [REV]...')),
2693 }
2694 }
@@ -1,3677 +1,3679 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, sys, subprocess, difflib, time, tempfile
11 import os, re, sys, subprocess, difflib, time, tempfile
12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
13 import patch, help, mdiff, url, encoding
13 import patch, help, mdiff, url, encoding
14 import archival, changegroup, cmdutil, sshserver, hbisect
14 import archival, changegroup, cmdutil, sshserver, hbisect
15 from hgweb import server
15 from hgweb import server
16 import merge as merge_
16 import merge as merge_
17 import minirst
17 import minirst
18
18
19 # Commands start here, listed alphabetically
19 # Commands start here, listed alphabetically
20
20
21 def add(ui, repo, *pats, **opts):
21 def add(ui, repo, *pats, **opts):
22 """add the specified files on the next commit
22 """add the specified files on the next commit
23
23
24 Schedule files to be version controlled and added to the
24 Schedule files to be version controlled and added to the
25 repository.
25 repository.
26
26
27 The files will be added to the repository at the next commit. To
27 The files will be added to the repository at the next commit. To
28 undo an add before that, see hg forget.
28 undo an add before that, see hg forget.
29
29
30 If no names are given, add all files to the repository.
30 If no names are given, add all files to the repository.
31 """
31 """
32
32
33 bad = []
33 bad = []
34 exacts = {}
34 exacts = {}
35 names = []
35 names = []
36 m = cmdutil.match(repo, pats, opts)
36 m = cmdutil.match(repo, pats, opts)
37 oldbad = m.bad
37 oldbad = m.bad
38 m.bad = lambda x,y: bad.append(x) or oldbad(x,y)
38 m.bad = lambda x,y: bad.append(x) or oldbad(x,y)
39
39
40 for f in repo.walk(m):
40 for f in repo.walk(m):
41 exact = m.exact(f)
41 exact = m.exact(f)
42 if exact or f not in repo.dirstate:
42 if exact or f not in repo.dirstate:
43 names.append(f)
43 names.append(f)
44 if ui.verbose or not exact:
44 if ui.verbose or not exact:
45 ui.status(_('adding %s\n') % m.rel(f))
45 ui.status(_('adding %s\n') % m.rel(f))
46 if not opts.get('dry_run'):
46 if not opts.get('dry_run'):
47 bad += [f for f in repo.add(names) if f in m.files()]
47 bad += [f for f in repo.add(names) if f in m.files()]
48 return bad and 1 or 0
48 return bad and 1 or 0
49
49
50 def addremove(ui, repo, *pats, **opts):
50 def addremove(ui, repo, *pats, **opts):
51 """add all new files, delete all missing files
51 """add all new files, delete all missing files
52
52
53 Add all new files and remove all missing files from the
53 Add all new files and remove all missing files from the
54 repository.
54 repository.
55
55
56 New files are ignored if they match any of the patterns in
56 New files are ignored if they match any of the patterns in
57 .hgignore. As with add, these changes take effect at the next
57 .hgignore. As with add, these changes take effect at the next
58 commit.
58 commit.
59
59
60 Use the -s/--similarity option to detect renamed files. With a
60 Use the -s/--similarity option to detect renamed files. With a
61 parameter greater than 0, this compares every removed file with
61 parameter greater than 0, this compares every removed file with
62 every added file and records those similar enough as renames. This
62 every added file and records those similar enough as renames. This
63 option takes a percentage between 0 (disabled) and 100 (files must
63 option takes a percentage between 0 (disabled) and 100 (files must
64 be identical) as its parameter. Detecting renamed files this way
64 be identical) as its parameter. Detecting renamed files this way
65 can be expensive.
65 can be expensive.
66 """
66 """
67 try:
67 try:
68 sim = float(opts.get('similarity') or 0)
68 sim = float(opts.get('similarity') or 0)
69 except ValueError:
69 except ValueError:
70 raise util.Abort(_('similarity must be a number'))
70 raise util.Abort(_('similarity must be a number'))
71 if sim < 0 or sim > 100:
71 if sim < 0 or sim > 100:
72 raise util.Abort(_('similarity must be between 0 and 100'))
72 raise util.Abort(_('similarity must be between 0 and 100'))
73 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
73 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
74
74
75 def annotate(ui, repo, *pats, **opts):
75 def annotate(ui, repo, *pats, **opts):
76 """show changeset information by line for each file
76 """show changeset information by line for each file
77
77
78 List changes in files, showing the revision id responsible for
78 List changes in files, showing the revision id responsible for
79 each line
79 each line
80
80
81 This command is useful for discovering when a change was made and
81 This command is useful for discovering when a change was made and
82 by whom.
82 by whom.
83
83
84 Without the -a/--text option, annotate will avoid processing files
84 Without the -a/--text option, annotate will avoid processing files
85 it detects as binary. With -a, annotate will annotate the file
85 it detects as binary. With -a, annotate will annotate the file
86 anyway, although the results will probably be neither useful
86 anyway, although the results will probably be neither useful
87 nor desirable.
87 nor desirable.
88 """
88 """
89 datefunc = ui.quiet and util.shortdate or util.datestr
89 datefunc = ui.quiet and util.shortdate or util.datestr
90 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
90 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
91
91
92 if not pats:
92 if not pats:
93 raise util.Abort(_('at least one filename or pattern is required'))
93 raise util.Abort(_('at least one filename or pattern is required'))
94
94
95 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
95 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
96 ('number', lambda x: str(x[0].rev())),
96 ('number', lambda x: str(x[0].rev())),
97 ('changeset', lambda x: short(x[0].node())),
97 ('changeset', lambda x: short(x[0].node())),
98 ('date', getdate),
98 ('date', getdate),
99 ('follow', lambda x: x[0].path()),
99 ('follow', lambda x: x[0].path()),
100 ]
100 ]
101
101
102 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
102 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
103 and not opts.get('follow')):
103 and not opts.get('follow')):
104 opts['number'] = 1
104 opts['number'] = 1
105
105
106 linenumber = opts.get('line_number') is not None
106 linenumber = opts.get('line_number') is not None
107 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
107 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
108 raise util.Abort(_('at least one of -n/-c is required for -l'))
108 raise util.Abort(_('at least one of -n/-c is required for -l'))
109
109
110 funcmap = [func for op, func in opmap if opts.get(op)]
110 funcmap = [func for op, func in opmap if opts.get(op)]
111 if linenumber:
111 if linenumber:
112 lastfunc = funcmap[-1]
112 lastfunc = funcmap[-1]
113 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
113 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
114
114
115 ctx = repo[opts.get('rev')]
115 ctx = repo[opts.get('rev')]
116
116
117 m = cmdutil.match(repo, pats, opts)
117 m = cmdutil.match(repo, pats, opts)
118 for abs in ctx.walk(m):
118 for abs in ctx.walk(m):
119 fctx = ctx[abs]
119 fctx = ctx[abs]
120 if not opts.get('text') and util.binary(fctx.data()):
120 if not opts.get('text') and util.binary(fctx.data()):
121 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
121 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
122 continue
122 continue
123
123
124 lines = fctx.annotate(follow=opts.get('follow'),
124 lines = fctx.annotate(follow=opts.get('follow'),
125 linenumber=linenumber)
125 linenumber=linenumber)
126 pieces = []
126 pieces = []
127
127
128 for f in funcmap:
128 for f in funcmap:
129 l = [f(n) for n, dummy in lines]
129 l = [f(n) for n, dummy in lines]
130 if l:
130 if l:
131 ml = max(map(len, l))
131 ml = max(map(len, l))
132 pieces.append(["%*s" % (ml, x) for x in l])
132 pieces.append(["%*s" % (ml, x) for x in l])
133
133
134 if pieces:
134 if pieces:
135 for p, l in zip(zip(*pieces), lines):
135 for p, l in zip(zip(*pieces), lines):
136 ui.write("%s: %s" % (" ".join(p), l[1]))
136 ui.write("%s: %s" % (" ".join(p), l[1]))
137
137
138 def archive(ui, repo, dest, **opts):
138 def archive(ui, repo, dest, **opts):
139 '''create an unversioned archive of a repository revision
139 '''create an unversioned archive of a repository revision
140
140
141 By default, the revision used is the parent of the working
141 By default, the revision used is the parent of the working
142 directory; use -r/--rev to specify a different revision.
142 directory; use -r/--rev to specify a different revision.
143
143
144 To specify the type of archive to create, use -t/--type. Valid
144 To specify the type of archive to create, use -t/--type. Valid
145 types are::
145 types are::
146
146
147 "files" (default): a directory full of files
147 "files" (default): a directory full of files
148 "tar": tar archive, uncompressed
148 "tar": tar archive, uncompressed
149 "tbz2": tar archive, compressed using bzip2
149 "tbz2": tar archive, compressed using bzip2
150 "tgz": tar archive, compressed using gzip
150 "tgz": tar archive, compressed using gzip
151 "uzip": zip archive, uncompressed
151 "uzip": zip archive, uncompressed
152 "zip": zip archive, compressed using deflate
152 "zip": zip archive, compressed using deflate
153
153
154 The exact name of the destination archive or directory is given
154 The exact name of the destination archive or directory is given
155 using a format string; see 'hg help export' for details.
155 using a format string; see 'hg help export' for details.
156
156
157 Each member added to an archive file has a directory prefix
157 Each member added to an archive file has a directory prefix
158 prepended. Use -p/--prefix to specify a format string for the
158 prepended. Use -p/--prefix to specify a format string for the
159 prefix. The default is the basename of the archive, with suffixes
159 prefix. The default is the basename of the archive, with suffixes
160 removed.
160 removed.
161 '''
161 '''
162
162
163 ctx = repo[opts.get('rev')]
163 ctx = repo[opts.get('rev')]
164 if not ctx:
164 if not ctx:
165 raise util.Abort(_('no working directory: please specify a revision'))
165 raise util.Abort(_('no working directory: please specify a revision'))
166 node = ctx.node()
166 node = ctx.node()
167 dest = cmdutil.make_filename(repo, dest, node)
167 dest = cmdutil.make_filename(repo, dest, node)
168 if os.path.realpath(dest) == repo.root:
168 if os.path.realpath(dest) == repo.root:
169 raise util.Abort(_('repository root cannot be destination'))
169 raise util.Abort(_('repository root cannot be destination'))
170 matchfn = cmdutil.match(repo, [], opts)
170 matchfn = cmdutil.match(repo, [], opts)
171 kind = opts.get('type') or 'files'
171 kind = opts.get('type') or 'files'
172 prefix = opts.get('prefix')
172 prefix = opts.get('prefix')
173 if dest == '-':
173 if dest == '-':
174 if kind == 'files':
174 if kind == 'files':
175 raise util.Abort(_('cannot archive plain files to stdout'))
175 raise util.Abort(_('cannot archive plain files to stdout'))
176 dest = sys.stdout
176 dest = sys.stdout
177 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
177 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
178 prefix = cmdutil.make_filename(repo, prefix, node)
178 prefix = cmdutil.make_filename(repo, prefix, node)
179 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
179 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
180 matchfn, prefix)
180 matchfn, prefix)
181
181
182 def backout(ui, repo, node=None, rev=None, **opts):
182 def backout(ui, repo, node=None, rev=None, **opts):
183 '''reverse effect of earlier changeset
183 '''reverse effect of earlier changeset
184
184
185 Commit the backed out changes as a new changeset. The new
185 Commit the backed out changes as a new changeset. The new
186 changeset is a child of the backed out changeset.
186 changeset is a child of the backed out changeset.
187
187
188 If you backout a changeset other than the tip, a new head is
188 If you backout a changeset other than the tip, a new head is
189 created. This head will be the new tip and you should merge this
189 created. This head will be the new tip and you should merge this
190 backout changeset with another head.
190 backout changeset with another head.
191
191
192 The --merge option remembers the parent of the working directory
192 The --merge option remembers the parent of the working directory
193 before starting the backout, then merges the new head with that
193 before starting the backout, then merges the new head with that
194 changeset afterwards. This saves you from doing the merge by hand.
194 changeset afterwards. This saves you from doing the merge by hand.
195 The result of this merge is not committed, as with a normal merge.
195 The result of this merge is not committed, as with a normal merge.
196
196
197 See 'hg help dates' for a list of formats valid for -d/--date.
197 See 'hg help dates' for a list of formats valid for -d/--date.
198 '''
198 '''
199 if rev and node:
199 if rev and node:
200 raise util.Abort(_("please specify just one revision"))
200 raise util.Abort(_("please specify just one revision"))
201
201
202 if not rev:
202 if not rev:
203 rev = node
203 rev = node
204
204
205 if not rev:
205 if not rev:
206 raise util.Abort(_("please specify a revision to backout"))
206 raise util.Abort(_("please specify a revision to backout"))
207
207
208 date = opts.get('date')
208 date = opts.get('date')
209 if date:
209 if date:
210 opts['date'] = util.parsedate(date)
210 opts['date'] = util.parsedate(date)
211
211
212 cmdutil.bail_if_changed(repo)
212 cmdutil.bail_if_changed(repo)
213 node = repo.lookup(rev)
213 node = repo.lookup(rev)
214
214
215 op1, op2 = repo.dirstate.parents()
215 op1, op2 = repo.dirstate.parents()
216 a = repo.changelog.ancestor(op1, node)
216 a = repo.changelog.ancestor(op1, node)
217 if a != node:
217 if a != node:
218 raise util.Abort(_('cannot backout change on a different branch'))
218 raise util.Abort(_('cannot backout change on a different branch'))
219
219
220 p1, p2 = repo.changelog.parents(node)
220 p1, p2 = repo.changelog.parents(node)
221 if p1 == nullid:
221 if p1 == nullid:
222 raise util.Abort(_('cannot backout a change with no parents'))
222 raise util.Abort(_('cannot backout a change with no parents'))
223 if p2 != nullid:
223 if p2 != nullid:
224 if not opts.get('parent'):
224 if not opts.get('parent'):
225 raise util.Abort(_('cannot backout a merge changeset without '
225 raise util.Abort(_('cannot backout a merge changeset without '
226 '--parent'))
226 '--parent'))
227 p = repo.lookup(opts['parent'])
227 p = repo.lookup(opts['parent'])
228 if p not in (p1, p2):
228 if p not in (p1, p2):
229 raise util.Abort(_('%s is not a parent of %s') %
229 raise util.Abort(_('%s is not a parent of %s') %
230 (short(p), short(node)))
230 (short(p), short(node)))
231 parent = p
231 parent = p
232 else:
232 else:
233 if opts.get('parent'):
233 if opts.get('parent'):
234 raise util.Abort(_('cannot use --parent on non-merge changeset'))
234 raise util.Abort(_('cannot use --parent on non-merge changeset'))
235 parent = p1
235 parent = p1
236
236
237 # the backout should appear on the same branch
237 # the backout should appear on the same branch
238 branch = repo.dirstate.branch()
238 branch = repo.dirstate.branch()
239 hg.clean(repo, node, show_stats=False)
239 hg.clean(repo, node, show_stats=False)
240 repo.dirstate.setbranch(branch)
240 repo.dirstate.setbranch(branch)
241 revert_opts = opts.copy()
241 revert_opts = opts.copy()
242 revert_opts['date'] = None
242 revert_opts['date'] = None
243 revert_opts['all'] = True
243 revert_opts['all'] = True
244 revert_opts['rev'] = hex(parent)
244 revert_opts['rev'] = hex(parent)
245 revert_opts['no_backup'] = None
245 revert_opts['no_backup'] = None
246 revert(ui, repo, **revert_opts)
246 revert(ui, repo, **revert_opts)
247 commit_opts = opts.copy()
247 commit_opts = opts.copy()
248 commit_opts['addremove'] = False
248 commit_opts['addremove'] = False
249 if not commit_opts['message'] and not commit_opts['logfile']:
249 if not commit_opts['message'] and not commit_opts['logfile']:
250 # we don't translate commit messages
250 # we don't translate commit messages
251 commit_opts['message'] = "Backed out changeset %s" % short(node)
251 commit_opts['message'] = "Backed out changeset %s" % short(node)
252 commit_opts['force_editor'] = True
252 commit_opts['force_editor'] = True
253 commit(ui, repo, **commit_opts)
253 commit(ui, repo, **commit_opts)
254 def nice(node):
254 def nice(node):
255 return '%d:%s' % (repo.changelog.rev(node), short(node))
255 return '%d:%s' % (repo.changelog.rev(node), short(node))
256 ui.status(_('changeset %s backs out changeset %s\n') %
256 ui.status(_('changeset %s backs out changeset %s\n') %
257 (nice(repo.changelog.tip()), nice(node)))
257 (nice(repo.changelog.tip()), nice(node)))
258 if op1 != node:
258 if op1 != node:
259 hg.clean(repo, op1, show_stats=False)
259 hg.clean(repo, op1, show_stats=False)
260 if opts.get('merge'):
260 if opts.get('merge'):
261 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
261 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
262 hg.merge(repo, hex(repo.changelog.tip()))
262 hg.merge(repo, hex(repo.changelog.tip()))
263 else:
263 else:
264 ui.status(_('the backout changeset is a new head - '
264 ui.status(_('the backout changeset is a new head - '
265 'do not forget to merge\n'))
265 'do not forget to merge\n'))
266 ui.status(_('(use "backout --merge" '
266 ui.status(_('(use "backout --merge" '
267 'if you want to auto-merge)\n'))
267 'if you want to auto-merge)\n'))
268
268
269 def bisect(ui, repo, rev=None, extra=None, command=None,
269 def bisect(ui, repo, rev=None, extra=None, command=None,
270 reset=None, good=None, bad=None, skip=None, noupdate=None):
270 reset=None, good=None, bad=None, skip=None, noupdate=None):
271 """subdivision search of changesets
271 """subdivision search of changesets
272
272
273 This command helps to find changesets which introduce problems. To
273 This command helps to find changesets which introduce problems. To
274 use, mark the earliest changeset you know exhibits the problem as
274 use, mark the earliest changeset you know exhibits the problem as
275 bad, then mark the latest changeset which is free from the problem
275 bad, then mark the latest changeset which is free from the problem
276 as good. Bisect will update your working directory to a revision
276 as good. Bisect will update your working directory to a revision
277 for testing (unless the -U/--noupdate option is specified). Once
277 for testing (unless the -U/--noupdate option is specified). Once
278 you have performed tests, mark the working directory as good or
278 you have performed tests, mark the working directory as good or
279 bad, and bisect will either update to another candidate changeset
279 bad, and bisect will either update to another candidate changeset
280 or announce that it has found the bad revision.
280 or announce that it has found the bad revision.
281
281
282 As a shortcut, you can also use the revision argument to mark a
282 As a shortcut, you can also use the revision argument to mark a
283 revision as good or bad without checking it out first.
283 revision as good or bad without checking it out first.
284
284
285 If you supply a command, it will be used for automatic bisection.
285 If you supply a command, it will be used for automatic bisection.
286 Its exit status will be used to mark revisions as good or bad:
286 Its exit status will be used to mark revisions as good or bad:
287 status 0 means good, 125 means to skip the revision, 127
287 status 0 means good, 125 means to skip the revision, 127
288 (command not found) will abort the bisection, and any other
288 (command not found) will abort the bisection, and any other
289 non-zero exit status means the revision is bad.
289 non-zero exit status means the revision is bad.
290 """
290 """
291 def print_result(nodes, good):
291 def print_result(nodes, good):
292 displayer = cmdutil.show_changeset(ui, repo, {})
292 displayer = cmdutil.show_changeset(ui, repo, {})
293 if len(nodes) == 1:
293 if len(nodes) == 1:
294 # narrowed it down to a single revision
294 # narrowed it down to a single revision
295 if good:
295 if good:
296 ui.write(_("The first good revision is:\n"))
296 ui.write(_("The first good revision is:\n"))
297 else:
297 else:
298 ui.write(_("The first bad revision is:\n"))
298 ui.write(_("The first bad revision is:\n"))
299 displayer.show(repo[nodes[0]])
299 displayer.show(repo[nodes[0]])
300 else:
300 else:
301 # multiple possible revisions
301 # multiple possible revisions
302 if good:
302 if good:
303 ui.write(_("Due to skipped revisions, the first "
303 ui.write(_("Due to skipped revisions, the first "
304 "good revision could be any of:\n"))
304 "good revision could be any of:\n"))
305 else:
305 else:
306 ui.write(_("Due to skipped revisions, the first "
306 ui.write(_("Due to skipped revisions, the first "
307 "bad revision could be any of:\n"))
307 "bad revision could be any of:\n"))
308 for n in nodes:
308 for n in nodes:
309 displayer.show(repo[n])
309 displayer.show(repo[n])
310
310
311 def check_state(state, interactive=True):
311 def check_state(state, interactive=True):
312 if not state['good'] or not state['bad']:
312 if not state['good'] or not state['bad']:
313 if (good or bad or skip or reset) and interactive:
313 if (good or bad or skip or reset) and interactive:
314 return
314 return
315 if not state['good']:
315 if not state['good']:
316 raise util.Abort(_('cannot bisect (no known good revisions)'))
316 raise util.Abort(_('cannot bisect (no known good revisions)'))
317 else:
317 else:
318 raise util.Abort(_('cannot bisect (no known bad revisions)'))
318 raise util.Abort(_('cannot bisect (no known bad revisions)'))
319 return True
319 return True
320
320
321 # backward compatibility
321 # backward compatibility
322 if rev in "good bad reset init".split():
322 if rev in "good bad reset init".split():
323 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
323 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
324 cmd, rev, extra = rev, extra, None
324 cmd, rev, extra = rev, extra, None
325 if cmd == "good":
325 if cmd == "good":
326 good = True
326 good = True
327 elif cmd == "bad":
327 elif cmd == "bad":
328 bad = True
328 bad = True
329 else:
329 else:
330 reset = True
330 reset = True
331 elif extra or good + bad + skip + reset + bool(command) > 1:
331 elif extra or good + bad + skip + reset + bool(command) > 1:
332 raise util.Abort(_('incompatible arguments'))
332 raise util.Abort(_('incompatible arguments'))
333
333
334 if reset:
334 if reset:
335 p = repo.join("bisect.state")
335 p = repo.join("bisect.state")
336 if os.path.exists(p):
336 if os.path.exists(p):
337 os.unlink(p)
337 os.unlink(p)
338 return
338 return
339
339
340 state = hbisect.load_state(repo)
340 state = hbisect.load_state(repo)
341
341
342 if command:
342 if command:
343 changesets = 1
343 changesets = 1
344 try:
344 try:
345 while changesets:
345 while changesets:
346 # update state
346 # update state
347 status = util.system(command)
347 status = util.system(command)
348 if status == 125:
348 if status == 125:
349 transition = "skip"
349 transition = "skip"
350 elif status == 0:
350 elif status == 0:
351 transition = "good"
351 transition = "good"
352 # status < 0 means process was killed
352 # status < 0 means process was killed
353 elif status == 127:
353 elif status == 127:
354 raise util.Abort(_("failed to execute %s") % command)
354 raise util.Abort(_("failed to execute %s") % command)
355 elif status < 0:
355 elif status < 0:
356 raise util.Abort(_("%s killed") % command)
356 raise util.Abort(_("%s killed") % command)
357 else:
357 else:
358 transition = "bad"
358 transition = "bad"
359 ctx = repo[rev or '.']
359 ctx = repo[rev or '.']
360 state[transition].append(ctx.node())
360 state[transition].append(ctx.node())
361 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
361 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
362 check_state(state, interactive=False)
362 check_state(state, interactive=False)
363 # bisect
363 # bisect
364 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
364 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
365 # update to next check
365 # update to next check
366 cmdutil.bail_if_changed(repo)
366 cmdutil.bail_if_changed(repo)
367 hg.clean(repo, nodes[0], show_stats=False)
367 hg.clean(repo, nodes[0], show_stats=False)
368 finally:
368 finally:
369 hbisect.save_state(repo, state)
369 hbisect.save_state(repo, state)
370 return print_result(nodes, good)
370 return print_result(nodes, good)
371
371
372 # update state
372 # update state
373 node = repo.lookup(rev or '.')
373 node = repo.lookup(rev or '.')
374 if good:
374 if good:
375 state['good'].append(node)
375 state['good'].append(node)
376 elif bad:
376 elif bad:
377 state['bad'].append(node)
377 state['bad'].append(node)
378 elif skip:
378 elif skip:
379 state['skip'].append(node)
379 state['skip'].append(node)
380
380
381 hbisect.save_state(repo, state)
381 hbisect.save_state(repo, state)
382
382
383 if not check_state(state):
383 if not check_state(state):
384 return
384 return
385
385
386 # actually bisect
386 # actually bisect
387 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
387 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
388 if changesets == 0:
388 if changesets == 0:
389 print_result(nodes, good)
389 print_result(nodes, good)
390 else:
390 else:
391 assert len(nodes) == 1 # only a single node can be tested next
391 assert len(nodes) == 1 # only a single node can be tested next
392 node = nodes[0]
392 node = nodes[0]
393 # compute the approximate number of remaining tests
393 # compute the approximate number of remaining tests
394 tests, size = 0, 2
394 tests, size = 0, 2
395 while size <= changesets:
395 while size <= changesets:
396 tests, size = tests + 1, size * 2
396 tests, size = tests + 1, size * 2
397 rev = repo.changelog.rev(node)
397 rev = repo.changelog.rev(node)
398 ui.write(_("Testing changeset %d:%s "
398 ui.write(_("Testing changeset %d:%s "
399 "(%d changesets remaining, ~%d tests)\n")
399 "(%d changesets remaining, ~%d tests)\n")
400 % (rev, short(node), changesets, tests))
400 % (rev, short(node), changesets, tests))
401 if not noupdate:
401 if not noupdate:
402 cmdutil.bail_if_changed(repo)
402 cmdutil.bail_if_changed(repo)
403 return hg.clean(repo, node)
403 return hg.clean(repo, node)
404
404
405 def branch(ui, repo, label=None, **opts):
405 def branch(ui, repo, label=None, **opts):
406 """set or show the current branch name
406 """set or show the current branch name
407
407
408 With no argument, show the current branch name. With one argument,
408 With no argument, show the current branch name. With one argument,
409 set the working directory branch name (the branch will not exist
409 set the working directory branch name (the branch will not exist
410 in the repository until the next commit). Standard practice
410 in the repository until the next commit). Standard practice
411 recommends that primary development take place on the 'default'
411 recommends that primary development take place on the 'default'
412 branch.
412 branch.
413
413
414 Unless -f/--force is specified, branch will not let you set a
414 Unless -f/--force is specified, branch will not let you set a
415 branch name that already exists, even if it's inactive.
415 branch name that already exists, even if it's inactive.
416
416
417 Use -C/--clean to reset the working directory branch to that of
417 Use -C/--clean to reset the working directory branch to that of
418 the parent of the working directory, negating a previous branch
418 the parent of the working directory, negating a previous branch
419 change.
419 change.
420
420
421 Use the command 'hg update' to switch to an existing branch. Use
421 Use the command 'hg update' to switch to an existing branch. Use
422 'hg commit --close-branch' to mark this branch as closed.
422 'hg commit --close-branch' to mark this branch as closed.
423 """
423 """
424
424
425 if opts.get('clean'):
425 if opts.get('clean'):
426 label = repo[None].parents()[0].branch()
426 label = repo[None].parents()[0].branch()
427 repo.dirstate.setbranch(label)
427 repo.dirstate.setbranch(label)
428 ui.status(_('reset working directory to branch %s\n') % label)
428 ui.status(_('reset working directory to branch %s\n') % label)
429 elif label:
429 elif label:
430 if not opts.get('force') and label in repo.branchtags():
430 if not opts.get('force') and label in repo.branchtags():
431 if label not in [p.branch() for p in repo.parents()]:
431 if label not in [p.branch() for p in repo.parents()]:
432 raise util.Abort(_('a branch of the same name already exists'
432 raise util.Abort(_('a branch of the same name already exists'
433 ' (use --force to override)'))
433 ' (use --force to override)'))
434 repo.dirstate.setbranch(encoding.fromlocal(label))
434 repo.dirstate.setbranch(encoding.fromlocal(label))
435 ui.status(_('marked working directory as branch %s\n') % label)
435 ui.status(_('marked working directory as branch %s\n') % label)
436 else:
436 else:
437 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
437 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
438
438
439 def branches(ui, repo, active=False, closed=False):
439 def branches(ui, repo, active=False, closed=False):
440 """list repository named branches
440 """list repository named branches
441
441
442 List the repository's named branches, indicating which ones are
442 List the repository's named branches, indicating which ones are
443 inactive. If -c/--closed is specified, also list branches which have
443 inactive. If -c/--closed is specified, also list branches which have
444 been marked closed (see hg commit --close-branch).
444 been marked closed (see hg commit --close-branch).
445
445
446 If -a/--active is specified, only show active branches. A branch
446 If -a/--active is specified, only show active branches. A branch
447 is considered active if it contains repository heads.
447 is considered active if it contains repository heads.
448
448
449 Use the command 'hg update' to switch to an existing branch.
449 Use the command 'hg update' to switch to an existing branch.
450 """
450 """
451
451
452 hexfunc = ui.debugflag and hex or short
452 hexfunc = ui.debugflag and hex or short
453 activebranches = [encoding.tolocal(repo[n].branch())
453 activebranches = [encoding.tolocal(repo[n].branch())
454 for n in repo.heads()]
454 for n in repo.heads()]
455 def testactive(tag, node):
455 def testactive(tag, node):
456 realhead = tag in activebranches
456 realhead = tag in activebranches
457 open = node in repo.branchheads(tag, closed=False)
457 open = node in repo.branchheads(tag, closed=False)
458 return realhead and open
458 return realhead and open
459 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
459 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
460 for tag, node in repo.branchtags().items()],
460 for tag, node in repo.branchtags().items()],
461 reverse=True)
461 reverse=True)
462
462
463 for isactive, node, tag in branches:
463 for isactive, node, tag in branches:
464 if (not active) or isactive:
464 if (not active) or isactive:
465 if ui.quiet:
465 if ui.quiet:
466 ui.write("%s\n" % tag)
466 ui.write("%s\n" % tag)
467 else:
467 else:
468 hn = repo.lookup(node)
468 hn = repo.lookup(node)
469 if isactive:
469 if isactive:
470 notice = ''
470 notice = ''
471 elif hn not in repo.branchheads(tag, closed=False):
471 elif hn not in repo.branchheads(tag, closed=False):
472 if not closed:
472 if not closed:
473 continue
473 continue
474 notice = ' (closed)'
474 notice = ' (closed)'
475 else:
475 else:
476 notice = ' (inactive)'
476 notice = ' (inactive)'
477 rev = str(node).rjust(31 - encoding.colwidth(tag))
477 rev = str(node).rjust(31 - encoding.colwidth(tag))
478 data = tag, rev, hexfunc(hn), notice
478 data = tag, rev, hexfunc(hn), notice
479 ui.write("%s %s:%s%s\n" % data)
479 ui.write("%s %s:%s%s\n" % data)
480
480
481 def bundle(ui, repo, fname, dest=None, **opts):
481 def bundle(ui, repo, fname, dest=None, **opts):
482 """create a changegroup file
482 """create a changegroup file
483
483
484 Generate a compressed changegroup file collecting changesets not
484 Generate a compressed changegroup file collecting changesets not
485 known to be in another repository.
485 known to be in another repository.
486
486
487 If no destination repository is specified the destination is
487 If no destination repository is specified the destination is
488 assumed to have all the nodes specified by one or more --base
488 assumed to have all the nodes specified by one or more --base
489 parameters. To create a bundle containing all changesets, use
489 parameters. To create a bundle containing all changesets, use
490 -a/--all (or --base null).
490 -a/--all (or --base null).
491
491
492 You can change compression method with the -t/--type option.
492 You can change compression method with the -t/--type option.
493 The available compression methods are: none, bzip2, and
493 The available compression methods are: none, bzip2, and
494 gzip (by default, bundles are compressed using bzip2).
494 gzip (by default, bundles are compressed using bzip2).
495
495
496 The bundle file can then be transferred using conventional means
496 The bundle file can then be transferred using conventional means
497 and applied to another repository with the unbundle or pull
497 and applied to another repository with the unbundle or pull
498 command. This is useful when direct push and pull are not
498 command. This is useful when direct push and pull are not
499 available or when exporting an entire repository is undesirable.
499 available or when exporting an entire repository is undesirable.
500
500
501 Applying bundles preserves all changeset contents including
501 Applying bundles preserves all changeset contents including
502 permissions, copy/rename information, and revision history.
502 permissions, copy/rename information, and revision history.
503 """
503 """
504 revs = opts.get('rev') or None
504 revs = opts.get('rev') or None
505 if revs:
505 if revs:
506 revs = [repo.lookup(rev) for rev in revs]
506 revs = [repo.lookup(rev) for rev in revs]
507 if opts.get('all'):
507 if opts.get('all'):
508 base = ['null']
508 base = ['null']
509 else:
509 else:
510 base = opts.get('base')
510 base = opts.get('base')
511 if base:
511 if base:
512 if dest:
512 if dest:
513 raise util.Abort(_("--base is incompatible with specifying "
513 raise util.Abort(_("--base is incompatible with specifying "
514 "a destination"))
514 "a destination"))
515 base = [repo.lookup(rev) for rev in base]
515 base = [repo.lookup(rev) for rev in base]
516 # create the right base
516 # create the right base
517 # XXX: nodesbetween / changegroup* should be "fixed" instead
517 # XXX: nodesbetween / changegroup* should be "fixed" instead
518 o = []
518 o = []
519 has = set((nullid,))
519 has = set((nullid,))
520 for n in base:
520 for n in base:
521 has.update(repo.changelog.reachable(n))
521 has.update(repo.changelog.reachable(n))
522 if revs:
522 if revs:
523 visit = list(revs)
523 visit = list(revs)
524 else:
524 else:
525 visit = repo.changelog.heads()
525 visit = repo.changelog.heads()
526 seen = {}
526 seen = {}
527 while visit:
527 while visit:
528 n = visit.pop(0)
528 n = visit.pop(0)
529 parents = [p for p in repo.changelog.parents(n) if p not in has]
529 parents = [p for p in repo.changelog.parents(n) if p not in has]
530 if len(parents) == 0:
530 if len(parents) == 0:
531 o.insert(0, n)
531 o.insert(0, n)
532 else:
532 else:
533 for p in parents:
533 for p in parents:
534 if p not in seen:
534 if p not in seen:
535 seen[p] = 1
535 seen[p] = 1
536 visit.append(p)
536 visit.append(p)
537 else:
537 else:
538 dest, revs, checkout = hg.parseurl(
538 dest, revs, checkout = hg.parseurl(
539 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
539 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
540 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
540 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
541 o = repo.findoutgoing(other, force=opts.get('force'))
541 o = repo.findoutgoing(other, force=opts.get('force'))
542
542
543 if revs:
543 if revs:
544 cg = repo.changegroupsubset(o, revs, 'bundle')
544 cg = repo.changegroupsubset(o, revs, 'bundle')
545 else:
545 else:
546 cg = repo.changegroup(o, 'bundle')
546 cg = repo.changegroup(o, 'bundle')
547
547
548 bundletype = opts.get('type', 'bzip2').lower()
548 bundletype = opts.get('type', 'bzip2').lower()
549 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
549 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
550 bundletype = btypes.get(bundletype)
550 bundletype = btypes.get(bundletype)
551 if bundletype not in changegroup.bundletypes:
551 if bundletype not in changegroup.bundletypes:
552 raise util.Abort(_('unknown bundle type specified with --type'))
552 raise util.Abort(_('unknown bundle type specified with --type'))
553
553
554 changegroup.writebundle(cg, fname, bundletype)
554 changegroup.writebundle(cg, fname, bundletype)
555
555
556 def cat(ui, repo, file1, *pats, **opts):
556 def cat(ui, repo, file1, *pats, **opts):
557 """output the current or given revision of files
557 """output the current or given revision of files
558
558
559 Print the specified files as they were at the given revision. If
559 Print the specified files as they were at the given revision. If
560 no revision is given, the parent of the working directory is used,
560 no revision is given, the parent of the working directory is used,
561 or tip if no revision is checked out.
561 or tip if no revision is checked out.
562
562
563 Output may be to a file, in which case the name of the file is
563 Output may be to a file, in which case the name of the file is
564 given using a format string. The formatting rules are the same as
564 given using a format string. The formatting rules are the same as
565 for the export command, with the following additions::
565 for the export command, with the following additions::
566
566
567 %s basename of file being printed
567 %s basename of file being printed
568 %d dirname of file being printed, or '.' if in repository root
568 %d dirname of file being printed, or '.' if in repository root
569 %p root-relative path name of file being printed
569 %p root-relative path name of file being printed
570 """
570 """
571 ctx = repo[opts.get('rev')]
571 ctx = repo[opts.get('rev')]
572 err = 1
572 err = 1
573 m = cmdutil.match(repo, (file1,) + pats, opts)
573 m = cmdutil.match(repo, (file1,) + pats, opts)
574 for abs in ctx.walk(m):
574 for abs in ctx.walk(m):
575 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
575 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
576 data = ctx[abs].data()
576 data = ctx[abs].data()
577 if opts.get('decode'):
577 if opts.get('decode'):
578 data = repo.wwritedata(abs, data)
578 data = repo.wwritedata(abs, data)
579 fp.write(data)
579 fp.write(data)
580 err = 0
580 err = 0
581 return err
581 return err
582
582
583 def clone(ui, source, dest=None, **opts):
583 def clone(ui, source, dest=None, **opts):
584 """make a copy of an existing repository
584 """make a copy of an existing repository
585
585
586 Create a copy of an existing repository in a new directory.
586 Create a copy of an existing repository in a new directory.
587
587
588 If no destination directory name is specified, it defaults to the
588 If no destination directory name is specified, it defaults to the
589 basename of the source.
589 basename of the source.
590
590
591 The location of the source is added to the new repository's
591 The location of the source is added to the new repository's
592 .hg/hgrc file, as the default to be used for future pulls.
592 .hg/hgrc file, as the default to be used for future pulls.
593
593
594 If you use the -r/--rev option to clone up to a specific revision,
594 If you use the -r/--rev option to clone up to a specific revision,
595 no subsequent revisions (including subsequent tags) will be
595 no subsequent revisions (including subsequent tags) will be
596 present in the cloned repository. This option implies --pull, even
596 present in the cloned repository. This option implies --pull, even
597 on local repositories.
597 on local repositories.
598
598
599 By default, clone will check out the head of the 'default' branch.
599 By default, clone will check out the head of the 'default' branch.
600 If the -U/--noupdate option is used, the new clone will contain
600 If the -U/--noupdate option is used, the new clone will contain
601 only a repository (.hg) and no working copy (the working copy
601 only a repository (.hg) and no working copy (the working copy
602 parent is the null revision).
602 parent is the null revision).
603
603
604 See 'hg help urls' for valid source format details.
604 See 'hg help urls' for valid source format details.
605
605
606 It is possible to specify an ssh:// URL as the destination, but no
606 It is possible to specify an ssh:// URL as the destination, but no
607 .hg/hgrc and working directory will be created on the remote side.
607 .hg/hgrc and working directory will be created on the remote side.
608 Please see 'hg help urls' for important details about ssh:// URLs.
608 Please see 'hg help urls' for important details about ssh:// URLs.
609
609
610 For efficiency, hardlinks are used for cloning whenever the source
610 For efficiency, hardlinks are used for cloning whenever the source
611 and destination are on the same filesystem (note this applies only
611 and destination are on the same filesystem (note this applies only
612 to the repository data, not to the checked out files). Some
612 to the repository data, not to the checked out files). Some
613 filesystems, such as AFS, implement hardlinking incorrectly, but
613 filesystems, such as AFS, implement hardlinking incorrectly, but
614 do not report errors. In these cases, use the --pull option to
614 do not report errors. In these cases, use the --pull option to
615 avoid hardlinking.
615 avoid hardlinking.
616
616
617 In some cases, you can clone repositories and checked out files
617 In some cases, you can clone repositories and checked out files
618 using full hardlinks with ::
618 using full hardlinks with ::
619
619
620 $ cp -al REPO REPOCLONE
620 $ cp -al REPO REPOCLONE
621
621
622 This is the fastest way to clone, but it is not always safe. The
622 This is the fastest way to clone, but it is not always safe. The
623 operation is not atomic (making sure REPO is not modified during
623 operation is not atomic (making sure REPO is not modified during
624 the operation is up to you) and you have to make sure your editor
624 the operation is up to you) and you have to make sure your editor
625 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
625 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
626 this is not compatible with certain extensions that place their
626 this is not compatible with certain extensions that place their
627 metadata under the .hg directory, such as mq.
627 metadata under the .hg directory, such as mq.
628 """
628 """
629 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
629 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
630 pull=opts.get('pull'),
630 pull=opts.get('pull'),
631 stream=opts.get('uncompressed'),
631 stream=opts.get('uncompressed'),
632 rev=opts.get('rev'),
632 rev=opts.get('rev'),
633 update=not opts.get('noupdate'))
633 update=not opts.get('noupdate'))
634
634
635 def commit(ui, repo, *pats, **opts):
635 def commit(ui, repo, *pats, **opts):
636 """commit the specified files or all outstanding changes
636 """commit the specified files or all outstanding changes
637
637
638 Commit changes to the given files into the repository. Unlike a
638 Commit changes to the given files into the repository. Unlike a
639 centralized RCS, this operation is a local operation. See hg push
639 centralized RCS, this operation is a local operation. See hg push
640 for a way to actively distribute your changes.
640 for a way to actively distribute your changes.
641
641
642 If a list of files is omitted, all changes reported by "hg status"
642 If a list of files is omitted, all changes reported by "hg status"
643 will be committed.
643 will be committed.
644
644
645 If you are committing the result of a merge, do not provide any
645 If you are committing the result of a merge, do not provide any
646 filenames or -I/-X filters.
646 filenames or -I/-X filters.
647
647
648 If no commit message is specified, the configured editor is
648 If no commit message is specified, the configured editor is
649 started to prompt you for a message.
649 started to prompt you for a message.
650
650
651 See 'hg help dates' for a list of formats valid for -d/--date.
651 See 'hg help dates' for a list of formats valid for -d/--date.
652 """
652 """
653 extra = {}
653 extra = {}
654 if opts.get('close_branch'):
654 if opts.get('close_branch'):
655 extra['close'] = 1
655 extra['close'] = 1
656 e = cmdutil.commiteditor
656 e = cmdutil.commiteditor
657 if opts.get('force_editor'):
657 if opts.get('force_editor'):
658 e = cmdutil.commitforceeditor
658 e = cmdutil.commitforceeditor
659
659
660 def commitfunc(ui, repo, message, match, opts):
660 def commitfunc(ui, repo, message, match, opts):
661 return repo.commit(message, opts.get('user'), opts.get('date'), match,
661 return repo.commit(message, opts.get('user'), opts.get('date'), match,
662 editor=e, extra=extra)
662 editor=e, extra=extra)
663
663
664 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
664 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
665 if not node:
665 if not node:
666 ui.status(_("nothing changed\n"))
666 ui.status(_("nothing changed\n"))
667 return
667 return
668 cl = repo.changelog
668 cl = repo.changelog
669 rev = cl.rev(node)
669 rev = cl.rev(node)
670 parents = cl.parentrevs(rev)
670 parents = cl.parentrevs(rev)
671 if rev - 1 in parents:
671 if rev - 1 in parents:
672 # one of the parents was the old tip
672 # one of the parents was the old tip
673 pass
673 pass
674 elif (parents == (nullrev, nullrev) or
674 elif (parents == (nullrev, nullrev) or
675 len(cl.heads(cl.node(parents[0]))) > 1 and
675 len(cl.heads(cl.node(parents[0]))) > 1 and
676 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
676 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
677 ui.status(_('created new head\n'))
677 ui.status(_('created new head\n'))
678
678
679 if ui.debugflag:
679 if ui.debugflag:
680 ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
680 ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
681 elif ui.verbose:
681 elif ui.verbose:
682 ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
682 ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
683
683
684 def copy(ui, repo, *pats, **opts):
684 def copy(ui, repo, *pats, **opts):
685 """mark files as copied for the next commit
685 """mark files as copied for the next commit
686
686
687 Mark dest as having copies of source files. If dest is a
687 Mark dest as having copies of source files. If dest is a
688 directory, copies are put in that directory. If dest is a file,
688 directory, copies are put in that directory. If dest is a file,
689 the source must be a single file.
689 the source must be a single file.
690
690
691 By default, this command copies the contents of files as they
691 By default, this command copies the contents of files as they
692 exist in the working directory. If invoked with -A/--after, the
692 exist in the working directory. If invoked with -A/--after, the
693 operation is recorded, but no copying is performed.
693 operation is recorded, but no copying is performed.
694
694
695 This command takes effect with the next commit. To undo a copy
695 This command takes effect with the next commit. To undo a copy
696 before that, see hg revert.
696 before that, see hg revert.
697 """
697 """
698 wlock = repo.wlock(False)
698 wlock = repo.wlock(False)
699 try:
699 try:
700 return cmdutil.copy(ui, repo, pats, opts)
700 return cmdutil.copy(ui, repo, pats, opts)
701 finally:
701 finally:
702 wlock.release()
702 wlock.release()
703
703
704 def debugancestor(ui, repo, *args):
704 def debugancestor(ui, repo, *args):
705 """find the ancestor revision of two revisions in a given index"""
705 """find the ancestor revision of two revisions in a given index"""
706 if len(args) == 3:
706 if len(args) == 3:
707 index, rev1, rev2 = args
707 index, rev1, rev2 = args
708 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
708 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
709 lookup = r.lookup
709 lookup = r.lookup
710 elif len(args) == 2:
710 elif len(args) == 2:
711 if not repo:
711 if not repo:
712 raise util.Abort(_("There is no Mercurial repository here "
712 raise util.Abort(_("There is no Mercurial repository here "
713 "(.hg not found)"))
713 "(.hg not found)"))
714 rev1, rev2 = args
714 rev1, rev2 = args
715 r = repo.changelog
715 r = repo.changelog
716 lookup = repo.lookup
716 lookup = repo.lookup
717 else:
717 else:
718 raise util.Abort(_('either two or three arguments required'))
718 raise util.Abort(_('either two or three arguments required'))
719 a = r.ancestor(lookup(rev1), lookup(rev2))
719 a = r.ancestor(lookup(rev1), lookup(rev2))
720 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
720 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
721
721
722 def debugcommands(ui, cmd='', *args):
722 def debugcommands(ui, cmd='', *args):
723 for cmd, vals in sorted(table.iteritems()):
723 for cmd, vals in sorted(table.iteritems()):
724 cmd = cmd.split('|')[0].strip('^')
724 cmd = cmd.split('|')[0].strip('^')
725 opts = ', '.join([i[1] for i in vals[1]])
725 opts = ', '.join([i[1] for i in vals[1]])
726 ui.write('%s: %s\n' % (cmd, opts))
726 ui.write('%s: %s\n' % (cmd, opts))
727
727
728 def debugcomplete(ui, cmd='', **opts):
728 def debugcomplete(ui, cmd='', **opts):
729 """returns the completion list associated with the given command"""
729 """returns the completion list associated with the given command"""
730
730
731 if opts.get('options'):
731 if opts.get('options'):
732 options = []
732 options = []
733 otables = [globalopts]
733 otables = [globalopts]
734 if cmd:
734 if cmd:
735 aliases, entry = cmdutil.findcmd(cmd, table, False)
735 aliases, entry = cmdutil.findcmd(cmd, table, False)
736 otables.append(entry[1])
736 otables.append(entry[1])
737 for t in otables:
737 for t in otables:
738 for o in t:
738 for o in t:
739 if o[0]:
739 if o[0]:
740 options.append('-%s' % o[0])
740 options.append('-%s' % o[0])
741 options.append('--%s' % o[1])
741 options.append('--%s' % o[1])
742 ui.write("%s\n" % "\n".join(options))
742 ui.write("%s\n" % "\n".join(options))
743 return
743 return
744
744
745 cmdlist = cmdutil.findpossible(cmd, table)
745 cmdlist = cmdutil.findpossible(cmd, table)
746 if ui.verbose:
746 if ui.verbose:
747 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
747 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
748 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
748 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
749
749
750 def debugfsinfo(ui, path = "."):
750 def debugfsinfo(ui, path = "."):
751 open('.debugfsinfo', 'w').write('')
751 open('.debugfsinfo', 'w').write('')
752 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
752 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
753 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
753 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
754 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
754 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
755 and 'yes' or 'no'))
755 and 'yes' or 'no'))
756 os.unlink('.debugfsinfo')
756 os.unlink('.debugfsinfo')
757
757
758 def debugrebuildstate(ui, repo, rev="tip"):
758 def debugrebuildstate(ui, repo, rev="tip"):
759 """rebuild the dirstate as it would look like for the given revision"""
759 """rebuild the dirstate as it would look like for the given revision"""
760 ctx = repo[rev]
760 ctx = repo[rev]
761 wlock = repo.wlock()
761 wlock = repo.wlock()
762 try:
762 try:
763 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
763 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
764 finally:
764 finally:
765 wlock.release()
765 wlock.release()
766
766
767 def debugcheckstate(ui, repo):
767 def debugcheckstate(ui, repo):
768 """validate the correctness of the current dirstate"""
768 """validate the correctness of the current dirstate"""
769 parent1, parent2 = repo.dirstate.parents()
769 parent1, parent2 = repo.dirstate.parents()
770 m1 = repo[parent1].manifest()
770 m1 = repo[parent1].manifest()
771 m2 = repo[parent2].manifest()
771 m2 = repo[parent2].manifest()
772 errors = 0
772 errors = 0
773 for f in repo.dirstate:
773 for f in repo.dirstate:
774 state = repo.dirstate[f]
774 state = repo.dirstate[f]
775 if state in "nr" and f not in m1:
775 if state in "nr" and f not in m1:
776 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
776 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
777 errors += 1
777 errors += 1
778 if state in "a" and f in m1:
778 if state in "a" and f in m1:
779 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
779 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
780 errors += 1
780 errors += 1
781 if state in "m" and f not in m1 and f not in m2:
781 if state in "m" and f not in m1 and f not in m2:
782 ui.warn(_("%s in state %s, but not in either manifest\n") %
782 ui.warn(_("%s in state %s, but not in either manifest\n") %
783 (f, state))
783 (f, state))
784 errors += 1
784 errors += 1
785 for f in m1:
785 for f in m1:
786 state = repo.dirstate[f]
786 state = repo.dirstate[f]
787 if state not in "nrm":
787 if state not in "nrm":
788 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
788 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
789 errors += 1
789 errors += 1
790 if errors:
790 if errors:
791 error = _(".hg/dirstate inconsistent with current parent's manifest")
791 error = _(".hg/dirstate inconsistent with current parent's manifest")
792 raise util.Abort(error)
792 raise util.Abort(error)
793
793
794 def showconfig(ui, repo, *values, **opts):
794 def showconfig(ui, repo, *values, **opts):
795 """show combined config settings from all hgrc files
795 """show combined config settings from all hgrc files
796
796
797 With no arguments, print names and values of all config items.
797 With no arguments, print names and values of all config items.
798
798
799 With one argument of the form section.name, print just the value
799 With one argument of the form section.name, print just the value
800 of that config item.
800 of that config item.
801
801
802 With multiple arguments, print names and values of all config
802 With multiple arguments, print names and values of all config
803 items with matching section names.
803 items with matching section names.
804
804
805 With --debug, the source (filename and line number) is printed
805 With --debug, the source (filename and line number) is printed
806 for each config item.
806 for each config item.
807 """
807 """
808
808
809 untrusted = bool(opts.get('untrusted'))
809 untrusted = bool(opts.get('untrusted'))
810 if values:
810 if values:
811 if len([v for v in values if '.' in v]) > 1:
811 if len([v for v in values if '.' in v]) > 1:
812 raise util.Abort(_('only one config item permitted'))
812 raise util.Abort(_('only one config item permitted'))
813 for section, name, value in ui.walkconfig(untrusted=untrusted):
813 for section, name, value in ui.walkconfig(untrusted=untrusted):
814 sectname = section + '.' + name
814 sectname = section + '.' + name
815 if values:
815 if values:
816 for v in values:
816 for v in values:
817 if v == section:
817 if v == section:
818 ui.debug('%s: ' %
818 ui.debug('%s: ' %
819 ui.configsource(section, name, untrusted))
819 ui.configsource(section, name, untrusted))
820 ui.write('%s=%s\n' % (sectname, value))
820 ui.write('%s=%s\n' % (sectname, value))
821 elif v == sectname:
821 elif v == sectname:
822 ui.debug('%s: ' %
822 ui.debug('%s: ' %
823 ui.configsource(section, name, untrusted))
823 ui.configsource(section, name, untrusted))
824 ui.write(value, '\n')
824 ui.write(value, '\n')
825 else:
825 else:
826 ui.debug('%s: ' %
826 ui.debug('%s: ' %
827 ui.configsource(section, name, untrusted))
827 ui.configsource(section, name, untrusted))
828 ui.write('%s=%s\n' % (sectname, value))
828 ui.write('%s=%s\n' % (sectname, value))
829
829
830 def debugsetparents(ui, repo, rev1, rev2=None):
830 def debugsetparents(ui, repo, rev1, rev2=None):
831 """manually set the parents of the current working directory
831 """manually set the parents of the current working directory
832
832
833 This is useful for writing repository conversion tools, but should
833 This is useful for writing repository conversion tools, but should
834 be used with care.
834 be used with care.
835 """
835 """
836
836
837 if not rev2:
837 if not rev2:
838 rev2 = hex(nullid)
838 rev2 = hex(nullid)
839
839
840 wlock = repo.wlock()
840 wlock = repo.wlock()
841 try:
841 try:
842 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
842 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
843 finally:
843 finally:
844 wlock.release()
844 wlock.release()
845
845
846 def debugstate(ui, repo, nodates=None):
846 def debugstate(ui, repo, nodates=None):
847 """show the contents of the current dirstate"""
847 """show the contents of the current dirstate"""
848 timestr = ""
848 timestr = ""
849 showdate = not nodates
849 showdate = not nodates
850 for file_, ent in sorted(repo.dirstate._map.iteritems()):
850 for file_, ent in sorted(repo.dirstate._map.iteritems()):
851 if showdate:
851 if showdate:
852 if ent[3] == -1:
852 if ent[3] == -1:
853 # Pad or slice to locale representation
853 # Pad or slice to locale representation
854 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
854 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
855 timestr = 'unset'
855 timestr = 'unset'
856 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
856 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
857 else:
857 else:
858 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
858 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
859 if ent[1] & 020000:
859 if ent[1] & 020000:
860 mode = 'lnk'
860 mode = 'lnk'
861 else:
861 else:
862 mode = '%3o' % (ent[1] & 0777)
862 mode = '%3o' % (ent[1] & 0777)
863 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
863 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
864 for f in repo.dirstate.copies():
864 for f in repo.dirstate.copies():
865 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
865 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
866
866
867 def debugsub(ui, repo, rev=None):
867 def debugsub(ui, repo, rev=None):
868 if rev == '':
868 if rev == '':
869 rev = None
869 rev = None
870 for k,v in sorted(repo[rev].substate.items()):
870 for k,v in sorted(repo[rev].substate.items()):
871 ui.write('path %s\n' % k)
871 ui.write('path %s\n' % k)
872 ui.write(' source %s\n' % v[0])
872 ui.write(' source %s\n' % v[0])
873 ui.write(' revision %s\n' % v[1])
873 ui.write(' revision %s\n' % v[1])
874
874
875 def debugdata(ui, file_, rev):
875 def debugdata(ui, file_, rev):
876 """dump the contents of a data file revision"""
876 """dump the contents of a data file revision"""
877 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
877 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
878 try:
878 try:
879 ui.write(r.revision(r.lookup(rev)))
879 ui.write(r.revision(r.lookup(rev)))
880 except KeyError:
880 except KeyError:
881 raise util.Abort(_('invalid revision identifier %s') % rev)
881 raise util.Abort(_('invalid revision identifier %s') % rev)
882
882
883 def debugdate(ui, date, range=None, **opts):
883 def debugdate(ui, date, range=None, **opts):
884 """parse and display a date"""
884 """parse and display a date"""
885 if opts["extended"]:
885 if opts["extended"]:
886 d = util.parsedate(date, util.extendeddateformats)
886 d = util.parsedate(date, util.extendeddateformats)
887 else:
887 else:
888 d = util.parsedate(date)
888 d = util.parsedate(date)
889 ui.write("internal: %s %s\n" % d)
889 ui.write("internal: %s %s\n" % d)
890 ui.write("standard: %s\n" % util.datestr(d))
890 ui.write("standard: %s\n" % util.datestr(d))
891 if range:
891 if range:
892 m = util.matchdate(range)
892 m = util.matchdate(range)
893 ui.write("match: %s\n" % m(d[0]))
893 ui.write("match: %s\n" % m(d[0]))
894
894
895 def debugindex(ui, file_):
895 def debugindex(ui, file_):
896 """dump the contents of an index file"""
896 """dump the contents of an index file"""
897 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
897 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
898 ui.write(" rev offset length base linkrev"
898 ui.write(" rev offset length base linkrev"
899 " nodeid p1 p2\n")
899 " nodeid p1 p2\n")
900 for i in r:
900 for i in r:
901 node = r.node(i)
901 node = r.node(i)
902 try:
902 try:
903 pp = r.parents(node)
903 pp = r.parents(node)
904 except:
904 except:
905 pp = [nullid, nullid]
905 pp = [nullid, nullid]
906 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
906 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
907 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
907 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
908 short(node), short(pp[0]), short(pp[1])))
908 short(node), short(pp[0]), short(pp[1])))
909
909
910 def debugindexdot(ui, file_):
910 def debugindexdot(ui, file_):
911 """dump an index DAG as a graphviz dot file"""
911 """dump an index DAG as a graphviz dot file"""
912 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
912 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
913 ui.write("digraph G {\n")
913 ui.write("digraph G {\n")
914 for i in r:
914 for i in r:
915 node = r.node(i)
915 node = r.node(i)
916 pp = r.parents(node)
916 pp = r.parents(node)
917 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
917 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
918 if pp[1] != nullid:
918 if pp[1] != nullid:
919 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
919 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
920 ui.write("}\n")
920 ui.write("}\n")
921
921
922 def debuginstall(ui):
922 def debuginstall(ui):
923 '''test Mercurial installation'''
923 '''test Mercurial installation'''
924
924
925 def writetemp(contents):
925 def writetemp(contents):
926 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
926 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
927 f = os.fdopen(fd, "wb")
927 f = os.fdopen(fd, "wb")
928 f.write(contents)
928 f.write(contents)
929 f.close()
929 f.close()
930 return name
930 return name
931
931
932 problems = 0
932 problems = 0
933
933
934 # encoding
934 # encoding
935 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
935 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
936 try:
936 try:
937 encoding.fromlocal("test")
937 encoding.fromlocal("test")
938 except util.Abort, inst:
938 except util.Abort, inst:
939 ui.write(" %s\n" % inst)
939 ui.write(" %s\n" % inst)
940 ui.write(_(" (check that your locale is properly set)\n"))
940 ui.write(_(" (check that your locale is properly set)\n"))
941 problems += 1
941 problems += 1
942
942
943 # compiled modules
943 # compiled modules
944 ui.status(_("Checking extensions...\n"))
944 ui.status(_("Checking extensions...\n"))
945 try:
945 try:
946 import bdiff, mpatch, base85
946 import bdiff, mpatch, base85
947 except Exception, inst:
947 except Exception, inst:
948 ui.write(" %s\n" % inst)
948 ui.write(" %s\n" % inst)
949 ui.write(_(" One or more extensions could not be found"))
949 ui.write(_(" One or more extensions could not be found"))
950 ui.write(_(" (check that you compiled the extensions)\n"))
950 ui.write(_(" (check that you compiled the extensions)\n"))
951 problems += 1
951 problems += 1
952
952
953 # templates
953 # templates
954 ui.status(_("Checking templates...\n"))
954 ui.status(_("Checking templates...\n"))
955 try:
955 try:
956 import templater
956 import templater
957 templater.templater(templater.templatepath("map-cmdline.default"))
957 templater.templater(templater.templatepath("map-cmdline.default"))
958 except Exception, inst:
958 except Exception, inst:
959 ui.write(" %s\n" % inst)
959 ui.write(" %s\n" % inst)
960 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
960 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
961 problems += 1
961 problems += 1
962
962
963 # patch
963 # patch
964 ui.status(_("Checking patch...\n"))
964 ui.status(_("Checking patch...\n"))
965 patchproblems = 0
965 patchproblems = 0
966 a = "1\n2\n3\n4\n"
966 a = "1\n2\n3\n4\n"
967 b = "1\n2\n3\ninsert\n4\n"
967 b = "1\n2\n3\ninsert\n4\n"
968 fa = writetemp(a)
968 fa = writetemp(a)
969 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
969 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
970 os.path.basename(fa))
970 os.path.basename(fa))
971 fd = writetemp(d)
971 fd = writetemp(d)
972
972
973 files = {}
973 files = {}
974 try:
974 try:
975 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
975 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
976 except util.Abort, e:
976 except util.Abort, e:
977 ui.write(_(" patch call failed:\n"))
977 ui.write(_(" patch call failed:\n"))
978 ui.write(" " + str(e) + "\n")
978 ui.write(" " + str(e) + "\n")
979 patchproblems += 1
979 patchproblems += 1
980 else:
980 else:
981 if list(files) != [os.path.basename(fa)]:
981 if list(files) != [os.path.basename(fa)]:
982 ui.write(_(" unexpected patch output!\n"))
982 ui.write(_(" unexpected patch output!\n"))
983 patchproblems += 1
983 patchproblems += 1
984 a = open(fa).read()
984 a = open(fa).read()
985 if a != b:
985 if a != b:
986 ui.write(_(" patch test failed!\n"))
986 ui.write(_(" patch test failed!\n"))
987 patchproblems += 1
987 patchproblems += 1
988
988
989 if patchproblems:
989 if patchproblems:
990 if ui.config('ui', 'patch'):
990 if ui.config('ui', 'patch'):
991 ui.write(_(" (Current patch tool may be incompatible with patch,"
991 ui.write(_(" (Current patch tool may be incompatible with patch,"
992 " or misconfigured. Please check your .hgrc file)\n"))
992 " or misconfigured. Please check your .hgrc file)\n"))
993 else:
993 else:
994 ui.write(_(" Internal patcher failure, please report this error"
994 ui.write(_(" Internal patcher failure, please report this error"
995 " to http://mercurial.selenic.com/bts/\n"))
995 " to http://mercurial.selenic.com/bts/\n"))
996 problems += patchproblems
996 problems += patchproblems
997
997
998 os.unlink(fa)
998 os.unlink(fa)
999 os.unlink(fd)
999 os.unlink(fd)
1000
1000
1001 # editor
1001 # editor
1002 ui.status(_("Checking commit editor...\n"))
1002 ui.status(_("Checking commit editor...\n"))
1003 editor = ui.geteditor()
1003 editor = ui.geteditor()
1004 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1004 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1005 if not cmdpath:
1005 if not cmdpath:
1006 if editor == 'vi':
1006 if editor == 'vi':
1007 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1007 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1008 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1008 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1009 else:
1009 else:
1010 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1010 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1011 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1011 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
1012 problems += 1
1012 problems += 1
1013
1013
1014 # check username
1014 # check username
1015 ui.status(_("Checking username...\n"))
1015 ui.status(_("Checking username...\n"))
1016 user = os.environ.get("HGUSER")
1016 user = os.environ.get("HGUSER")
1017 if user is None:
1017 if user is None:
1018 user = ui.config("ui", "username")
1018 user = ui.config("ui", "username")
1019 if user is None:
1019 if user is None:
1020 user = os.environ.get("EMAIL")
1020 user = os.environ.get("EMAIL")
1021 if not user:
1021 if not user:
1022 ui.warn(" ")
1022 ui.warn(" ")
1023 ui.username()
1023 ui.username()
1024 ui.write(_(" (specify a username in your .hgrc file)\n"))
1024 ui.write(_(" (specify a username in your .hgrc file)\n"))
1025
1025
1026 if not problems:
1026 if not problems:
1027 ui.status(_("No problems detected\n"))
1027 ui.status(_("No problems detected\n"))
1028 else:
1028 else:
1029 ui.write(_("%s problems detected,"
1029 ui.write(_("%s problems detected,"
1030 " please check your install!\n") % problems)
1030 " please check your install!\n") % problems)
1031
1031
1032 return problems
1032 return problems
1033
1033
1034 def debugrename(ui, repo, file1, *pats, **opts):
1034 def debugrename(ui, repo, file1, *pats, **opts):
1035 """dump rename information"""
1035 """dump rename information"""
1036
1036
1037 ctx = repo[opts.get('rev')]
1037 ctx = repo[opts.get('rev')]
1038 m = cmdutil.match(repo, (file1,) + pats, opts)
1038 m = cmdutil.match(repo, (file1,) + pats, opts)
1039 for abs in ctx.walk(m):
1039 for abs in ctx.walk(m):
1040 fctx = ctx[abs]
1040 fctx = ctx[abs]
1041 o = fctx.filelog().renamed(fctx.filenode())
1041 o = fctx.filelog().renamed(fctx.filenode())
1042 rel = m.rel(abs)
1042 rel = m.rel(abs)
1043 if o:
1043 if o:
1044 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1044 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1045 else:
1045 else:
1046 ui.write(_("%s not renamed\n") % rel)
1046 ui.write(_("%s not renamed\n") % rel)
1047
1047
1048 def debugwalk(ui, repo, *pats, **opts):
1048 def debugwalk(ui, repo, *pats, **opts):
1049 """show how files match on given patterns"""
1049 """show how files match on given patterns"""
1050 m = cmdutil.match(repo, pats, opts)
1050 m = cmdutil.match(repo, pats, opts)
1051 items = list(repo.walk(m))
1051 items = list(repo.walk(m))
1052 if not items:
1052 if not items:
1053 return
1053 return
1054 fmt = 'f %%-%ds %%-%ds %%s' % (
1054 fmt = 'f %%-%ds %%-%ds %%s' % (
1055 max([len(abs) for abs in items]),
1055 max([len(abs) for abs in items]),
1056 max([len(m.rel(abs)) for abs in items]))
1056 max([len(m.rel(abs)) for abs in items]))
1057 for abs in items:
1057 for abs in items:
1058 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1058 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1059 ui.write("%s\n" % line.rstrip())
1059 ui.write("%s\n" % line.rstrip())
1060
1060
1061 def diff(ui, repo, *pats, **opts):
1061 def diff(ui, repo, *pats, **opts):
1062 """diff repository (or selected files)
1062 """diff repository (or selected files)
1063
1063
1064 Show differences between revisions for the specified files.
1064 Show differences between revisions for the specified files.
1065
1065
1066 Differences between files are shown using the unified diff format.
1066 Differences between files are shown using the unified diff format.
1067
1067
1068 NOTE: diff may generate unexpected results for merges, as it will
1068 NOTE: diff may generate unexpected results for merges, as it will
1069 default to comparing against the working directory's first parent
1069 default to comparing against the working directory's first parent
1070 changeset if no revisions are specified.
1070 changeset if no revisions are specified.
1071
1071
1072 When two revision arguments are given, then changes are shown
1072 When two revision arguments are given, then changes are shown
1073 between those revisions. If only one revision is specified then
1073 between those revisions. If only one revision is specified then
1074 that revision is compared to the working directory, and, when no
1074 that revision is compared to the working directory, and, when no
1075 revisions are specified, the working directory files are compared
1075 revisions are specified, the working directory files are compared
1076 to its parent.
1076 to its parent.
1077
1077
1078 Without the -a/--text option, diff will avoid generating diffs of
1078 Without the -a/--text option, diff will avoid generating diffs of
1079 files it detects as binary. With -a, diff will generate a diff
1079 files it detects as binary. With -a, diff will generate a diff
1080 anyway, probably with undesirable results.
1080 anyway, probably with undesirable results.
1081
1081
1082 Use the -g/--git option to generate diffs in the git extended diff
1082 Use the -g/--git option to generate diffs in the git extended diff
1083 format. For more information, read 'hg help diffs'.
1083 format. For more information, read 'hg help diffs'.
1084 """
1084 """
1085
1085
1086 revs = opts.get('rev')
1086 revs = opts.get('rev')
1087 change = opts.get('change')
1087 change = opts.get('change')
1088 stat = opts.get('stat')
1088 stat = opts.get('stat')
1089
1089
1090 if revs and change:
1090 if revs and change:
1091 msg = _('cannot specify --rev and --change at the same time')
1091 msg = _('cannot specify --rev and --change at the same time')
1092 raise util.Abort(msg)
1092 raise util.Abort(msg)
1093 elif change:
1093 elif change:
1094 node2 = repo.lookup(change)
1094 node2 = repo.lookup(change)
1095 node1 = repo[node2].parents()[0].node()
1095 node1 = repo[node2].parents()[0].node()
1096 else:
1096 else:
1097 node1, node2 = cmdutil.revpair(repo, revs)
1097 node1, node2 = cmdutil.revpair(repo, revs)
1098
1098
1099 if stat:
1099 if stat:
1100 opts['unified'] = '0'
1100 opts['unified'] = '0'
1101 diffopts = patch.diffopts(ui, opts)
1101
1102
1102 m = cmdutil.match(repo, pats, opts)
1103 m = cmdutil.match(repo, pats, opts)
1103 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1104 it = patch.diff(repo, node1, node2, match=m, opts=diffopts)
1104 if stat:
1105 if stat:
1105 width = ui.interactive() and util.termwidth() or 80
1106 width = ui.interactive() and util.termwidth() or 80
1106 ui.write(patch.diffstat(util.iterlines(it), width=width))
1107 ui.write(patch.diffstat(util.iterlines(it), width=width,
1108 git=diffopts.git))
1107 else:
1109 else:
1108 for chunk in it:
1110 for chunk in it:
1109 ui.write(chunk)
1111 ui.write(chunk)
1110
1112
1111 def export(ui, repo, *changesets, **opts):
1113 def export(ui, repo, *changesets, **opts):
1112 """dump the header and diffs for one or more changesets
1114 """dump the header and diffs for one or more changesets
1113
1115
1114 Print the changeset header and diffs for one or more revisions.
1116 Print the changeset header and diffs for one or more revisions.
1115
1117
1116 The information shown in the changeset header is: author,
1118 The information shown in the changeset header is: author,
1117 changeset hash, parent(s) and commit comment.
1119 changeset hash, parent(s) and commit comment.
1118
1120
1119 NOTE: export may generate unexpected diff output for merge
1121 NOTE: export may generate unexpected diff output for merge
1120 changesets, as it will compare the merge changeset against its
1122 changesets, as it will compare the merge changeset against its
1121 first parent only.
1123 first parent only.
1122
1124
1123 Output may be to a file, in which case the name of the file is
1125 Output may be to a file, in which case the name of the file is
1124 given using a format string. The formatting rules are as follows::
1126 given using a format string. The formatting rules are as follows::
1125
1127
1126 %% literal "%" character
1128 %% literal "%" character
1127 %H changeset hash (40 bytes of hexadecimal)
1129 %H changeset hash (40 bytes of hexadecimal)
1128 %N number of patches being generated
1130 %N number of patches being generated
1129 %R changeset revision number
1131 %R changeset revision number
1130 %b basename of the exporting repository
1132 %b basename of the exporting repository
1131 %h short-form changeset hash (12 bytes of hexadecimal)
1133 %h short-form changeset hash (12 bytes of hexadecimal)
1132 %n zero-padded sequence number, starting at 1
1134 %n zero-padded sequence number, starting at 1
1133 %r zero-padded changeset revision number
1135 %r zero-padded changeset revision number
1134
1136
1135 Without the -a/--text option, export will avoid generating diffs
1137 Without the -a/--text option, export will avoid generating diffs
1136 of files it detects as binary. With -a, export will generate a
1138 of files it detects as binary. With -a, export will generate a
1137 diff anyway, probably with undesirable results.
1139 diff anyway, probably with undesirable results.
1138
1140
1139 Use the -g/--git option to generate diffs in the git extended diff
1141 Use the -g/--git option to generate diffs in the git extended diff
1140 format. See 'hg help diffs' for more information.
1142 format. See 'hg help diffs' for more information.
1141
1143
1142 With the --switch-parent option, the diff will be against the
1144 With the --switch-parent option, the diff will be against the
1143 second parent. It can be useful to review a merge.
1145 second parent. It can be useful to review a merge.
1144 """
1146 """
1145 if not changesets:
1147 if not changesets:
1146 raise util.Abort(_("export requires at least one changeset"))
1148 raise util.Abort(_("export requires at least one changeset"))
1147 revs = cmdutil.revrange(repo, changesets)
1149 revs = cmdutil.revrange(repo, changesets)
1148 if len(revs) > 1:
1150 if len(revs) > 1:
1149 ui.note(_('exporting patches:\n'))
1151 ui.note(_('exporting patches:\n'))
1150 else:
1152 else:
1151 ui.note(_('exporting patch:\n'))
1153 ui.note(_('exporting patch:\n'))
1152 patch.export(repo, revs, template=opts.get('output'),
1154 patch.export(repo, revs, template=opts.get('output'),
1153 switch_parent=opts.get('switch_parent'),
1155 switch_parent=opts.get('switch_parent'),
1154 opts=patch.diffopts(ui, opts))
1156 opts=patch.diffopts(ui, opts))
1155
1157
1156 def forget(ui, repo, *pats, **opts):
1158 def forget(ui, repo, *pats, **opts):
1157 """forget the specified files on the next commit
1159 """forget the specified files on the next commit
1158
1160
1159 Mark the specified files so they will no longer be tracked
1161 Mark the specified files so they will no longer be tracked
1160 after the next commit.
1162 after the next commit.
1161
1163
1162 This only removes files from the current branch, not from the
1164 This only removes files from the current branch, not from the
1163 entire project history, and it does not delete them from the
1165 entire project history, and it does not delete them from the
1164 working directory.
1166 working directory.
1165
1167
1166 To undo a forget before the next commit, see hg add.
1168 To undo a forget before the next commit, see hg add.
1167 """
1169 """
1168
1170
1169 if not pats:
1171 if not pats:
1170 raise util.Abort(_('no files specified'))
1172 raise util.Abort(_('no files specified'))
1171
1173
1172 m = cmdutil.match(repo, pats, opts)
1174 m = cmdutil.match(repo, pats, opts)
1173 s = repo.status(match=m, clean=True)
1175 s = repo.status(match=m, clean=True)
1174 forget = sorted(s[0] + s[1] + s[3] + s[6])
1176 forget = sorted(s[0] + s[1] + s[3] + s[6])
1175
1177
1176 for f in m.files():
1178 for f in m.files():
1177 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1179 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1178 ui.warn(_('not removing %s: file is already untracked\n')
1180 ui.warn(_('not removing %s: file is already untracked\n')
1179 % m.rel(f))
1181 % m.rel(f))
1180
1182
1181 for f in forget:
1183 for f in forget:
1182 if ui.verbose or not m.exact(f):
1184 if ui.verbose or not m.exact(f):
1183 ui.status(_('removing %s\n') % m.rel(f))
1185 ui.status(_('removing %s\n') % m.rel(f))
1184
1186
1185 repo.remove(forget, unlink=False)
1187 repo.remove(forget, unlink=False)
1186
1188
1187 def grep(ui, repo, pattern, *pats, **opts):
1189 def grep(ui, repo, pattern, *pats, **opts):
1188 """search for a pattern in specified files and revisions
1190 """search for a pattern in specified files and revisions
1189
1191
1190 Search revisions of files for a regular expression.
1192 Search revisions of files for a regular expression.
1191
1193
1192 This command behaves differently than Unix grep. It only accepts
1194 This command behaves differently than Unix grep. It only accepts
1193 Python/Perl regexps. It searches repository history, not the
1195 Python/Perl regexps. It searches repository history, not the
1194 working directory. It always prints the revision number in which a
1196 working directory. It always prints the revision number in which a
1195 match appears.
1197 match appears.
1196
1198
1197 By default, grep only prints output for the first revision of a
1199 By default, grep only prints output for the first revision of a
1198 file in which it finds a match. To get it to print every revision
1200 file in which it finds a match. To get it to print every revision
1199 that contains a change in match status ("-" for a match that
1201 that contains a change in match status ("-" for a match that
1200 becomes a non-match, or "+" for a non-match that becomes a match),
1202 becomes a non-match, or "+" for a non-match that becomes a match),
1201 use the --all flag.
1203 use the --all flag.
1202 """
1204 """
1203 reflags = 0
1205 reflags = 0
1204 if opts.get('ignore_case'):
1206 if opts.get('ignore_case'):
1205 reflags |= re.I
1207 reflags |= re.I
1206 try:
1208 try:
1207 regexp = re.compile(pattern, reflags)
1209 regexp = re.compile(pattern, reflags)
1208 except Exception, inst:
1210 except Exception, inst:
1209 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1211 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1210 return None
1212 return None
1211 sep, eol = ':', '\n'
1213 sep, eol = ':', '\n'
1212 if opts.get('print0'):
1214 if opts.get('print0'):
1213 sep = eol = '\0'
1215 sep = eol = '\0'
1214
1216
1215 getfile = util.lrucachefunc(repo.file)
1217 getfile = util.lrucachefunc(repo.file)
1216
1218
1217 def matchlines(body):
1219 def matchlines(body):
1218 begin = 0
1220 begin = 0
1219 linenum = 0
1221 linenum = 0
1220 while True:
1222 while True:
1221 match = regexp.search(body, begin)
1223 match = regexp.search(body, begin)
1222 if not match:
1224 if not match:
1223 break
1225 break
1224 mstart, mend = match.span()
1226 mstart, mend = match.span()
1225 linenum += body.count('\n', begin, mstart) + 1
1227 linenum += body.count('\n', begin, mstart) + 1
1226 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1228 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1227 begin = body.find('\n', mend) + 1 or len(body)
1229 begin = body.find('\n', mend) + 1 or len(body)
1228 lend = begin - 1
1230 lend = begin - 1
1229 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1231 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1230
1232
1231 class linestate(object):
1233 class linestate(object):
1232 def __init__(self, line, linenum, colstart, colend):
1234 def __init__(self, line, linenum, colstart, colend):
1233 self.line = line
1235 self.line = line
1234 self.linenum = linenum
1236 self.linenum = linenum
1235 self.colstart = colstart
1237 self.colstart = colstart
1236 self.colend = colend
1238 self.colend = colend
1237
1239
1238 def __hash__(self):
1240 def __hash__(self):
1239 return hash((self.linenum, self.line))
1241 return hash((self.linenum, self.line))
1240
1242
1241 def __eq__(self, other):
1243 def __eq__(self, other):
1242 return self.line == other.line
1244 return self.line == other.line
1243
1245
1244 matches = {}
1246 matches = {}
1245 copies = {}
1247 copies = {}
1246 def grepbody(fn, rev, body):
1248 def grepbody(fn, rev, body):
1247 matches[rev].setdefault(fn, [])
1249 matches[rev].setdefault(fn, [])
1248 m = matches[rev][fn]
1250 m = matches[rev][fn]
1249 for lnum, cstart, cend, line in matchlines(body):
1251 for lnum, cstart, cend, line in matchlines(body):
1250 s = linestate(line, lnum, cstart, cend)
1252 s = linestate(line, lnum, cstart, cend)
1251 m.append(s)
1253 m.append(s)
1252
1254
1253 def difflinestates(a, b):
1255 def difflinestates(a, b):
1254 sm = difflib.SequenceMatcher(None, a, b)
1256 sm = difflib.SequenceMatcher(None, a, b)
1255 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1257 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1256 if tag == 'insert':
1258 if tag == 'insert':
1257 for i in xrange(blo, bhi):
1259 for i in xrange(blo, bhi):
1258 yield ('+', b[i])
1260 yield ('+', b[i])
1259 elif tag == 'delete':
1261 elif tag == 'delete':
1260 for i in xrange(alo, ahi):
1262 for i in xrange(alo, ahi):
1261 yield ('-', a[i])
1263 yield ('-', a[i])
1262 elif tag == 'replace':
1264 elif tag == 'replace':
1263 for i in xrange(alo, ahi):
1265 for i in xrange(alo, ahi):
1264 yield ('-', a[i])
1266 yield ('-', a[i])
1265 for i in xrange(blo, bhi):
1267 for i in xrange(blo, bhi):
1266 yield ('+', b[i])
1268 yield ('+', b[i])
1267
1269
1268 def display(fn, r, pstates, states):
1270 def display(fn, r, pstates, states):
1269 datefunc = ui.quiet and util.shortdate or util.datestr
1271 datefunc = ui.quiet and util.shortdate or util.datestr
1270 found = False
1272 found = False
1271 filerevmatches = {}
1273 filerevmatches = {}
1272 if opts.get('all'):
1274 if opts.get('all'):
1273 iter = difflinestates(pstates, states)
1275 iter = difflinestates(pstates, states)
1274 else:
1276 else:
1275 iter = [('', l) for l in states]
1277 iter = [('', l) for l in states]
1276 for change, l in iter:
1278 for change, l in iter:
1277 cols = [fn, str(r)]
1279 cols = [fn, str(r)]
1278 if opts.get('line_number'):
1280 if opts.get('line_number'):
1279 cols.append(str(l.linenum))
1281 cols.append(str(l.linenum))
1280 if opts.get('all'):
1282 if opts.get('all'):
1281 cols.append(change)
1283 cols.append(change)
1282 if opts.get('user'):
1284 if opts.get('user'):
1283 cols.append(ui.shortuser(get(r).user()))
1285 cols.append(ui.shortuser(get(r).user()))
1284 if opts.get('date'):
1286 if opts.get('date'):
1285 cols.append(datefunc(get(r).date()))
1287 cols.append(datefunc(get(r).date()))
1286 if opts.get('files_with_matches'):
1288 if opts.get('files_with_matches'):
1287 c = (fn, r)
1289 c = (fn, r)
1288 if c in filerevmatches:
1290 if c in filerevmatches:
1289 continue
1291 continue
1290 filerevmatches[c] = 1
1292 filerevmatches[c] = 1
1291 else:
1293 else:
1292 cols.append(l.line)
1294 cols.append(l.line)
1293 ui.write(sep.join(cols), eol)
1295 ui.write(sep.join(cols), eol)
1294 found = True
1296 found = True
1295 return found
1297 return found
1296
1298
1297 skip = {}
1299 skip = {}
1298 revfiles = {}
1300 revfiles = {}
1299 get = util.cachefunc(lambda r: repo[r])
1301 get = util.cachefunc(lambda r: repo[r])
1300 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1302 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1301 found = False
1303 found = False
1302 follow = opts.get('follow')
1304 follow = opts.get('follow')
1303 for st, rev, fns in changeiter:
1305 for st, rev, fns in changeiter:
1304 if st == 'window':
1306 if st == 'window':
1305 matches.clear()
1307 matches.clear()
1306 revfiles.clear()
1308 revfiles.clear()
1307 elif st == 'add':
1309 elif st == 'add':
1308 ctx = get(rev)
1310 ctx = get(rev)
1309 pctx = ctx.parents()[0]
1311 pctx = ctx.parents()[0]
1310 parent = pctx.rev()
1312 parent = pctx.rev()
1311 matches.setdefault(rev, {})
1313 matches.setdefault(rev, {})
1312 matches.setdefault(parent, {})
1314 matches.setdefault(parent, {})
1313 files = revfiles.setdefault(rev, [])
1315 files = revfiles.setdefault(rev, [])
1314 for fn in fns:
1316 for fn in fns:
1315 flog = getfile(fn)
1317 flog = getfile(fn)
1316 try:
1318 try:
1317 fnode = ctx.filenode(fn)
1319 fnode = ctx.filenode(fn)
1318 except error.LookupError:
1320 except error.LookupError:
1319 continue
1321 continue
1320
1322
1321 copied = flog.renamed(fnode)
1323 copied = flog.renamed(fnode)
1322 copy = follow and copied and copied[0]
1324 copy = follow and copied and copied[0]
1323 if copy:
1325 if copy:
1324 copies.setdefault(rev, {})[fn] = copy
1326 copies.setdefault(rev, {})[fn] = copy
1325 if fn in skip:
1327 if fn in skip:
1326 if copy:
1328 if copy:
1327 skip[copy] = True
1329 skip[copy] = True
1328 continue
1330 continue
1329 files.append(fn)
1331 files.append(fn)
1330
1332
1331 if fn not in matches[rev]:
1333 if fn not in matches[rev]:
1332 grepbody(fn, rev, flog.read(fnode))
1334 grepbody(fn, rev, flog.read(fnode))
1333
1335
1334 pfn = copy or fn
1336 pfn = copy or fn
1335 if pfn not in matches[parent]:
1337 if pfn not in matches[parent]:
1336 try:
1338 try:
1337 fnode = pctx.filenode(pfn)
1339 fnode = pctx.filenode(pfn)
1338 grepbody(pfn, parent, flog.read(fnode))
1340 grepbody(pfn, parent, flog.read(fnode))
1339 except error.LookupError:
1341 except error.LookupError:
1340 pass
1342 pass
1341 elif st == 'iter':
1343 elif st == 'iter':
1342 parent = get(rev).parents()[0].rev()
1344 parent = get(rev).parents()[0].rev()
1343 for fn in sorted(revfiles.get(rev, [])):
1345 for fn in sorted(revfiles.get(rev, [])):
1344 states = matches[rev][fn]
1346 states = matches[rev][fn]
1345 copy = copies.get(rev, {}).get(fn)
1347 copy = copies.get(rev, {}).get(fn)
1346 if fn in skip:
1348 if fn in skip:
1347 if copy:
1349 if copy:
1348 skip[copy] = True
1350 skip[copy] = True
1349 continue
1351 continue
1350 pstates = matches.get(parent, {}).get(copy or fn, [])
1352 pstates = matches.get(parent, {}).get(copy or fn, [])
1351 if pstates or states:
1353 if pstates or states:
1352 r = display(fn, rev, pstates, states)
1354 r = display(fn, rev, pstates, states)
1353 found = found or r
1355 found = found or r
1354 if r and not opts.get('all'):
1356 if r and not opts.get('all'):
1355 skip[fn] = True
1357 skip[fn] = True
1356 if copy:
1358 if copy:
1357 skip[copy] = True
1359 skip[copy] = True
1358
1360
1359 def heads(ui, repo, *branchrevs, **opts):
1361 def heads(ui, repo, *branchrevs, **opts):
1360 """show current repository heads or show branch heads
1362 """show current repository heads or show branch heads
1361
1363
1362 With no arguments, show all repository head changesets.
1364 With no arguments, show all repository head changesets.
1363
1365
1364 Repository "heads" are changesets with no child changesets. They are
1366 Repository "heads" are changesets with no child changesets. They are
1365 where development generally takes place and are the usual targets
1367 where development generally takes place and are the usual targets
1366 for update and merge operations.
1368 for update and merge operations.
1367
1369
1368 If one or more REV is given, the "branch heads" will be shown for
1370 If one or more REV is given, the "branch heads" will be shown for
1369 the named branch associated with the specified changeset(s).
1371 the named branch associated with the specified changeset(s).
1370
1372
1371 Branch heads are changesets on a named branch with no descendants on
1373 Branch heads are changesets on a named branch with no descendants on
1372 the same branch. A branch head could be a "true" (repository) head,
1374 the same branch. A branch head could be a "true" (repository) head,
1373 or it could be the last changeset on that branch before it was
1375 or it could be the last changeset on that branch before it was
1374 merged into another branch, or it could be the last changeset on the
1376 merged into another branch, or it could be the last changeset on the
1375 branch before a new branch was created. If none of the branch heads
1377 branch before a new branch was created. If none of the branch heads
1376 are true heads, the branch is considered inactive.
1378 are true heads, the branch is considered inactive.
1377
1379
1378 If -c/--closed is specified, also show branch heads marked closed
1380 If -c/--closed is specified, also show branch heads marked closed
1379 (see hg commit --close-branch).
1381 (see hg commit --close-branch).
1380
1382
1381 If STARTREV is specified, only those heads that are descendants of
1383 If STARTREV is specified, only those heads that are descendants of
1382 STARTREV will be displayed.
1384 STARTREV will be displayed.
1383 """
1385 """
1384 if opts.get('rev'):
1386 if opts.get('rev'):
1385 start = repo.lookup(opts['rev'])
1387 start = repo.lookup(opts['rev'])
1386 else:
1388 else:
1387 start = None
1389 start = None
1388 closed = opts.get('closed')
1390 closed = opts.get('closed')
1389 hideinactive, _heads = opts.get('active'), None
1391 hideinactive, _heads = opts.get('active'), None
1390 if not branchrevs:
1392 if not branchrevs:
1391 if closed:
1393 if closed:
1392 raise error.Abort(_('you must specify a branch to use --closed'))
1394 raise error.Abort(_('you must specify a branch to use --closed'))
1393 # Assume we're looking repo-wide heads if no revs were specified.
1395 # Assume we're looking repo-wide heads if no revs were specified.
1394 heads = repo.heads(start)
1396 heads = repo.heads(start)
1395 else:
1397 else:
1396 if hideinactive:
1398 if hideinactive:
1397 _heads = repo.heads(start)
1399 _heads = repo.heads(start)
1398 heads = []
1400 heads = []
1399 visitedset = set()
1401 visitedset = set()
1400 for branchrev in branchrevs:
1402 for branchrev in branchrevs:
1401 branch = repo[branchrev].branch()
1403 branch = repo[branchrev].branch()
1402 if branch in visitedset:
1404 if branch in visitedset:
1403 continue
1405 continue
1404 visitedset.add(branch)
1406 visitedset.add(branch)
1405 bheads = repo.branchheads(branch, start, closed=closed)
1407 bheads = repo.branchheads(branch, start, closed=closed)
1406 if not bheads:
1408 if not bheads:
1407 if not opts.get('rev'):
1409 if not opts.get('rev'):
1408 ui.warn(_("no open branch heads on branch %s\n") % branch)
1410 ui.warn(_("no open branch heads on branch %s\n") % branch)
1409 elif branch != branchrev:
1411 elif branch != branchrev:
1410 ui.warn(_("no changes on branch %s containing %s are "
1412 ui.warn(_("no changes on branch %s containing %s are "
1411 "reachable from %s\n")
1413 "reachable from %s\n")
1412 % (branch, branchrev, opts.get('rev')))
1414 % (branch, branchrev, opts.get('rev')))
1413 else:
1415 else:
1414 ui.warn(_("no changes on branch %s are reachable from %s\n")
1416 ui.warn(_("no changes on branch %s are reachable from %s\n")
1415 % (branch, opts.get('rev')))
1417 % (branch, opts.get('rev')))
1416 if hideinactive:
1418 if hideinactive:
1417 bheads = [bhead for bhead in bheads if bhead in _heads]
1419 bheads = [bhead for bhead in bheads if bhead in _heads]
1418 heads.extend(bheads)
1420 heads.extend(bheads)
1419 if not heads:
1421 if not heads:
1420 return 1
1422 return 1
1421 displayer = cmdutil.show_changeset(ui, repo, opts)
1423 displayer = cmdutil.show_changeset(ui, repo, opts)
1422 for n in heads:
1424 for n in heads:
1423 displayer.show(repo[n])
1425 displayer.show(repo[n])
1424
1426
1425 def help_(ui, name=None, with_version=False):
1427 def help_(ui, name=None, with_version=False):
1426 """show help for a given topic or a help overview
1428 """show help for a given topic or a help overview
1427
1429
1428 With no arguments, print a list of commands with short help messages.
1430 With no arguments, print a list of commands with short help messages.
1429
1431
1430 Given a topic, extension, or command name, print help for that
1432 Given a topic, extension, or command name, print help for that
1431 topic."""
1433 topic."""
1432 option_lists = []
1434 option_lists = []
1433 textwidth = util.termwidth() - 2
1435 textwidth = util.termwidth() - 2
1434
1436
1435 def addglobalopts(aliases):
1437 def addglobalopts(aliases):
1436 if ui.verbose:
1438 if ui.verbose:
1437 option_lists.append((_("global options:"), globalopts))
1439 option_lists.append((_("global options:"), globalopts))
1438 if name == 'shortlist':
1440 if name == 'shortlist':
1439 option_lists.append((_('use "hg help" for the full list '
1441 option_lists.append((_('use "hg help" for the full list '
1440 'of commands'), ()))
1442 'of commands'), ()))
1441 else:
1443 else:
1442 if name == 'shortlist':
1444 if name == 'shortlist':
1443 msg = _('use "hg help" for the full list of commands '
1445 msg = _('use "hg help" for the full list of commands '
1444 'or "hg -v" for details')
1446 'or "hg -v" for details')
1445 elif aliases:
1447 elif aliases:
1446 msg = _('use "hg -v help%s" to show aliases and '
1448 msg = _('use "hg -v help%s" to show aliases and '
1447 'global options') % (name and " " + name or "")
1449 'global options') % (name and " " + name or "")
1448 else:
1450 else:
1449 msg = _('use "hg -v help %s" to show global options') % name
1451 msg = _('use "hg -v help %s" to show global options') % name
1450 option_lists.append((msg, ()))
1452 option_lists.append((msg, ()))
1451
1453
1452 def helpcmd(name):
1454 def helpcmd(name):
1453 if with_version:
1455 if with_version:
1454 version_(ui)
1456 version_(ui)
1455 ui.write('\n')
1457 ui.write('\n')
1456
1458
1457 try:
1459 try:
1458 aliases, i = cmdutil.findcmd(name, table, False)
1460 aliases, i = cmdutil.findcmd(name, table, False)
1459 except error.AmbiguousCommand, inst:
1461 except error.AmbiguousCommand, inst:
1460 # py3k fix: except vars can't be used outside the scope of the
1462 # py3k fix: except vars can't be used outside the scope of the
1461 # except block, nor can be used inside a lambda. python issue4617
1463 # except block, nor can be used inside a lambda. python issue4617
1462 prefix = inst.args[0]
1464 prefix = inst.args[0]
1463 select = lambda c: c.lstrip('^').startswith(prefix)
1465 select = lambda c: c.lstrip('^').startswith(prefix)
1464 helplist(_('list of commands:\n\n'), select)
1466 helplist(_('list of commands:\n\n'), select)
1465 return
1467 return
1466
1468
1467 # synopsis
1469 # synopsis
1468 if len(i) > 2:
1470 if len(i) > 2:
1469 if i[2].startswith('hg'):
1471 if i[2].startswith('hg'):
1470 ui.write("%s\n" % i[2])
1472 ui.write("%s\n" % i[2])
1471 else:
1473 else:
1472 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1474 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1473 else:
1475 else:
1474 ui.write('hg %s\n' % aliases[0])
1476 ui.write('hg %s\n' % aliases[0])
1475
1477
1476 # aliases
1478 # aliases
1477 if not ui.quiet and len(aliases) > 1:
1479 if not ui.quiet and len(aliases) > 1:
1478 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1480 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1479
1481
1480 # description
1482 # description
1481 doc = gettext(i[0].__doc__)
1483 doc = gettext(i[0].__doc__)
1482 if not doc:
1484 if not doc:
1483 doc = _("(no help text available)")
1485 doc = _("(no help text available)")
1484 if ui.quiet:
1486 if ui.quiet:
1485 doc = doc.splitlines()[0]
1487 doc = doc.splitlines()[0]
1486 ui.write("\n%s\n" % minirst.format(doc, textwidth))
1488 ui.write("\n%s\n" % minirst.format(doc, textwidth))
1487
1489
1488 if not ui.quiet:
1490 if not ui.quiet:
1489 # options
1491 # options
1490 if i[1]:
1492 if i[1]:
1491 option_lists.append((_("options:\n"), i[1]))
1493 option_lists.append((_("options:\n"), i[1]))
1492
1494
1493 addglobalopts(False)
1495 addglobalopts(False)
1494
1496
1495 def helplist(header, select=None):
1497 def helplist(header, select=None):
1496 h = {}
1498 h = {}
1497 cmds = {}
1499 cmds = {}
1498 for c, e in table.iteritems():
1500 for c, e in table.iteritems():
1499 f = c.split("|", 1)[0]
1501 f = c.split("|", 1)[0]
1500 if select and not select(f):
1502 if select and not select(f):
1501 continue
1503 continue
1502 if (not select and name != 'shortlist' and
1504 if (not select and name != 'shortlist' and
1503 e[0].__module__ != __name__):
1505 e[0].__module__ != __name__):
1504 continue
1506 continue
1505 if name == "shortlist" and not f.startswith("^"):
1507 if name == "shortlist" and not f.startswith("^"):
1506 continue
1508 continue
1507 f = f.lstrip("^")
1509 f = f.lstrip("^")
1508 if not ui.debugflag and f.startswith("debug"):
1510 if not ui.debugflag and f.startswith("debug"):
1509 continue
1511 continue
1510 doc = e[0].__doc__
1512 doc = e[0].__doc__
1511 if doc and 'DEPRECATED' in doc and not ui.verbose:
1513 if doc and 'DEPRECATED' in doc and not ui.verbose:
1512 continue
1514 continue
1513 doc = gettext(doc)
1515 doc = gettext(doc)
1514 if not doc:
1516 if not doc:
1515 doc = _("(no help text available)")
1517 doc = _("(no help text available)")
1516 h[f] = doc.splitlines()[0].rstrip()
1518 h[f] = doc.splitlines()[0].rstrip()
1517 cmds[f] = c.lstrip("^")
1519 cmds[f] = c.lstrip("^")
1518
1520
1519 if not h:
1521 if not h:
1520 ui.status(_('no commands defined\n'))
1522 ui.status(_('no commands defined\n'))
1521 return
1523 return
1522
1524
1523 ui.status(header)
1525 ui.status(header)
1524 fns = sorted(h)
1526 fns = sorted(h)
1525 m = max(map(len, fns))
1527 m = max(map(len, fns))
1526 for f in fns:
1528 for f in fns:
1527 if ui.verbose:
1529 if ui.verbose:
1528 commands = cmds[f].replace("|",", ")
1530 commands = cmds[f].replace("|",", ")
1529 ui.write(" %s:\n %s\n"%(commands, h[f]))
1531 ui.write(" %s:\n %s\n"%(commands, h[f]))
1530 else:
1532 else:
1531 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1533 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1532
1534
1533 if name != 'shortlist':
1535 if name != 'shortlist':
1534 exts, maxlength = extensions.enabled()
1536 exts, maxlength = extensions.enabled()
1535 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1537 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1536 if text:
1538 if text:
1537 ui.write("\n%s\n" % minirst.format(text, textwidth))
1539 ui.write("\n%s\n" % minirst.format(text, textwidth))
1538
1540
1539 if not ui.quiet:
1541 if not ui.quiet:
1540 addglobalopts(True)
1542 addglobalopts(True)
1541
1543
1542 def helptopic(name):
1544 def helptopic(name):
1543 for names, header, doc in help.helptable:
1545 for names, header, doc in help.helptable:
1544 if name in names:
1546 if name in names:
1545 break
1547 break
1546 else:
1548 else:
1547 raise error.UnknownCommand(name)
1549 raise error.UnknownCommand(name)
1548
1550
1549 # description
1551 # description
1550 if not doc:
1552 if not doc:
1551 doc = _("(no help text available)")
1553 doc = _("(no help text available)")
1552 if hasattr(doc, '__call__'):
1554 if hasattr(doc, '__call__'):
1553 doc = doc()
1555 doc = doc()
1554
1556
1555 ui.write("%s\n\n" % header)
1557 ui.write("%s\n\n" % header)
1556 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1558 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
1557
1559
1558 def helpext(name):
1560 def helpext(name):
1559 try:
1561 try:
1560 mod = extensions.find(name)
1562 mod = extensions.find(name)
1561 except KeyError:
1563 except KeyError:
1562 raise error.UnknownCommand(name)
1564 raise error.UnknownCommand(name)
1563
1565
1564 doc = gettext(mod.__doc__) or _('no help text available')
1566 doc = gettext(mod.__doc__) or _('no help text available')
1565 if '\n' not in doc:
1567 if '\n' not in doc:
1566 head, tail = doc, ""
1568 head, tail = doc, ""
1567 else:
1569 else:
1568 head, tail = doc.split('\n', 1)
1570 head, tail = doc.split('\n', 1)
1569 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1571 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1570 if tail:
1572 if tail:
1571 ui.write(minirst.format(tail, textwidth))
1573 ui.write(minirst.format(tail, textwidth))
1572 ui.status('\n\n')
1574 ui.status('\n\n')
1573
1575
1574 try:
1576 try:
1575 ct = mod.cmdtable
1577 ct = mod.cmdtable
1576 except AttributeError:
1578 except AttributeError:
1577 ct = {}
1579 ct = {}
1578
1580
1579 modcmds = set([c.split('|', 1)[0] for c in ct])
1581 modcmds = set([c.split('|', 1)[0] for c in ct])
1580 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1582 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1581
1583
1582 if name and name != 'shortlist':
1584 if name and name != 'shortlist':
1583 i = None
1585 i = None
1584 for f in (helptopic, helpcmd, helpext):
1586 for f in (helptopic, helpcmd, helpext):
1585 try:
1587 try:
1586 f(name)
1588 f(name)
1587 i = None
1589 i = None
1588 break
1590 break
1589 except error.UnknownCommand, inst:
1591 except error.UnknownCommand, inst:
1590 i = inst
1592 i = inst
1591 if i:
1593 if i:
1592 raise i
1594 raise i
1593
1595
1594 else:
1596 else:
1595 # program name
1597 # program name
1596 if ui.verbose or with_version:
1598 if ui.verbose or with_version:
1597 version_(ui)
1599 version_(ui)
1598 else:
1600 else:
1599 ui.status(_("Mercurial Distributed SCM\n"))
1601 ui.status(_("Mercurial Distributed SCM\n"))
1600 ui.status('\n')
1602 ui.status('\n')
1601
1603
1602 # list of commands
1604 # list of commands
1603 if name == "shortlist":
1605 if name == "shortlist":
1604 header = _('basic commands:\n\n')
1606 header = _('basic commands:\n\n')
1605 else:
1607 else:
1606 header = _('list of commands:\n\n')
1608 header = _('list of commands:\n\n')
1607
1609
1608 helplist(header)
1610 helplist(header)
1609
1611
1610 # list all option lists
1612 # list all option lists
1611 opt_output = []
1613 opt_output = []
1612 for title, options in option_lists:
1614 for title, options in option_lists:
1613 opt_output.append(("\n%s" % title, None))
1615 opt_output.append(("\n%s" % title, None))
1614 for shortopt, longopt, default, desc in options:
1616 for shortopt, longopt, default, desc in options:
1615 if _("DEPRECATED") in desc and not ui.verbose: continue
1617 if _("DEPRECATED") in desc and not ui.verbose: continue
1616 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1618 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1617 longopt and " --%s" % longopt),
1619 longopt and " --%s" % longopt),
1618 "%s%s" % (desc,
1620 "%s%s" % (desc,
1619 default
1621 default
1620 and _(" (default: %s)") % default
1622 and _(" (default: %s)") % default
1621 or "")))
1623 or "")))
1622
1624
1623 if not name:
1625 if not name:
1624 ui.write(_("\nadditional help topics:\n\n"))
1626 ui.write(_("\nadditional help topics:\n\n"))
1625 topics = []
1627 topics = []
1626 for names, header, doc in help.helptable:
1628 for names, header, doc in help.helptable:
1627 topics.append((sorted(names, key=len, reverse=True)[0], header))
1629 topics.append((sorted(names, key=len, reverse=True)[0], header))
1628 topics_len = max([len(s[0]) for s in topics])
1630 topics_len = max([len(s[0]) for s in topics])
1629 for t, desc in topics:
1631 for t, desc in topics:
1630 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1632 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1631
1633
1632 if opt_output:
1634 if opt_output:
1633 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1635 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1634 for first, second in opt_output:
1636 for first, second in opt_output:
1635 if second:
1637 if second:
1636 second = util.wrap(second, opts_len + 3)
1638 second = util.wrap(second, opts_len + 3)
1637 ui.write(" %-*s %s\n" % (opts_len, first, second))
1639 ui.write(" %-*s %s\n" % (opts_len, first, second))
1638 else:
1640 else:
1639 ui.write("%s\n" % first)
1641 ui.write("%s\n" % first)
1640
1642
1641 def identify(ui, repo, source=None,
1643 def identify(ui, repo, source=None,
1642 rev=None, num=None, id=None, branch=None, tags=None):
1644 rev=None, num=None, id=None, branch=None, tags=None):
1643 """identify the working copy or specified revision
1645 """identify the working copy or specified revision
1644
1646
1645 With no revision, print a summary of the current state of the
1647 With no revision, print a summary of the current state of the
1646 repository.
1648 repository.
1647
1649
1648 Specifying a path to a repository root or Mercurial bundle will
1650 Specifying a path to a repository root or Mercurial bundle will
1649 cause lookup to operate on that repository/bundle.
1651 cause lookup to operate on that repository/bundle.
1650
1652
1651 This summary identifies the repository state using one or two
1653 This summary identifies the repository state using one or two
1652 parent hash identifiers, followed by a "+" if there are
1654 parent hash identifiers, followed by a "+" if there are
1653 uncommitted changes in the working directory, a list of tags for
1655 uncommitted changes in the working directory, a list of tags for
1654 this revision and a branch name for non-default branches.
1656 this revision and a branch name for non-default branches.
1655 """
1657 """
1656
1658
1657 if not repo and not source:
1659 if not repo and not source:
1658 raise util.Abort(_("There is no Mercurial repository here "
1660 raise util.Abort(_("There is no Mercurial repository here "
1659 "(.hg not found)"))
1661 "(.hg not found)"))
1660
1662
1661 hexfunc = ui.debugflag and hex or short
1663 hexfunc = ui.debugflag and hex or short
1662 default = not (num or id or branch or tags)
1664 default = not (num or id or branch or tags)
1663 output = []
1665 output = []
1664
1666
1665 revs = []
1667 revs = []
1666 if source:
1668 if source:
1667 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1669 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1668 repo = hg.repository(ui, source)
1670 repo = hg.repository(ui, source)
1669
1671
1670 if not repo.local():
1672 if not repo.local():
1671 if not rev and revs:
1673 if not rev and revs:
1672 rev = revs[0]
1674 rev = revs[0]
1673 if not rev:
1675 if not rev:
1674 rev = "tip"
1676 rev = "tip"
1675 if num or branch or tags:
1677 if num or branch or tags:
1676 raise util.Abort(
1678 raise util.Abort(
1677 "can't query remote revision number, branch, or tags")
1679 "can't query remote revision number, branch, or tags")
1678 output = [hexfunc(repo.lookup(rev))]
1680 output = [hexfunc(repo.lookup(rev))]
1679 elif not rev:
1681 elif not rev:
1680 ctx = repo[None]
1682 ctx = repo[None]
1681 parents = ctx.parents()
1683 parents = ctx.parents()
1682 changed = False
1684 changed = False
1683 if default or id or num:
1685 if default or id or num:
1684 changed = ctx.files() + ctx.deleted()
1686 changed = ctx.files() + ctx.deleted()
1685 if default or id:
1687 if default or id:
1686 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1688 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1687 (changed) and "+" or "")]
1689 (changed) and "+" or "")]
1688 if num:
1690 if num:
1689 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1691 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1690 (changed) and "+" or ""))
1692 (changed) and "+" or ""))
1691 else:
1693 else:
1692 ctx = repo[rev]
1694 ctx = repo[rev]
1693 if default or id:
1695 if default or id:
1694 output = [hexfunc(ctx.node())]
1696 output = [hexfunc(ctx.node())]
1695 if num:
1697 if num:
1696 output.append(str(ctx.rev()))
1698 output.append(str(ctx.rev()))
1697
1699
1698 if repo.local() and default and not ui.quiet:
1700 if repo.local() and default and not ui.quiet:
1699 b = encoding.tolocal(ctx.branch())
1701 b = encoding.tolocal(ctx.branch())
1700 if b != 'default':
1702 if b != 'default':
1701 output.append("(%s)" % b)
1703 output.append("(%s)" % b)
1702
1704
1703 # multiple tags for a single parent separated by '/'
1705 # multiple tags for a single parent separated by '/'
1704 t = "/".join(ctx.tags())
1706 t = "/".join(ctx.tags())
1705 if t:
1707 if t:
1706 output.append(t)
1708 output.append(t)
1707
1709
1708 if branch:
1710 if branch:
1709 output.append(encoding.tolocal(ctx.branch()))
1711 output.append(encoding.tolocal(ctx.branch()))
1710
1712
1711 if tags:
1713 if tags:
1712 output.extend(ctx.tags())
1714 output.extend(ctx.tags())
1713
1715
1714 ui.write("%s\n" % ' '.join(output))
1716 ui.write("%s\n" % ' '.join(output))
1715
1717
1716 def import_(ui, repo, patch1, *patches, **opts):
1718 def import_(ui, repo, patch1, *patches, **opts):
1717 """import an ordered set of patches
1719 """import an ordered set of patches
1718
1720
1719 Import a list of patches and commit them individually.
1721 Import a list of patches and commit them individually.
1720
1722
1721 If there are outstanding changes in the working directory, import
1723 If there are outstanding changes in the working directory, import
1722 will abort unless given the -f/--force flag.
1724 will abort unless given the -f/--force flag.
1723
1725
1724 You can import a patch straight from a mail message. Even patches
1726 You can import a patch straight from a mail message. Even patches
1725 as attachments work (to use the body part, it must have type
1727 as attachments work (to use the body part, it must have type
1726 text/plain or text/x-patch). From and Subject headers of email
1728 text/plain or text/x-patch). From and Subject headers of email
1727 message are used as default committer and commit message. All
1729 message are used as default committer and commit message. All
1728 text/plain body parts before first diff are added to commit
1730 text/plain body parts before first diff are added to commit
1729 message.
1731 message.
1730
1732
1731 If the imported patch was generated by hg export, user and
1733 If the imported patch was generated by hg export, user and
1732 description from patch override values from message headers and
1734 description from patch override values from message headers and
1733 body. Values given on command line with -m/--message and -u/--user
1735 body. Values given on command line with -m/--message and -u/--user
1734 override these.
1736 override these.
1735
1737
1736 If --exact is specified, import will set the working directory to
1738 If --exact is specified, import will set the working directory to
1737 the parent of each patch before applying it, and will abort if the
1739 the parent of each patch before applying it, and will abort if the
1738 resulting changeset has a different ID than the one recorded in
1740 resulting changeset has a different ID than the one recorded in
1739 the patch. This may happen due to character set problems or other
1741 the patch. This may happen due to character set problems or other
1740 deficiencies in the text patch format.
1742 deficiencies in the text patch format.
1741
1743
1742 With -s/--similarity, hg will attempt to discover renames and
1744 With -s/--similarity, hg will attempt to discover renames and
1743 copies in the patch in the same way as 'addremove'.
1745 copies in the patch in the same way as 'addremove'.
1744
1746
1745 To read a patch from standard input, use "-" as the patch name. If
1747 To read a patch from standard input, use "-" as the patch name. If
1746 a URL is specified, the patch will be downloaded from it.
1748 a URL is specified, the patch will be downloaded from it.
1747 See 'hg help dates' for a list of formats valid for -d/--date.
1749 See 'hg help dates' for a list of formats valid for -d/--date.
1748 """
1750 """
1749 patches = (patch1,) + patches
1751 patches = (patch1,) + patches
1750
1752
1751 date = opts.get('date')
1753 date = opts.get('date')
1752 if date:
1754 if date:
1753 opts['date'] = util.parsedate(date)
1755 opts['date'] = util.parsedate(date)
1754
1756
1755 try:
1757 try:
1756 sim = float(opts.get('similarity') or 0)
1758 sim = float(opts.get('similarity') or 0)
1757 except ValueError:
1759 except ValueError:
1758 raise util.Abort(_('similarity must be a number'))
1760 raise util.Abort(_('similarity must be a number'))
1759 if sim < 0 or sim > 100:
1761 if sim < 0 or sim > 100:
1760 raise util.Abort(_('similarity must be between 0 and 100'))
1762 raise util.Abort(_('similarity must be between 0 and 100'))
1761
1763
1762 if opts.get('exact') or not opts.get('force'):
1764 if opts.get('exact') or not opts.get('force'):
1763 cmdutil.bail_if_changed(repo)
1765 cmdutil.bail_if_changed(repo)
1764
1766
1765 d = opts["base"]
1767 d = opts["base"]
1766 strip = opts["strip"]
1768 strip = opts["strip"]
1767 wlock = lock = None
1769 wlock = lock = None
1768 try:
1770 try:
1769 wlock = repo.wlock()
1771 wlock = repo.wlock()
1770 lock = repo.lock()
1772 lock = repo.lock()
1771 for p in patches:
1773 for p in patches:
1772 pf = os.path.join(d, p)
1774 pf = os.path.join(d, p)
1773
1775
1774 if pf == '-':
1776 if pf == '-':
1775 ui.status(_("applying patch from stdin\n"))
1777 ui.status(_("applying patch from stdin\n"))
1776 pf = sys.stdin
1778 pf = sys.stdin
1777 else:
1779 else:
1778 ui.status(_("applying %s\n") % p)
1780 ui.status(_("applying %s\n") % p)
1779 pf = url.open(ui, pf)
1781 pf = url.open(ui, pf)
1780 data = patch.extract(ui, pf)
1782 data = patch.extract(ui, pf)
1781 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1783 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1782
1784
1783 if tmpname is None:
1785 if tmpname is None:
1784 raise util.Abort(_('no diffs found'))
1786 raise util.Abort(_('no diffs found'))
1785
1787
1786 try:
1788 try:
1787 cmdline_message = cmdutil.logmessage(opts)
1789 cmdline_message = cmdutil.logmessage(opts)
1788 if cmdline_message:
1790 if cmdline_message:
1789 # pickup the cmdline msg
1791 # pickup the cmdline msg
1790 message = cmdline_message
1792 message = cmdline_message
1791 elif message:
1793 elif message:
1792 # pickup the patch msg
1794 # pickup the patch msg
1793 message = message.strip()
1795 message = message.strip()
1794 else:
1796 else:
1795 # launch the editor
1797 # launch the editor
1796 message = None
1798 message = None
1797 ui.debug('message:\n%s\n' % message)
1799 ui.debug('message:\n%s\n' % message)
1798
1800
1799 wp = repo.parents()
1801 wp = repo.parents()
1800 if opts.get('exact'):
1802 if opts.get('exact'):
1801 if not nodeid or not p1:
1803 if not nodeid or not p1:
1802 raise util.Abort(_('not a Mercurial patch'))
1804 raise util.Abort(_('not a Mercurial patch'))
1803 p1 = repo.lookup(p1)
1805 p1 = repo.lookup(p1)
1804 p2 = repo.lookup(p2 or hex(nullid))
1806 p2 = repo.lookup(p2 or hex(nullid))
1805
1807
1806 if p1 != wp[0].node():
1808 if p1 != wp[0].node():
1807 hg.clean(repo, p1)
1809 hg.clean(repo, p1)
1808 repo.dirstate.setparents(p1, p2)
1810 repo.dirstate.setparents(p1, p2)
1809 elif p2:
1811 elif p2:
1810 try:
1812 try:
1811 p1 = repo.lookup(p1)
1813 p1 = repo.lookup(p1)
1812 p2 = repo.lookup(p2)
1814 p2 = repo.lookup(p2)
1813 if p1 == wp[0].node():
1815 if p1 == wp[0].node():
1814 repo.dirstate.setparents(p1, p2)
1816 repo.dirstate.setparents(p1, p2)
1815 except error.RepoError:
1817 except error.RepoError:
1816 pass
1818 pass
1817 if opts.get('exact') or opts.get('import_branch'):
1819 if opts.get('exact') or opts.get('import_branch'):
1818 repo.dirstate.setbranch(branch or 'default')
1820 repo.dirstate.setbranch(branch or 'default')
1819
1821
1820 files = {}
1822 files = {}
1821 try:
1823 try:
1822 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1824 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1823 files=files, eolmode=None)
1825 files=files, eolmode=None)
1824 finally:
1826 finally:
1825 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1827 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1826 if not opts.get('no_commit'):
1828 if not opts.get('no_commit'):
1827 m = cmdutil.matchfiles(repo, files or [])
1829 m = cmdutil.matchfiles(repo, files or [])
1828 n = repo.commit(message, opts.get('user') or user,
1830 n = repo.commit(message, opts.get('user') or user,
1829 opts.get('date') or date, match=m,
1831 opts.get('date') or date, match=m,
1830 editor=cmdutil.commiteditor)
1832 editor=cmdutil.commiteditor)
1831 if opts.get('exact'):
1833 if opts.get('exact'):
1832 if hex(n) != nodeid:
1834 if hex(n) != nodeid:
1833 repo.rollback()
1835 repo.rollback()
1834 raise util.Abort(_('patch is damaged'
1836 raise util.Abort(_('patch is damaged'
1835 ' or loses information'))
1837 ' or loses information'))
1836 # Force a dirstate write so that the next transaction
1838 # Force a dirstate write so that the next transaction
1837 # backups an up-do-date file.
1839 # backups an up-do-date file.
1838 repo.dirstate.write()
1840 repo.dirstate.write()
1839 finally:
1841 finally:
1840 os.unlink(tmpname)
1842 os.unlink(tmpname)
1841 finally:
1843 finally:
1842 release(lock, wlock)
1844 release(lock, wlock)
1843
1845
1844 def incoming(ui, repo, source="default", **opts):
1846 def incoming(ui, repo, source="default", **opts):
1845 """show new changesets found in source
1847 """show new changesets found in source
1846
1848
1847 Show new changesets found in the specified path/URL or the default
1849 Show new changesets found in the specified path/URL or the default
1848 pull location. These are the changesets that would have been pulled
1850 pull location. These are the changesets that would have been pulled
1849 if a pull at the time you issued this command.
1851 if a pull at the time you issued this command.
1850
1852
1851 For remote repository, using --bundle avoids downloading the
1853 For remote repository, using --bundle avoids downloading the
1852 changesets twice if the incoming is followed by a pull.
1854 changesets twice if the incoming is followed by a pull.
1853
1855
1854 See pull for valid source format details.
1856 See pull for valid source format details.
1855 """
1857 """
1856 limit = cmdutil.loglimit(opts)
1858 limit = cmdutil.loglimit(opts)
1857 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1859 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1858 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1860 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1859 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1861 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1860 if revs:
1862 if revs:
1861 revs = [other.lookup(rev) for rev in revs]
1863 revs = [other.lookup(rev) for rev in revs]
1862 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1864 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1863 force=opts["force"])
1865 force=opts["force"])
1864 if not incoming:
1866 if not incoming:
1865 try:
1867 try:
1866 os.unlink(opts["bundle"])
1868 os.unlink(opts["bundle"])
1867 except:
1869 except:
1868 pass
1870 pass
1869 ui.status(_("no changes found\n"))
1871 ui.status(_("no changes found\n"))
1870 return 1
1872 return 1
1871
1873
1872 cleanup = None
1874 cleanup = None
1873 try:
1875 try:
1874 fname = opts["bundle"]
1876 fname = opts["bundle"]
1875 if fname or not other.local():
1877 if fname or not other.local():
1876 # create a bundle (uncompressed if other repo is not local)
1878 # create a bundle (uncompressed if other repo is not local)
1877
1879
1878 if revs is None and other.capable('changegroupsubset'):
1880 if revs is None and other.capable('changegroupsubset'):
1879 revs = rheads
1881 revs = rheads
1880
1882
1881 if revs is None:
1883 if revs is None:
1882 cg = other.changegroup(incoming, "incoming")
1884 cg = other.changegroup(incoming, "incoming")
1883 else:
1885 else:
1884 cg = other.changegroupsubset(incoming, revs, 'incoming')
1886 cg = other.changegroupsubset(incoming, revs, 'incoming')
1885 bundletype = other.local() and "HG10BZ" or "HG10UN"
1887 bundletype = other.local() and "HG10BZ" or "HG10UN"
1886 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1888 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1887 # keep written bundle?
1889 # keep written bundle?
1888 if opts["bundle"]:
1890 if opts["bundle"]:
1889 cleanup = None
1891 cleanup = None
1890 if not other.local():
1892 if not other.local():
1891 # use the created uncompressed bundlerepo
1893 # use the created uncompressed bundlerepo
1892 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1894 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1893
1895
1894 o = other.changelog.nodesbetween(incoming, revs)[0]
1896 o = other.changelog.nodesbetween(incoming, revs)[0]
1895 if opts.get('newest_first'):
1897 if opts.get('newest_first'):
1896 o.reverse()
1898 o.reverse()
1897 displayer = cmdutil.show_changeset(ui, other, opts)
1899 displayer = cmdutil.show_changeset(ui, other, opts)
1898 count = 0
1900 count = 0
1899 for n in o:
1901 for n in o:
1900 if count >= limit:
1902 if count >= limit:
1901 break
1903 break
1902 parents = [p for p in other.changelog.parents(n) if p != nullid]
1904 parents = [p for p in other.changelog.parents(n) if p != nullid]
1903 if opts.get('no_merges') and len(parents) == 2:
1905 if opts.get('no_merges') and len(parents) == 2:
1904 continue
1906 continue
1905 count += 1
1907 count += 1
1906 displayer.show(other[n])
1908 displayer.show(other[n])
1907 finally:
1909 finally:
1908 if hasattr(other, 'close'):
1910 if hasattr(other, 'close'):
1909 other.close()
1911 other.close()
1910 if cleanup:
1912 if cleanup:
1911 os.unlink(cleanup)
1913 os.unlink(cleanup)
1912
1914
1913 def init(ui, dest=".", **opts):
1915 def init(ui, dest=".", **opts):
1914 """create a new repository in the given directory
1916 """create a new repository in the given directory
1915
1917
1916 Initialize a new repository in the given directory. If the given
1918 Initialize a new repository in the given directory. If the given
1917 directory does not exist, it will be created.
1919 directory does not exist, it will be created.
1918
1920
1919 If no directory is given, the current directory is used.
1921 If no directory is given, the current directory is used.
1920
1922
1921 It is possible to specify an ssh:// URL as the destination.
1923 It is possible to specify an ssh:// URL as the destination.
1922 See 'hg help urls' for more information.
1924 See 'hg help urls' for more information.
1923 """
1925 """
1924 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1926 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1925
1927
1926 def locate(ui, repo, *pats, **opts):
1928 def locate(ui, repo, *pats, **opts):
1927 """locate files matching specific patterns
1929 """locate files matching specific patterns
1928
1930
1929 Print files under Mercurial control in the working directory whose
1931 Print files under Mercurial control in the working directory whose
1930 names match the given patterns.
1932 names match the given patterns.
1931
1933
1932 By default, this command searches all directories in the working
1934 By default, this command searches all directories in the working
1933 directory. To search just the current directory and its
1935 directory. To search just the current directory and its
1934 subdirectories, use "--include .".
1936 subdirectories, use "--include .".
1935
1937
1936 If no patterns are given to match, this command prints the names
1938 If no patterns are given to match, this command prints the names
1937 of all files under Mercurial control in the working directory.
1939 of all files under Mercurial control in the working directory.
1938
1940
1939 If you want to feed the output of this command into the "xargs"
1941 If you want to feed the output of this command into the "xargs"
1940 command, use the -0 option to both this command and "xargs". This
1942 command, use the -0 option to both this command and "xargs". This
1941 will avoid the problem of "xargs" treating single filenames that
1943 will avoid the problem of "xargs" treating single filenames that
1942 contain whitespace as multiple filenames.
1944 contain whitespace as multiple filenames.
1943 """
1945 """
1944 end = opts.get('print0') and '\0' or '\n'
1946 end = opts.get('print0') and '\0' or '\n'
1945 rev = opts.get('rev') or None
1947 rev = opts.get('rev') or None
1946
1948
1947 ret = 1
1949 ret = 1
1948 m = cmdutil.match(repo, pats, opts, default='relglob')
1950 m = cmdutil.match(repo, pats, opts, default='relglob')
1949 m.bad = lambda x,y: False
1951 m.bad = lambda x,y: False
1950 for abs in repo[rev].walk(m):
1952 for abs in repo[rev].walk(m):
1951 if not rev and abs not in repo.dirstate:
1953 if not rev and abs not in repo.dirstate:
1952 continue
1954 continue
1953 if opts.get('fullpath'):
1955 if opts.get('fullpath'):
1954 ui.write(repo.wjoin(abs), end)
1956 ui.write(repo.wjoin(abs), end)
1955 else:
1957 else:
1956 ui.write(((pats and m.rel(abs)) or abs), end)
1958 ui.write(((pats and m.rel(abs)) or abs), end)
1957 ret = 0
1959 ret = 0
1958
1960
1959 return ret
1961 return ret
1960
1962
1961 def log(ui, repo, *pats, **opts):
1963 def log(ui, repo, *pats, **opts):
1962 """show revision history of entire repository or files
1964 """show revision history of entire repository or files
1963
1965
1964 Print the revision history of the specified files or the entire
1966 Print the revision history of the specified files or the entire
1965 project.
1967 project.
1966
1968
1967 File history is shown without following rename or copy history of
1969 File history is shown without following rename or copy history of
1968 files. Use -f/--follow with a filename to follow history across
1970 files. Use -f/--follow with a filename to follow history across
1969 renames and copies. --follow without a filename will only show
1971 renames and copies. --follow without a filename will only show
1970 ancestors or descendants of the starting revision. --follow-first
1972 ancestors or descendants of the starting revision. --follow-first
1971 only follows the first parent of merge revisions.
1973 only follows the first parent of merge revisions.
1972
1974
1973 If no revision range is specified, the default is tip:0 unless
1975 If no revision range is specified, the default is tip:0 unless
1974 --follow is set, in which case the working directory parent is
1976 --follow is set, in which case the working directory parent is
1975 used as the starting revision.
1977 used as the starting revision.
1976
1978
1977 See 'hg help dates' for a list of formats valid for -d/--date.
1979 See 'hg help dates' for a list of formats valid for -d/--date.
1978
1980
1979 By default this command prints revision number and changeset id,
1981 By default this command prints revision number and changeset id,
1980 tags, non-trivial parents, user, date and time, and a summary for
1982 tags, non-trivial parents, user, date and time, and a summary for
1981 each commit. When the -v/--verbose switch is used, the list of
1983 each commit. When the -v/--verbose switch is used, the list of
1982 changed files and full commit message are shown.
1984 changed files and full commit message are shown.
1983
1985
1984 NOTE: log -p/--patch may generate unexpected diff output for merge
1986 NOTE: log -p/--patch may generate unexpected diff output for merge
1985 changesets, as it will only compare the merge changeset against
1987 changesets, as it will only compare the merge changeset against
1986 its first parent. Also, only files different from BOTH parents
1988 its first parent. Also, only files different from BOTH parents
1987 will appear in files:.
1989 will appear in files:.
1988 """
1990 """
1989
1991
1990 get = util.cachefunc(lambda r: repo[r])
1992 get = util.cachefunc(lambda r: repo[r])
1991 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1993 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1992
1994
1993 limit = cmdutil.loglimit(opts)
1995 limit = cmdutil.loglimit(opts)
1994 count = 0
1996 count = 0
1995
1997
1996 if opts.get('copies') and opts.get('rev'):
1998 if opts.get('copies') and opts.get('rev'):
1997 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1999 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1998 else:
2000 else:
1999 endrev = len(repo)
2001 endrev = len(repo)
2000 rcache = {}
2002 rcache = {}
2001 ncache = {}
2003 ncache = {}
2002 def getrenamed(fn, rev):
2004 def getrenamed(fn, rev):
2003 '''looks up all renames for a file (up to endrev) the first
2005 '''looks up all renames for a file (up to endrev) the first
2004 time the file is given. It indexes on the changerev and only
2006 time the file is given. It indexes on the changerev and only
2005 parses the manifest if linkrev != changerev.
2007 parses the manifest if linkrev != changerev.
2006 Returns rename info for fn at changerev rev.'''
2008 Returns rename info for fn at changerev rev.'''
2007 if fn not in rcache:
2009 if fn not in rcache:
2008 rcache[fn] = {}
2010 rcache[fn] = {}
2009 ncache[fn] = {}
2011 ncache[fn] = {}
2010 fl = repo.file(fn)
2012 fl = repo.file(fn)
2011 for i in fl:
2013 for i in fl:
2012 node = fl.node(i)
2014 node = fl.node(i)
2013 lr = fl.linkrev(i)
2015 lr = fl.linkrev(i)
2014 renamed = fl.renamed(node)
2016 renamed = fl.renamed(node)
2015 rcache[fn][lr] = renamed
2017 rcache[fn][lr] = renamed
2016 if renamed:
2018 if renamed:
2017 ncache[fn][node] = renamed
2019 ncache[fn][node] = renamed
2018 if lr >= endrev:
2020 if lr >= endrev:
2019 break
2021 break
2020 if rev in rcache[fn]:
2022 if rev in rcache[fn]:
2021 return rcache[fn][rev]
2023 return rcache[fn][rev]
2022
2024
2023 # If linkrev != rev (i.e. rev not found in rcache) fallback to
2025 # If linkrev != rev (i.e. rev not found in rcache) fallback to
2024 # filectx logic.
2026 # filectx logic.
2025
2027
2026 try:
2028 try:
2027 return repo[rev][fn].renamed()
2029 return repo[rev][fn].renamed()
2028 except error.LookupError:
2030 except error.LookupError:
2029 pass
2031 pass
2030 return None
2032 return None
2031
2033
2032 df = False
2034 df = False
2033 if opts["date"]:
2035 if opts["date"]:
2034 df = util.matchdate(opts["date"])
2036 df = util.matchdate(opts["date"])
2035
2037
2036 only_branches = opts.get('only_branch')
2038 only_branches = opts.get('only_branch')
2037
2039
2038 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2040 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2039 for st, rev, fns in changeiter:
2041 for st, rev, fns in changeiter:
2040 if st == 'add':
2042 if st == 'add':
2041 parents = [p for p in repo.changelog.parentrevs(rev)
2043 parents = [p for p in repo.changelog.parentrevs(rev)
2042 if p != nullrev]
2044 if p != nullrev]
2043 if opts.get('no_merges') and len(parents) == 2:
2045 if opts.get('no_merges') and len(parents) == 2:
2044 continue
2046 continue
2045 if opts.get('only_merges') and len(parents) != 2:
2047 if opts.get('only_merges') and len(parents) != 2:
2046 continue
2048 continue
2047
2049
2048 ctx = get(rev)
2050 ctx = get(rev)
2049 if only_branches and ctx.branch() not in only_branches:
2051 if only_branches and ctx.branch() not in only_branches:
2050 continue
2052 continue
2051
2053
2052 if df and not df(ctx.date()[0]):
2054 if df and not df(ctx.date()[0]):
2053 continue
2055 continue
2054
2056
2055 if opts.get('keyword'):
2057 if opts.get('keyword'):
2056 miss = 0
2058 miss = 0
2057 for k in [kw.lower() for kw in opts['keyword']]:
2059 for k in [kw.lower() for kw in opts['keyword']]:
2058 if not (k in ctx.user().lower() or
2060 if not (k in ctx.user().lower() or
2059 k in ctx.description().lower() or
2061 k in ctx.description().lower() or
2060 k in " ".join(ctx.files()).lower()):
2062 k in " ".join(ctx.files()).lower()):
2061 miss = 1
2063 miss = 1
2062 break
2064 break
2063 if miss:
2065 if miss:
2064 continue
2066 continue
2065
2067
2066 if opts['user']:
2068 if opts['user']:
2067 if not [k for k in opts['user'] if k in ctx.user()]:
2069 if not [k for k in opts['user'] if k in ctx.user()]:
2068 continue
2070 continue
2069
2071
2070 copies = []
2072 copies = []
2071 if opts.get('copies') and rev:
2073 if opts.get('copies') and rev:
2072 for fn in ctx.files():
2074 for fn in ctx.files():
2073 rename = getrenamed(fn, rev)
2075 rename = getrenamed(fn, rev)
2074 if rename:
2076 if rename:
2075 copies.append((fn, rename[0]))
2077 copies.append((fn, rename[0]))
2076
2078
2077 displayer.show(ctx, copies=copies)
2079 displayer.show(ctx, copies=copies)
2078
2080
2079 elif st == 'iter':
2081 elif st == 'iter':
2080 if count == limit: break
2082 if count == limit: break
2081 if displayer.flush(rev):
2083 if displayer.flush(rev):
2082 count += 1
2084 count += 1
2083
2085
2084 def manifest(ui, repo, node=None, rev=None):
2086 def manifest(ui, repo, node=None, rev=None):
2085 """output the current or given revision of the project manifest
2087 """output the current or given revision of the project manifest
2086
2088
2087 Print a list of version controlled files for the given revision.
2089 Print a list of version controlled files for the given revision.
2088 If no revision is given, the first parent of the working directory
2090 If no revision is given, the first parent of the working directory
2089 is used, or the null revision if no revision is checked out.
2091 is used, or the null revision if no revision is checked out.
2090
2092
2091 With -v, print file permissions, symlink and executable bits.
2093 With -v, print file permissions, symlink and executable bits.
2092 With --debug, print file revision hashes.
2094 With --debug, print file revision hashes.
2093 """
2095 """
2094
2096
2095 if rev and node:
2097 if rev and node:
2096 raise util.Abort(_("please specify just one revision"))
2098 raise util.Abort(_("please specify just one revision"))
2097
2099
2098 if not node:
2100 if not node:
2099 node = rev
2101 node = rev
2100
2102
2101 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2103 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2102 ctx = repo[node]
2104 ctx = repo[node]
2103 for f in ctx:
2105 for f in ctx:
2104 if ui.debugflag:
2106 if ui.debugflag:
2105 ui.write("%40s " % hex(ctx.manifest()[f]))
2107 ui.write("%40s " % hex(ctx.manifest()[f]))
2106 if ui.verbose:
2108 if ui.verbose:
2107 ui.write(decor[ctx.flags(f)])
2109 ui.write(decor[ctx.flags(f)])
2108 ui.write("%s\n" % f)
2110 ui.write("%s\n" % f)
2109
2111
2110 def merge(ui, repo, node=None, **opts):
2112 def merge(ui, repo, node=None, **opts):
2111 """merge working directory with another revision
2113 """merge working directory with another revision
2112
2114
2113 The current working directory is updated with all changes made in
2115 The current working directory is updated with all changes made in
2114 the requested revision since the last common predecessor revision.
2116 the requested revision since the last common predecessor revision.
2115
2117
2116 Files that changed between either parent are marked as changed for
2118 Files that changed between either parent are marked as changed for
2117 the next commit and a commit must be performed before any further
2119 the next commit and a commit must be performed before any further
2118 updates to the repository are allowed. The next commit will have
2120 updates to the repository are allowed. The next commit will have
2119 two parents.
2121 two parents.
2120
2122
2121 If no revision is specified, the working directory's parent is a
2123 If no revision is specified, the working directory's parent is a
2122 head revision, and the current branch contains exactly one other
2124 head revision, and the current branch contains exactly one other
2123 head, the other head is merged with by default. Otherwise, an
2125 head, the other head is merged with by default. Otherwise, an
2124 explicit revision with which to merge with must be provided.
2126 explicit revision with which to merge with must be provided.
2125 """
2127 """
2126
2128
2127 if opts.get('rev') and node:
2129 if opts.get('rev') and node:
2128 raise util.Abort(_("please specify just one revision"))
2130 raise util.Abort(_("please specify just one revision"))
2129 if not node:
2131 if not node:
2130 node = opts.get('rev')
2132 node = opts.get('rev')
2131
2133
2132 if not node:
2134 if not node:
2133 branch = repo.changectx(None).branch()
2135 branch = repo.changectx(None).branch()
2134 bheads = repo.branchheads(branch)
2136 bheads = repo.branchheads(branch)
2135 if len(bheads) > 2:
2137 if len(bheads) > 2:
2136 raise util.Abort(_("branch '%s' has %d heads - "
2138 raise util.Abort(_("branch '%s' has %d heads - "
2137 "please merge with an explicit rev") %
2139 "please merge with an explicit rev") %
2138 (branch, len(bheads)))
2140 (branch, len(bheads)))
2139
2141
2140 parent = repo.dirstate.parents()[0]
2142 parent = repo.dirstate.parents()[0]
2141 if len(bheads) == 1:
2143 if len(bheads) == 1:
2142 if len(repo.heads()) > 1:
2144 if len(repo.heads()) > 1:
2143 raise util.Abort(_("branch '%s' has one head - "
2145 raise util.Abort(_("branch '%s' has one head - "
2144 "please merge with an explicit rev") %
2146 "please merge with an explicit rev") %
2145 branch)
2147 branch)
2146 msg = _('there is nothing to merge')
2148 msg = _('there is nothing to merge')
2147 if parent != repo.lookup(repo[None].branch()):
2149 if parent != repo.lookup(repo[None].branch()):
2148 msg = _('%s - use "hg update" instead') % msg
2150 msg = _('%s - use "hg update" instead') % msg
2149 raise util.Abort(msg)
2151 raise util.Abort(msg)
2150
2152
2151 if parent not in bheads:
2153 if parent not in bheads:
2152 raise util.Abort(_('working dir not at a head rev - '
2154 raise util.Abort(_('working dir not at a head rev - '
2153 'use "hg update" or merge with an explicit rev'))
2155 'use "hg update" or merge with an explicit rev'))
2154 node = parent == bheads[0] and bheads[-1] or bheads[0]
2156 node = parent == bheads[0] and bheads[-1] or bheads[0]
2155
2157
2156 if opts.get('preview'):
2158 if opts.get('preview'):
2157 p1 = repo['.']
2159 p1 = repo['.']
2158 p2 = repo[node]
2160 p2 = repo[node]
2159 common = p1.ancestor(p2)
2161 common = p1.ancestor(p2)
2160 roots, heads = [common.node()], [p2.node()]
2162 roots, heads = [common.node()], [p2.node()]
2161 displayer = cmdutil.show_changeset(ui, repo, opts)
2163 displayer = cmdutil.show_changeset(ui, repo, opts)
2162 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2164 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2163 if node not in roots:
2165 if node not in roots:
2164 displayer.show(repo[node])
2166 displayer.show(repo[node])
2165 return 0
2167 return 0
2166
2168
2167 return hg.merge(repo, node, force=opts.get('force'))
2169 return hg.merge(repo, node, force=opts.get('force'))
2168
2170
2169 def outgoing(ui, repo, dest=None, **opts):
2171 def outgoing(ui, repo, dest=None, **opts):
2170 """show changesets not found in destination
2172 """show changesets not found in destination
2171
2173
2172 Show changesets not found in the specified destination repository
2174 Show changesets not found in the specified destination repository
2173 or the default push location. These are the changesets that would
2175 or the default push location. These are the changesets that would
2174 be pushed if a push was requested.
2176 be pushed if a push was requested.
2175
2177
2176 See pull for valid destination format details.
2178 See pull for valid destination format details.
2177 """
2179 """
2178 limit = cmdutil.loglimit(opts)
2180 limit = cmdutil.loglimit(opts)
2179 dest, revs, checkout = hg.parseurl(
2181 dest, revs, checkout = hg.parseurl(
2180 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2182 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2181 if revs:
2183 if revs:
2182 revs = [repo.lookup(rev) for rev in revs]
2184 revs = [repo.lookup(rev) for rev in revs]
2183
2185
2184 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2186 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2185 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2187 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2186 o = repo.findoutgoing(other, force=opts.get('force'))
2188 o = repo.findoutgoing(other, force=opts.get('force'))
2187 if not o:
2189 if not o:
2188 ui.status(_("no changes found\n"))
2190 ui.status(_("no changes found\n"))
2189 return 1
2191 return 1
2190 o = repo.changelog.nodesbetween(o, revs)[0]
2192 o = repo.changelog.nodesbetween(o, revs)[0]
2191 if opts.get('newest_first'):
2193 if opts.get('newest_first'):
2192 o.reverse()
2194 o.reverse()
2193 displayer = cmdutil.show_changeset(ui, repo, opts)
2195 displayer = cmdutil.show_changeset(ui, repo, opts)
2194 count = 0
2196 count = 0
2195 for n in o:
2197 for n in o:
2196 if count >= limit:
2198 if count >= limit:
2197 break
2199 break
2198 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2200 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2199 if opts.get('no_merges') and len(parents) == 2:
2201 if opts.get('no_merges') and len(parents) == 2:
2200 continue
2202 continue
2201 count += 1
2203 count += 1
2202 displayer.show(repo[n])
2204 displayer.show(repo[n])
2203
2205
2204 def parents(ui, repo, file_=None, **opts):
2206 def parents(ui, repo, file_=None, **opts):
2205 """show the parents of the working directory or revision
2207 """show the parents of the working directory or revision
2206
2208
2207 Print the working directory's parent revisions. If a revision is
2209 Print the working directory's parent revisions. If a revision is
2208 given via -r/--rev, the parent of that revision will be printed.
2210 given via -r/--rev, the parent of that revision will be printed.
2209 If a file argument is given, the revision in which the file was
2211 If a file argument is given, the revision in which the file was
2210 last changed (before the working directory revision or the
2212 last changed (before the working directory revision or the
2211 argument to --rev if given) is printed.
2213 argument to --rev if given) is printed.
2212 """
2214 """
2213 rev = opts.get('rev')
2215 rev = opts.get('rev')
2214 if rev:
2216 if rev:
2215 ctx = repo[rev]
2217 ctx = repo[rev]
2216 else:
2218 else:
2217 ctx = repo[None]
2219 ctx = repo[None]
2218
2220
2219 if file_:
2221 if file_:
2220 m = cmdutil.match(repo, (file_,), opts)
2222 m = cmdutil.match(repo, (file_,), opts)
2221 if m.anypats() or len(m.files()) != 1:
2223 if m.anypats() or len(m.files()) != 1:
2222 raise util.Abort(_('can only specify an explicit filename'))
2224 raise util.Abort(_('can only specify an explicit filename'))
2223 file_ = m.files()[0]
2225 file_ = m.files()[0]
2224 filenodes = []
2226 filenodes = []
2225 for cp in ctx.parents():
2227 for cp in ctx.parents():
2226 if not cp:
2228 if not cp:
2227 continue
2229 continue
2228 try:
2230 try:
2229 filenodes.append(cp.filenode(file_))
2231 filenodes.append(cp.filenode(file_))
2230 except error.LookupError:
2232 except error.LookupError:
2231 pass
2233 pass
2232 if not filenodes:
2234 if not filenodes:
2233 raise util.Abort(_("'%s' not found in manifest!") % file_)
2235 raise util.Abort(_("'%s' not found in manifest!") % file_)
2234 fl = repo.file(file_)
2236 fl = repo.file(file_)
2235 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2237 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2236 else:
2238 else:
2237 p = [cp.node() for cp in ctx.parents()]
2239 p = [cp.node() for cp in ctx.parents()]
2238
2240
2239 displayer = cmdutil.show_changeset(ui, repo, opts)
2241 displayer = cmdutil.show_changeset(ui, repo, opts)
2240 for n in p:
2242 for n in p:
2241 if n != nullid:
2243 if n != nullid:
2242 displayer.show(repo[n])
2244 displayer.show(repo[n])
2243
2245
2244 def paths(ui, repo, search=None):
2246 def paths(ui, repo, search=None):
2245 """show aliases for remote repositories
2247 """show aliases for remote repositories
2246
2248
2247 Show definition of symbolic path name NAME. If no name is given,
2249 Show definition of symbolic path name NAME. If no name is given,
2248 show definition of all available names.
2250 show definition of all available names.
2249
2251
2250 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2252 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2251 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2253 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2252
2254
2253 See 'hg help urls' for more information.
2255 See 'hg help urls' for more information.
2254 """
2256 """
2255 if search:
2257 if search:
2256 for name, path in ui.configitems("paths"):
2258 for name, path in ui.configitems("paths"):
2257 if name == search:
2259 if name == search:
2258 ui.write("%s\n" % url.hidepassword(path))
2260 ui.write("%s\n" % url.hidepassword(path))
2259 return
2261 return
2260 ui.warn(_("not found!\n"))
2262 ui.warn(_("not found!\n"))
2261 return 1
2263 return 1
2262 else:
2264 else:
2263 for name, path in ui.configitems("paths"):
2265 for name, path in ui.configitems("paths"):
2264 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2266 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2265
2267
2266 def postincoming(ui, repo, modheads, optupdate, checkout):
2268 def postincoming(ui, repo, modheads, optupdate, checkout):
2267 if modheads == 0:
2269 if modheads == 0:
2268 return
2270 return
2269 if optupdate:
2271 if optupdate:
2270 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2272 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2271 return hg.update(repo, checkout)
2273 return hg.update(repo, checkout)
2272 else:
2274 else:
2273 ui.status(_("not updating, since new heads added\n"))
2275 ui.status(_("not updating, since new heads added\n"))
2274 if modheads > 1:
2276 if modheads > 1:
2275 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2277 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2276 else:
2278 else:
2277 ui.status(_("(run 'hg update' to get a working copy)\n"))
2279 ui.status(_("(run 'hg update' to get a working copy)\n"))
2278
2280
2279 def pull(ui, repo, source="default", **opts):
2281 def pull(ui, repo, source="default", **opts):
2280 """pull changes from the specified source
2282 """pull changes from the specified source
2281
2283
2282 Pull changes from a remote repository to a local one.
2284 Pull changes from a remote repository to a local one.
2283
2285
2284 This finds all changes from the repository at the specified path
2286 This finds all changes from the repository at the specified path
2285 or URL and adds them to a local repository (the current one unless
2287 or URL and adds them to a local repository (the current one unless
2286 -R is specified). By default, this does not update the copy of the
2288 -R is specified). By default, this does not update the copy of the
2287 project in the working directory.
2289 project in the working directory.
2288
2290
2289 Use hg incoming if you want to see what would have been added by a
2291 Use hg incoming if you want to see what would have been added by a
2290 pull at the time you issued this command. If you then decide to
2292 pull at the time you issued this command. If you then decide to
2291 added those changes to the repository, you should use pull -r X
2293 added those changes to the repository, you should use pull -r X
2292 where X is the last changeset listed by hg incoming.
2294 where X is the last changeset listed by hg incoming.
2293
2295
2294 If SOURCE is omitted, the 'default' path will be used.
2296 If SOURCE is omitted, the 'default' path will be used.
2295 See 'hg help urls' for more information.
2297 See 'hg help urls' for more information.
2296 """
2298 """
2297 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2299 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2298 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2300 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2299 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2301 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2300 if revs:
2302 if revs:
2301 try:
2303 try:
2302 revs = [other.lookup(rev) for rev in revs]
2304 revs = [other.lookup(rev) for rev in revs]
2303 except error.CapabilityError:
2305 except error.CapabilityError:
2304 err = _("Other repository doesn't support revision lookup, "
2306 err = _("Other repository doesn't support revision lookup, "
2305 "so a rev cannot be specified.")
2307 "so a rev cannot be specified.")
2306 raise util.Abort(err)
2308 raise util.Abort(err)
2307
2309
2308 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2310 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2309 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2311 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2310
2312
2311 def push(ui, repo, dest=None, **opts):
2313 def push(ui, repo, dest=None, **opts):
2312 """push changes to the specified destination
2314 """push changes to the specified destination
2313
2315
2314 Push changes from the local repository to the given destination.
2316 Push changes from the local repository to the given destination.
2315
2317
2316 This is the symmetrical operation for pull. It moves changes from
2318 This is the symmetrical operation for pull. It moves changes from
2317 the current repository to a different one. If the destination is
2319 the current repository to a different one. If the destination is
2318 local this is identical to a pull in that directory from the
2320 local this is identical to a pull in that directory from the
2319 current one.
2321 current one.
2320
2322
2321 By default, push will refuse to run if it detects the result would
2323 By default, push will refuse to run if it detects the result would
2322 increase the number of remote heads. This generally indicates the
2324 increase the number of remote heads. This generally indicates the
2323 user forgot to pull and merge before pushing.
2325 user forgot to pull and merge before pushing.
2324
2326
2325 If -r/--rev is used, the named revision and all its ancestors will
2327 If -r/--rev is used, the named revision and all its ancestors will
2326 be pushed to the remote repository.
2328 be pushed to the remote repository.
2327
2329
2328 Please see 'hg help urls' for important details about ssh://
2330 Please see 'hg help urls' for important details about ssh://
2329 URLs. If DESTINATION is omitted, a default path will be used.
2331 URLs. If DESTINATION is omitted, a default path will be used.
2330 """
2332 """
2331 dest, revs, checkout = hg.parseurl(
2333 dest, revs, checkout = hg.parseurl(
2332 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2334 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2333 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2335 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2334 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2336 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2335 if revs:
2337 if revs:
2336 revs = [repo.lookup(rev) for rev in revs]
2338 revs = [repo.lookup(rev) for rev in revs]
2337
2339
2338 # push subrepos depth-first for coherent ordering
2340 # push subrepos depth-first for coherent ordering
2339 c = repo['']
2341 c = repo['']
2340 subs = c.substate # only repos that are committed
2342 subs = c.substate # only repos that are committed
2341 for s in sorted(subs):
2343 for s in sorted(subs):
2342 c.sub(s).push(opts.get('force'))
2344 c.sub(s).push(opts.get('force'))
2343
2345
2344 r = repo.push(other, opts.get('force'), revs=revs)
2346 r = repo.push(other, opts.get('force'), revs=revs)
2345 return r == 0
2347 return r == 0
2346
2348
2347 def recover(ui, repo):
2349 def recover(ui, repo):
2348 """roll back an interrupted transaction
2350 """roll back an interrupted transaction
2349
2351
2350 Recover from an interrupted commit or pull.
2352 Recover from an interrupted commit or pull.
2351
2353
2352 This command tries to fix the repository status after an
2354 This command tries to fix the repository status after an
2353 interrupted operation. It should only be necessary when Mercurial
2355 interrupted operation. It should only be necessary when Mercurial
2354 suggests it.
2356 suggests it.
2355 """
2357 """
2356 if repo.recover():
2358 if repo.recover():
2357 return hg.verify(repo)
2359 return hg.verify(repo)
2358 return 1
2360 return 1
2359
2361
2360 def remove(ui, repo, *pats, **opts):
2362 def remove(ui, repo, *pats, **opts):
2361 """remove the specified files on the next commit
2363 """remove the specified files on the next commit
2362
2364
2363 Schedule the indicated files for removal from the repository.
2365 Schedule the indicated files for removal from the repository.
2364
2366
2365 This only removes files from the current branch, not from the
2367 This only removes files from the current branch, not from the
2366 entire project history. -A/--after can be used to remove only
2368 entire project history. -A/--after can be used to remove only
2367 files that have already been deleted, -f/--force can be used to
2369 files that have already been deleted, -f/--force can be used to
2368 force deletion, and -Af can be used to remove files from the next
2370 force deletion, and -Af can be used to remove files from the next
2369 revision without deleting them from the working directory.
2371 revision without deleting them from the working directory.
2370
2372
2371 The following table details the behavior of remove for different
2373 The following table details the behavior of remove for different
2372 file states (columns) and option combinations (rows). The file
2374 file states (columns) and option combinations (rows). The file
2373 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2375 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2374 reported by hg status). The actions are Warn, Remove (from branch)
2376 reported by hg status). The actions are Warn, Remove (from branch)
2375 and Delete (from disk)::
2377 and Delete (from disk)::
2376
2378
2377 A C M !
2379 A C M !
2378 none W RD W R
2380 none W RD W R
2379 -f R RD RD R
2381 -f R RD RD R
2380 -A W W W R
2382 -A W W W R
2381 -Af R R R R
2383 -Af R R R R
2382
2384
2383 This command schedules the files to be removed at the next commit.
2385 This command schedules the files to be removed at the next commit.
2384 To undo a remove before that, see hg revert.
2386 To undo a remove before that, see hg revert.
2385 """
2387 """
2386
2388
2387 after, force = opts.get('after'), opts.get('force')
2389 after, force = opts.get('after'), opts.get('force')
2388 if not pats and not after:
2390 if not pats and not after:
2389 raise util.Abort(_('no files specified'))
2391 raise util.Abort(_('no files specified'))
2390
2392
2391 m = cmdutil.match(repo, pats, opts)
2393 m = cmdutil.match(repo, pats, opts)
2392 s = repo.status(match=m, clean=True)
2394 s = repo.status(match=m, clean=True)
2393 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2395 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2394
2396
2395 for f in m.files():
2397 for f in m.files():
2396 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2398 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2397 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2399 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2398
2400
2399 def warn(files, reason):
2401 def warn(files, reason):
2400 for f in files:
2402 for f in files:
2401 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2403 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2402 % (m.rel(f), reason))
2404 % (m.rel(f), reason))
2403
2405
2404 if force:
2406 if force:
2405 remove, forget = modified + deleted + clean, added
2407 remove, forget = modified + deleted + clean, added
2406 elif after:
2408 elif after:
2407 remove, forget = deleted, []
2409 remove, forget = deleted, []
2408 warn(modified + added + clean, _('still exists'))
2410 warn(modified + added + clean, _('still exists'))
2409 else:
2411 else:
2410 remove, forget = deleted + clean, []
2412 remove, forget = deleted + clean, []
2411 warn(modified, _('is modified'))
2413 warn(modified, _('is modified'))
2412 warn(added, _('has been marked for add'))
2414 warn(added, _('has been marked for add'))
2413
2415
2414 for f in sorted(remove + forget):
2416 for f in sorted(remove + forget):
2415 if ui.verbose or not m.exact(f):
2417 if ui.verbose or not m.exact(f):
2416 ui.status(_('removing %s\n') % m.rel(f))
2418 ui.status(_('removing %s\n') % m.rel(f))
2417
2419
2418 repo.forget(forget)
2420 repo.forget(forget)
2419 repo.remove(remove, unlink=not after)
2421 repo.remove(remove, unlink=not after)
2420
2422
2421 def rename(ui, repo, *pats, **opts):
2423 def rename(ui, repo, *pats, **opts):
2422 """rename files; equivalent of copy + remove
2424 """rename files; equivalent of copy + remove
2423
2425
2424 Mark dest as copies of sources; mark sources for deletion. If dest
2426 Mark dest as copies of sources; mark sources for deletion. If dest
2425 is a directory, copies are put in that directory. If dest is a
2427 is a directory, copies are put in that directory. If dest is a
2426 file, there can only be one source.
2428 file, there can only be one source.
2427
2429
2428 By default, this command copies the contents of files as they
2430 By default, this command copies the contents of files as they
2429 exist in the working directory. If invoked with -A/--after, the
2431 exist in the working directory. If invoked with -A/--after, the
2430 operation is recorded, but no copying is performed.
2432 operation is recorded, but no copying is performed.
2431
2433
2432 This command takes effect at the next commit. To undo a rename
2434 This command takes effect at the next commit. To undo a rename
2433 before that, see hg revert.
2435 before that, see hg revert.
2434 """
2436 """
2435 wlock = repo.wlock(False)
2437 wlock = repo.wlock(False)
2436 try:
2438 try:
2437 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2439 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2438 finally:
2440 finally:
2439 wlock.release()
2441 wlock.release()
2440
2442
2441 def resolve(ui, repo, *pats, **opts):
2443 def resolve(ui, repo, *pats, **opts):
2442 """retry file merges from a merge or update
2444 """retry file merges from a merge or update
2443
2445
2444 This command will cleanly retry unresolved file merges using file
2446 This command will cleanly retry unresolved file merges using file
2445 revisions preserved from the last update or merge. To attempt to
2447 revisions preserved from the last update or merge. To attempt to
2446 resolve all unresolved files, use the -a/--all switch.
2448 resolve all unresolved files, use the -a/--all switch.
2447
2449
2448 If a conflict is resolved manually, please note that the changes
2450 If a conflict is resolved manually, please note that the changes
2449 will be overwritten if the merge is retried with resolve. The
2451 will be overwritten if the merge is retried with resolve. The
2450 -m/--mark switch should be used to mark the file as resolved.
2452 -m/--mark switch should be used to mark the file as resolved.
2451
2453
2452 This command also allows listing resolved files and manually
2454 This command also allows listing resolved files and manually
2453 indicating whether or not files are resolved. All files must be
2455 indicating whether or not files are resolved. All files must be
2454 marked as resolved before a commit is permitted.
2456 marked as resolved before a commit is permitted.
2455
2457
2456 The codes used to show the status of files are::
2458 The codes used to show the status of files are::
2457
2459
2458 U = unresolved
2460 U = unresolved
2459 R = resolved
2461 R = resolved
2460 """
2462 """
2461
2463
2462 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2464 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2463
2465
2464 if (show and (mark or unmark)) or (mark and unmark):
2466 if (show and (mark or unmark)) or (mark and unmark):
2465 raise util.Abort(_("too many options specified"))
2467 raise util.Abort(_("too many options specified"))
2466 if pats and all:
2468 if pats and all:
2467 raise util.Abort(_("can't specify --all and patterns"))
2469 raise util.Abort(_("can't specify --all and patterns"))
2468 if not (all or pats or show or mark or unmark):
2470 if not (all or pats or show or mark or unmark):
2469 raise util.Abort(_('no files or directories specified; '
2471 raise util.Abort(_('no files or directories specified; '
2470 'use --all to remerge all files'))
2472 'use --all to remerge all files'))
2471
2473
2472 ms = merge_.mergestate(repo)
2474 ms = merge_.mergestate(repo)
2473 m = cmdutil.match(repo, pats, opts)
2475 m = cmdutil.match(repo, pats, opts)
2474
2476
2475 for f in ms:
2477 for f in ms:
2476 if m(f):
2478 if m(f):
2477 if show:
2479 if show:
2478 ui.write("%s %s\n" % (ms[f].upper(), f))
2480 ui.write("%s %s\n" % (ms[f].upper(), f))
2479 elif mark:
2481 elif mark:
2480 ms.mark(f, "r")
2482 ms.mark(f, "r")
2481 elif unmark:
2483 elif unmark:
2482 ms.mark(f, "u")
2484 ms.mark(f, "u")
2483 else:
2485 else:
2484 wctx = repo[None]
2486 wctx = repo[None]
2485 mctx = wctx.parents()[-1]
2487 mctx = wctx.parents()[-1]
2486
2488
2487 # backup pre-resolve (merge uses .orig for its own purposes)
2489 # backup pre-resolve (merge uses .orig for its own purposes)
2488 a = repo.wjoin(f)
2490 a = repo.wjoin(f)
2489 util.copyfile(a, a + ".resolve")
2491 util.copyfile(a, a + ".resolve")
2490
2492
2491 # resolve file
2493 # resolve file
2492 ms.resolve(f, wctx, mctx)
2494 ms.resolve(f, wctx, mctx)
2493
2495
2494 # replace filemerge's .orig file with our resolve file
2496 # replace filemerge's .orig file with our resolve file
2495 util.rename(a + ".resolve", a + ".orig")
2497 util.rename(a + ".resolve", a + ".orig")
2496
2498
2497 def revert(ui, repo, *pats, **opts):
2499 def revert(ui, repo, *pats, **opts):
2498 """restore individual files or directories to an earlier state
2500 """restore individual files or directories to an earlier state
2499
2501
2500 (Use update -r to check out earlier revisions, revert does not
2502 (Use update -r to check out earlier revisions, revert does not
2501 change the working directory parents.)
2503 change the working directory parents.)
2502
2504
2503 With no revision specified, revert the named files or directories
2505 With no revision specified, revert the named files or directories
2504 to the contents they had in the parent of the working directory.
2506 to the contents they had in the parent of the working directory.
2505 This restores the contents of the affected files to an unmodified
2507 This restores the contents of the affected files to an unmodified
2506 state and unschedules adds, removes, copies, and renames. If the
2508 state and unschedules adds, removes, copies, and renames. If the
2507 working directory has two parents, you must explicitly specify the
2509 working directory has two parents, you must explicitly specify the
2508 revision to revert to.
2510 revision to revert to.
2509
2511
2510 Using the -r/--rev option, revert the given files or directories
2512 Using the -r/--rev option, revert the given files or directories
2511 to their contents as of a specific revision. This can be helpful
2513 to their contents as of a specific revision. This can be helpful
2512 to "roll back" some or all of an earlier change. See 'hg help
2514 to "roll back" some or all of an earlier change. See 'hg help
2513 dates' for a list of formats valid for -d/--date.
2515 dates' for a list of formats valid for -d/--date.
2514
2516
2515 Revert modifies the working directory. It does not commit any
2517 Revert modifies the working directory. It does not commit any
2516 changes, or change the parent of the working directory. If you
2518 changes, or change the parent of the working directory. If you
2517 revert to a revision other than the parent of the working
2519 revert to a revision other than the parent of the working
2518 directory, the reverted files will thus appear modified
2520 directory, the reverted files will thus appear modified
2519 afterwards.
2521 afterwards.
2520
2522
2521 If a file has been deleted, it is restored. If the executable mode
2523 If a file has been deleted, it is restored. If the executable mode
2522 of a file was changed, it is reset.
2524 of a file was changed, it is reset.
2523
2525
2524 If names are given, all files matching the names are reverted.
2526 If names are given, all files matching the names are reverted.
2525 If no arguments are given, no files are reverted.
2527 If no arguments are given, no files are reverted.
2526
2528
2527 Modified files are saved with a .orig suffix before reverting.
2529 Modified files are saved with a .orig suffix before reverting.
2528 To disable these backups, use --no-backup.
2530 To disable these backups, use --no-backup.
2529 """
2531 """
2530
2532
2531 if opts["date"]:
2533 if opts["date"]:
2532 if opts["rev"]:
2534 if opts["rev"]:
2533 raise util.Abort(_("you can't specify a revision and a date"))
2535 raise util.Abort(_("you can't specify a revision and a date"))
2534 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2536 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2535
2537
2536 if not pats and not opts.get('all'):
2538 if not pats and not opts.get('all'):
2537 raise util.Abort(_('no files or directories specified; '
2539 raise util.Abort(_('no files or directories specified; '
2538 'use --all to revert the whole repo'))
2540 'use --all to revert the whole repo'))
2539
2541
2540 parent, p2 = repo.dirstate.parents()
2542 parent, p2 = repo.dirstate.parents()
2541 if not opts.get('rev') and p2 != nullid:
2543 if not opts.get('rev') and p2 != nullid:
2542 raise util.Abort(_('uncommitted merge - please provide a '
2544 raise util.Abort(_('uncommitted merge - please provide a '
2543 'specific revision'))
2545 'specific revision'))
2544 ctx = repo[opts.get('rev')]
2546 ctx = repo[opts.get('rev')]
2545 node = ctx.node()
2547 node = ctx.node()
2546 mf = ctx.manifest()
2548 mf = ctx.manifest()
2547 if node == parent:
2549 if node == parent:
2548 pmf = mf
2550 pmf = mf
2549 else:
2551 else:
2550 pmf = None
2552 pmf = None
2551
2553
2552 # need all matching names in dirstate and manifest of target rev,
2554 # need all matching names in dirstate and manifest of target rev,
2553 # so have to walk both. do not print errors if files exist in one
2555 # so have to walk both. do not print errors if files exist in one
2554 # but not other.
2556 # but not other.
2555
2557
2556 names = {}
2558 names = {}
2557
2559
2558 wlock = repo.wlock()
2560 wlock = repo.wlock()
2559 try:
2561 try:
2560 # walk dirstate.
2562 # walk dirstate.
2561
2563
2562 m = cmdutil.match(repo, pats, opts)
2564 m = cmdutil.match(repo, pats, opts)
2563 m.bad = lambda x,y: False
2565 m.bad = lambda x,y: False
2564 for abs in repo.walk(m):
2566 for abs in repo.walk(m):
2565 names[abs] = m.rel(abs), m.exact(abs)
2567 names[abs] = m.rel(abs), m.exact(abs)
2566
2568
2567 # walk target manifest.
2569 # walk target manifest.
2568
2570
2569 def badfn(path, msg):
2571 def badfn(path, msg):
2570 if path in names:
2572 if path in names:
2571 return
2573 return
2572 path_ = path + '/'
2574 path_ = path + '/'
2573 for f in names:
2575 for f in names:
2574 if f.startswith(path_):
2576 if f.startswith(path_):
2575 return
2577 return
2576 ui.warn("%s: %s\n" % (m.rel(path), msg))
2578 ui.warn("%s: %s\n" % (m.rel(path), msg))
2577
2579
2578 m = cmdutil.match(repo, pats, opts)
2580 m = cmdutil.match(repo, pats, opts)
2579 m.bad = badfn
2581 m.bad = badfn
2580 for abs in repo[node].walk(m):
2582 for abs in repo[node].walk(m):
2581 if abs not in names:
2583 if abs not in names:
2582 names[abs] = m.rel(abs), m.exact(abs)
2584 names[abs] = m.rel(abs), m.exact(abs)
2583
2585
2584 m = cmdutil.matchfiles(repo, names)
2586 m = cmdutil.matchfiles(repo, names)
2585 changes = repo.status(match=m)[:4]
2587 changes = repo.status(match=m)[:4]
2586 modified, added, removed, deleted = map(set, changes)
2588 modified, added, removed, deleted = map(set, changes)
2587
2589
2588 # if f is a rename, also revert the source
2590 # if f is a rename, also revert the source
2589 cwd = repo.getcwd()
2591 cwd = repo.getcwd()
2590 for f in added:
2592 for f in added:
2591 src = repo.dirstate.copied(f)
2593 src = repo.dirstate.copied(f)
2592 if src and src not in names and repo.dirstate[src] == 'r':
2594 if src and src not in names and repo.dirstate[src] == 'r':
2593 removed.add(src)
2595 removed.add(src)
2594 names[src] = (repo.pathto(src, cwd), True)
2596 names[src] = (repo.pathto(src, cwd), True)
2595
2597
2596 def removeforget(abs):
2598 def removeforget(abs):
2597 if repo.dirstate[abs] == 'a':
2599 if repo.dirstate[abs] == 'a':
2598 return _('forgetting %s\n')
2600 return _('forgetting %s\n')
2599 return _('removing %s\n')
2601 return _('removing %s\n')
2600
2602
2601 revert = ([], _('reverting %s\n'))
2603 revert = ([], _('reverting %s\n'))
2602 add = ([], _('adding %s\n'))
2604 add = ([], _('adding %s\n'))
2603 remove = ([], removeforget)
2605 remove = ([], removeforget)
2604 undelete = ([], _('undeleting %s\n'))
2606 undelete = ([], _('undeleting %s\n'))
2605
2607
2606 disptable = (
2608 disptable = (
2607 # dispatch table:
2609 # dispatch table:
2608 # file state
2610 # file state
2609 # action if in target manifest
2611 # action if in target manifest
2610 # action if not in target manifest
2612 # action if not in target manifest
2611 # make backup if in target manifest
2613 # make backup if in target manifest
2612 # make backup if not in target manifest
2614 # make backup if not in target manifest
2613 (modified, revert, remove, True, True),
2615 (modified, revert, remove, True, True),
2614 (added, revert, remove, True, False),
2616 (added, revert, remove, True, False),
2615 (removed, undelete, None, False, False),
2617 (removed, undelete, None, False, False),
2616 (deleted, revert, remove, False, False),
2618 (deleted, revert, remove, False, False),
2617 )
2619 )
2618
2620
2619 for abs, (rel, exact) in sorted(names.items()):
2621 for abs, (rel, exact) in sorted(names.items()):
2620 mfentry = mf.get(abs)
2622 mfentry = mf.get(abs)
2621 target = repo.wjoin(abs)
2623 target = repo.wjoin(abs)
2622 def handle(xlist, dobackup):
2624 def handle(xlist, dobackup):
2623 xlist[0].append(abs)
2625 xlist[0].append(abs)
2624 if dobackup and not opts.get('no_backup') and util.lexists(target):
2626 if dobackup and not opts.get('no_backup') and util.lexists(target):
2625 bakname = "%s.orig" % rel
2627 bakname = "%s.orig" % rel
2626 ui.note(_('saving current version of %s as %s\n') %
2628 ui.note(_('saving current version of %s as %s\n') %
2627 (rel, bakname))
2629 (rel, bakname))
2628 if not opts.get('dry_run'):
2630 if not opts.get('dry_run'):
2629 util.copyfile(target, bakname)
2631 util.copyfile(target, bakname)
2630 if ui.verbose or not exact:
2632 if ui.verbose or not exact:
2631 msg = xlist[1]
2633 msg = xlist[1]
2632 if not isinstance(msg, basestring):
2634 if not isinstance(msg, basestring):
2633 msg = msg(abs)
2635 msg = msg(abs)
2634 ui.status(msg % rel)
2636 ui.status(msg % rel)
2635 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2637 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2636 if abs not in table: continue
2638 if abs not in table: continue
2637 # file has changed in dirstate
2639 # file has changed in dirstate
2638 if mfentry:
2640 if mfentry:
2639 handle(hitlist, backuphit)
2641 handle(hitlist, backuphit)
2640 elif misslist is not None:
2642 elif misslist is not None:
2641 handle(misslist, backupmiss)
2643 handle(misslist, backupmiss)
2642 break
2644 break
2643 else:
2645 else:
2644 if abs not in repo.dirstate:
2646 if abs not in repo.dirstate:
2645 if mfentry:
2647 if mfentry:
2646 handle(add, True)
2648 handle(add, True)
2647 elif exact:
2649 elif exact:
2648 ui.warn(_('file not managed: %s\n') % rel)
2650 ui.warn(_('file not managed: %s\n') % rel)
2649 continue
2651 continue
2650 # file has not changed in dirstate
2652 # file has not changed in dirstate
2651 if node == parent:
2653 if node == parent:
2652 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2654 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2653 continue
2655 continue
2654 if pmf is None:
2656 if pmf is None:
2655 # only need parent manifest in this unlikely case,
2657 # only need parent manifest in this unlikely case,
2656 # so do not read by default
2658 # so do not read by default
2657 pmf = repo[parent].manifest()
2659 pmf = repo[parent].manifest()
2658 if abs in pmf:
2660 if abs in pmf:
2659 if mfentry:
2661 if mfentry:
2660 # if version of file is same in parent and target
2662 # if version of file is same in parent and target
2661 # manifests, do nothing
2663 # manifests, do nothing
2662 if (pmf[abs] != mfentry or
2664 if (pmf[abs] != mfentry or
2663 pmf.flags(abs) != mf.flags(abs)):
2665 pmf.flags(abs) != mf.flags(abs)):
2664 handle(revert, False)
2666 handle(revert, False)
2665 else:
2667 else:
2666 handle(remove, False)
2668 handle(remove, False)
2667
2669
2668 if not opts.get('dry_run'):
2670 if not opts.get('dry_run'):
2669 def checkout(f):
2671 def checkout(f):
2670 fc = ctx[f]
2672 fc = ctx[f]
2671 repo.wwrite(f, fc.data(), fc.flags())
2673 repo.wwrite(f, fc.data(), fc.flags())
2672
2674
2673 audit_path = util.path_auditor(repo.root)
2675 audit_path = util.path_auditor(repo.root)
2674 for f in remove[0]:
2676 for f in remove[0]:
2675 if repo.dirstate[f] == 'a':
2677 if repo.dirstate[f] == 'a':
2676 repo.dirstate.forget(f)
2678 repo.dirstate.forget(f)
2677 continue
2679 continue
2678 audit_path(f)
2680 audit_path(f)
2679 try:
2681 try:
2680 util.unlink(repo.wjoin(f))
2682 util.unlink(repo.wjoin(f))
2681 except OSError:
2683 except OSError:
2682 pass
2684 pass
2683 repo.dirstate.remove(f)
2685 repo.dirstate.remove(f)
2684
2686
2685 normal = None
2687 normal = None
2686 if node == parent:
2688 if node == parent:
2687 # We're reverting to our parent. If possible, we'd like status
2689 # We're reverting to our parent. If possible, we'd like status
2688 # to report the file as clean. We have to use normallookup for
2690 # to report the file as clean. We have to use normallookup for
2689 # merges to avoid losing information about merged/dirty files.
2691 # merges to avoid losing information about merged/dirty files.
2690 if p2 != nullid:
2692 if p2 != nullid:
2691 normal = repo.dirstate.normallookup
2693 normal = repo.dirstate.normallookup
2692 else:
2694 else:
2693 normal = repo.dirstate.normal
2695 normal = repo.dirstate.normal
2694 for f in revert[0]:
2696 for f in revert[0]:
2695 checkout(f)
2697 checkout(f)
2696 if normal:
2698 if normal:
2697 normal(f)
2699 normal(f)
2698
2700
2699 for f in add[0]:
2701 for f in add[0]:
2700 checkout(f)
2702 checkout(f)
2701 repo.dirstate.add(f)
2703 repo.dirstate.add(f)
2702
2704
2703 normal = repo.dirstate.normallookup
2705 normal = repo.dirstate.normallookup
2704 if node == parent and p2 == nullid:
2706 if node == parent and p2 == nullid:
2705 normal = repo.dirstate.normal
2707 normal = repo.dirstate.normal
2706 for f in undelete[0]:
2708 for f in undelete[0]:
2707 checkout(f)
2709 checkout(f)
2708 normal(f)
2710 normal(f)
2709
2711
2710 finally:
2712 finally:
2711 wlock.release()
2713 wlock.release()
2712
2714
2713 def rollback(ui, repo):
2715 def rollback(ui, repo):
2714 """roll back the last transaction
2716 """roll back the last transaction
2715
2717
2716 This command should be used with care. There is only one level of
2718 This command should be used with care. There is only one level of
2717 rollback, and there is no way to undo a rollback. It will also
2719 rollback, and there is no way to undo a rollback. It will also
2718 restore the dirstate at the time of the last transaction, losing
2720 restore the dirstate at the time of the last transaction, losing
2719 any dirstate changes since that time. This command does not alter
2721 any dirstate changes since that time. This command does not alter
2720 the working directory.
2722 the working directory.
2721
2723
2722 Transactions are used to encapsulate the effects of all commands
2724 Transactions are used to encapsulate the effects of all commands
2723 that create new changesets or propagate existing changesets into a
2725 that create new changesets or propagate existing changesets into a
2724 repository. For example, the following commands are transactional,
2726 repository. For example, the following commands are transactional,
2725 and their effects can be rolled back::
2727 and their effects can be rolled back::
2726
2728
2727 commit
2729 commit
2728 import
2730 import
2729 pull
2731 pull
2730 push (with this repository as destination)
2732 push (with this repository as destination)
2731 unbundle
2733 unbundle
2732
2734
2733 This command is not intended for use on public repositories. Once
2735 This command is not intended for use on public repositories. Once
2734 changes are visible for pull by other users, rolling a transaction
2736 changes are visible for pull by other users, rolling a transaction
2735 back locally is ineffective (someone else may already have pulled
2737 back locally is ineffective (someone else may already have pulled
2736 the changes). Furthermore, a race is possible with readers of the
2738 the changes). Furthermore, a race is possible with readers of the
2737 repository; for example an in-progress pull from the repository
2739 repository; for example an in-progress pull from the repository
2738 may fail if a rollback is performed.
2740 may fail if a rollback is performed.
2739 """
2741 """
2740 repo.rollback()
2742 repo.rollback()
2741
2743
2742 def root(ui, repo):
2744 def root(ui, repo):
2743 """print the root (top) of the current working directory
2745 """print the root (top) of the current working directory
2744
2746
2745 Print the root directory of the current repository.
2747 Print the root directory of the current repository.
2746 """
2748 """
2747 ui.write(repo.root + "\n")
2749 ui.write(repo.root + "\n")
2748
2750
2749 def serve(ui, repo, **opts):
2751 def serve(ui, repo, **opts):
2750 """export the repository via HTTP
2752 """export the repository via HTTP
2751
2753
2752 Start a local HTTP repository browser and pull server.
2754 Start a local HTTP repository browser and pull server.
2753
2755
2754 By default, the server logs accesses to stdout and errors to
2756 By default, the server logs accesses to stdout and errors to
2755 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2757 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2756 files.
2758 files.
2757 """
2759 """
2758
2760
2759 if opts["stdio"]:
2761 if opts["stdio"]:
2760 if repo is None:
2762 if repo is None:
2761 raise error.RepoError(_("There is no Mercurial repository here"
2763 raise error.RepoError(_("There is no Mercurial repository here"
2762 " (.hg not found)"))
2764 " (.hg not found)"))
2763 s = sshserver.sshserver(ui, repo)
2765 s = sshserver.sshserver(ui, repo)
2764 s.serve_forever()
2766 s.serve_forever()
2765
2767
2766 baseui = repo and repo.baseui or ui
2768 baseui = repo and repo.baseui or ui
2767 optlist = ("name templates style address port prefix ipv6"
2769 optlist = ("name templates style address port prefix ipv6"
2768 " accesslog errorlog webdir_conf certificate encoding")
2770 " accesslog errorlog webdir_conf certificate encoding")
2769 for o in optlist.split():
2771 for o in optlist.split():
2770 if opts.get(o, None):
2772 if opts.get(o, None):
2771 baseui.setconfig("web", o, str(opts[o]))
2773 baseui.setconfig("web", o, str(opts[o]))
2772 if (repo is not None) and (repo.ui != baseui):
2774 if (repo is not None) and (repo.ui != baseui):
2773 repo.ui.setconfig("web", o, str(opts[o]))
2775 repo.ui.setconfig("web", o, str(opts[o]))
2774
2776
2775 if repo is None and not ui.config("web", "webdir_conf"):
2777 if repo is None and not ui.config("web", "webdir_conf"):
2776 raise error.RepoError(_("There is no Mercurial repository here"
2778 raise error.RepoError(_("There is no Mercurial repository here"
2777 " (.hg not found)"))
2779 " (.hg not found)"))
2778
2780
2779 class service(object):
2781 class service(object):
2780 def init(self):
2782 def init(self):
2781 util.set_signal_handler()
2783 util.set_signal_handler()
2782 self.httpd = server.create_server(baseui, repo)
2784 self.httpd = server.create_server(baseui, repo)
2783
2785
2784 if not ui.verbose: return
2786 if not ui.verbose: return
2785
2787
2786 if self.httpd.prefix:
2788 if self.httpd.prefix:
2787 prefix = self.httpd.prefix.strip('/') + '/'
2789 prefix = self.httpd.prefix.strip('/') + '/'
2788 else:
2790 else:
2789 prefix = ''
2791 prefix = ''
2790
2792
2791 port = ':%d' % self.httpd.port
2793 port = ':%d' % self.httpd.port
2792 if port == ':80':
2794 if port == ':80':
2793 port = ''
2795 port = ''
2794
2796
2795 bindaddr = self.httpd.addr
2797 bindaddr = self.httpd.addr
2796 if bindaddr == '0.0.0.0':
2798 if bindaddr == '0.0.0.0':
2797 bindaddr = '*'
2799 bindaddr = '*'
2798 elif ':' in bindaddr: # IPv6
2800 elif ':' in bindaddr: # IPv6
2799 bindaddr = '[%s]' % bindaddr
2801 bindaddr = '[%s]' % bindaddr
2800
2802
2801 fqaddr = self.httpd.fqaddr
2803 fqaddr = self.httpd.fqaddr
2802 if ':' in fqaddr:
2804 if ':' in fqaddr:
2803 fqaddr = '[%s]' % fqaddr
2805 fqaddr = '[%s]' % fqaddr
2804 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2806 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2805 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2807 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2806
2808
2807 def run(self):
2809 def run(self):
2808 self.httpd.serve_forever()
2810 self.httpd.serve_forever()
2809
2811
2810 service = service()
2812 service = service()
2811
2813
2812 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2814 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2813
2815
2814 def status(ui, repo, *pats, **opts):
2816 def status(ui, repo, *pats, **opts):
2815 """show changed files in the working directory
2817 """show changed files in the working directory
2816
2818
2817 Show status of files in the repository. If names are given, only
2819 Show status of files in the repository. If names are given, only
2818 files that match are shown. Files that are clean or ignored or
2820 files that match are shown. Files that are clean or ignored or
2819 the source of a copy/move operation, are not listed unless
2821 the source of a copy/move operation, are not listed unless
2820 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2822 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2821 Unless options described with "show only ..." are given, the
2823 Unless options described with "show only ..." are given, the
2822 options -mardu are used.
2824 options -mardu are used.
2823
2825
2824 Option -q/--quiet hides untracked (unknown and ignored) files
2826 Option -q/--quiet hides untracked (unknown and ignored) files
2825 unless explicitly requested with -u/--unknown or -i/--ignored.
2827 unless explicitly requested with -u/--unknown or -i/--ignored.
2826
2828
2827 NOTE: status may appear to disagree with diff if permissions have
2829 NOTE: status may appear to disagree with diff if permissions have
2828 changed or a merge has occurred. The standard diff format does not
2830 changed or a merge has occurred. The standard diff format does not
2829 report permission changes and diff only reports changes relative
2831 report permission changes and diff only reports changes relative
2830 to one merge parent.
2832 to one merge parent.
2831
2833
2832 If one revision is given, it is used as the base revision.
2834 If one revision is given, it is used as the base revision.
2833 If two revisions are given, the differences between them are
2835 If two revisions are given, the differences between them are
2834 shown.
2836 shown.
2835
2837
2836 The codes used to show the status of files are::
2838 The codes used to show the status of files are::
2837
2839
2838 M = modified
2840 M = modified
2839 A = added
2841 A = added
2840 R = removed
2842 R = removed
2841 C = clean
2843 C = clean
2842 ! = missing (deleted by non-hg command, but still tracked)
2844 ! = missing (deleted by non-hg command, but still tracked)
2843 ? = not tracked
2845 ? = not tracked
2844 I = ignored
2846 I = ignored
2845 = origin of the previous file listed as A (added)
2847 = origin of the previous file listed as A (added)
2846 """
2848 """
2847
2849
2848 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2850 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2849 cwd = (pats and repo.getcwd()) or ''
2851 cwd = (pats and repo.getcwd()) or ''
2850 end = opts.get('print0') and '\0' or '\n'
2852 end = opts.get('print0') and '\0' or '\n'
2851 copy = {}
2853 copy = {}
2852 states = 'modified added removed deleted unknown ignored clean'.split()
2854 states = 'modified added removed deleted unknown ignored clean'.split()
2853 show = [k for k in states if opts.get(k)]
2855 show = [k for k in states if opts.get(k)]
2854 if opts.get('all'):
2856 if opts.get('all'):
2855 show += ui.quiet and (states[:4] + ['clean']) or states
2857 show += ui.quiet and (states[:4] + ['clean']) or states
2856 if not show:
2858 if not show:
2857 show = ui.quiet and states[:4] or states[:5]
2859 show = ui.quiet and states[:4] or states[:5]
2858
2860
2859 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2861 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2860 'ignored' in show, 'clean' in show, 'unknown' in show)
2862 'ignored' in show, 'clean' in show, 'unknown' in show)
2861 changestates = zip(states, 'MAR!?IC', stat)
2863 changestates = zip(states, 'MAR!?IC', stat)
2862
2864
2863 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2865 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2864 ctxn = repo[nullid]
2866 ctxn = repo[nullid]
2865 ctx1 = repo[node1]
2867 ctx1 = repo[node1]
2866 ctx2 = repo[node2]
2868 ctx2 = repo[node2]
2867 added = stat[1]
2869 added = stat[1]
2868 if node2 is None:
2870 if node2 is None:
2869 added = stat[0] + stat[1] # merged?
2871 added = stat[0] + stat[1] # merged?
2870
2872
2871 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2873 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2872 if k in added:
2874 if k in added:
2873 copy[k] = v
2875 copy[k] = v
2874 elif v in added:
2876 elif v in added:
2875 copy[v] = k
2877 copy[v] = k
2876
2878
2877 for state, char, files in changestates:
2879 for state, char, files in changestates:
2878 if state in show:
2880 if state in show:
2879 format = "%s %%s%s" % (char, end)
2881 format = "%s %%s%s" % (char, end)
2880 if opts.get('no_status'):
2882 if opts.get('no_status'):
2881 format = "%%s%s" % end
2883 format = "%%s%s" % end
2882
2884
2883 for f in files:
2885 for f in files:
2884 ui.write(format % repo.pathto(f, cwd))
2886 ui.write(format % repo.pathto(f, cwd))
2885 if f in copy:
2887 if f in copy:
2886 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2888 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2887
2889
2888 def summary(ui, repo, **opts):
2890 def summary(ui, repo, **opts):
2889 """summarize working directory state
2891 """summarize working directory state
2890
2892
2891 This generates a brief summary of the working directory state,
2893 This generates a brief summary of the working directory state,
2892 including parents, branch, commit status, and available updates.
2894 including parents, branch, commit status, and available updates.
2893
2895
2894 With the --remote option, this will check the default paths for
2896 With the --remote option, this will check the default paths for
2895 incoming and outgoing changes. This can be time-consuming.
2897 incoming and outgoing changes. This can be time-consuming.
2896 """
2898 """
2897
2899
2898 ctx = repo[None]
2900 ctx = repo[None]
2899 parents = ctx.parents()
2901 parents = ctx.parents()
2900 pnode = parents[0].node()
2902 pnode = parents[0].node()
2901 tags = repo.tags()
2903 tags = repo.tags()
2902
2904
2903 for p in parents:
2905 for p in parents:
2904 t = ' '.join([t for t in tags if tags[t] == p.node()])
2906 t = ' '.join([t for t in tags if tags[t] == p.node()])
2905 if p.rev() == -1:
2907 if p.rev() == -1:
2906 if not len(repo):
2908 if not len(repo):
2907 t += _(' (empty repository)')
2909 t += _(' (empty repository)')
2908 else:
2910 else:
2909 t += _(' (no revision checked out)')
2911 t += _(' (no revision checked out)')
2910 ui.write(_('parent: %d:%s %s\n') % (p.rev(), str(p), t))
2912 ui.write(_('parent: %d:%s %s\n') % (p.rev(), str(p), t))
2911 if p.description():
2913 if p.description():
2912 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
2914 ui.status(' ' + p.description().splitlines()[0].strip() + '\n')
2913
2915
2914 branch = ctx.branch()
2916 branch = ctx.branch()
2915 bheads = repo.branchheads(branch)
2917 bheads = repo.branchheads(branch)
2916 ui.status(_('branch: %s\n') % branch)
2918 ui.status(_('branch: %s\n') % branch)
2917
2919
2918 st = list(repo.status(unknown=True))[:7]
2920 st = list(repo.status(unknown=True))[:7]
2919 ms = merge_.mergestate(repo)
2921 ms = merge_.mergestate(repo)
2920 st.append([f for f in ms if f == 'u'])
2922 st.append([f for f in ms if f == 'u'])
2921 labels = [_('%d modified'), _('%d added'), _('%d removed'),
2923 labels = [_('%d modified'), _('%d added'), _('%d removed'),
2922 _('%d deleted'), _('%d unknown'), _('%d ignored'),
2924 _('%d deleted'), _('%d unknown'), _('%d ignored'),
2923 _('%d unresolved')]
2925 _('%d unresolved')]
2924 t = []
2926 t = []
2925 for s,l in zip(st, labels):
2927 for s,l in zip(st, labels):
2926 if s:
2928 if s:
2927 t.append(l % len(s))
2929 t.append(l % len(s))
2928
2930
2929 t = ', '.join(t)
2931 t = ', '.join(t)
2930
2932
2931 if len(parents) > 1:
2933 if len(parents) > 1:
2932 t += _(' (merge)')
2934 t += _(' (merge)')
2933 elif branch != parents[0].branch():
2935 elif branch != parents[0].branch():
2934 t += _(' (new branch)')
2936 t += _(' (new branch)')
2935 elif (not st[0] and not st[1] and not st[2]):
2937 elif (not st[0] and not st[1] and not st[2]):
2936 t += _(' (clean)')
2938 t += _(' (clean)')
2937 elif pnode not in bheads:
2939 elif pnode not in bheads:
2938 t += _(' (new branch head)')
2940 t += _(' (new branch head)')
2939
2941
2940 if 'clean' in t:
2942 if 'clean' in t:
2941 ui.status(_('commit: %s\n') % t.strip())
2943 ui.status(_('commit: %s\n') % t.strip())
2942 else:
2944 else:
2943 ui.write(_('commit: %s\n') % t.strip())
2945 ui.write(_('commit: %s\n') % t.strip())
2944
2946
2945 # all ancestors of branch heads - all ancestors of parent = new csets
2947 # all ancestors of branch heads - all ancestors of parent = new csets
2946 new = [0] * len(repo)
2948 new = [0] * len(repo)
2947 cl = repo.changelog
2949 cl = repo.changelog
2948 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
2950 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
2949 new[a] = 1
2951 new[a] = 1
2950 for a in cl.ancestors(*[p.rev() for p in parents]):
2952 for a in cl.ancestors(*[p.rev() for p in parents]):
2951 new[a] = 0
2953 new[a] = 0
2952 new = sum(new)
2954 new = sum(new)
2953
2955
2954 if new == 0:
2956 if new == 0:
2955 ui.status(_('update: (current)\n'))
2957 ui.status(_('update: (current)\n'))
2956 elif pnode not in bheads:
2958 elif pnode not in bheads:
2957 ui.write(_('update: %d new changesets (update)\n') % new)
2959 ui.write(_('update: %d new changesets (update)\n') % new)
2958 else:
2960 else:
2959 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
2961 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
2960 (new, len(bheads)))
2962 (new, len(bheads)))
2961
2963
2962 if opts.get('remote'):
2964 if opts.get('remote'):
2963 t = []
2965 t = []
2964 source, revs, checkout = hg.parseurl(ui.expandpath('default'),
2966 source, revs, checkout = hg.parseurl(ui.expandpath('default'),
2965 opts.get('rev'))
2967 opts.get('rev'))
2966 other = hg.repository(cmdutil.remoteui(repo, {}), source)
2968 other = hg.repository(cmdutil.remoteui(repo, {}), source)
2967 ui.debug('comparing with %s\n' % url.hidepassword(source))
2969 ui.debug('comparing with %s\n' % url.hidepassword(source))
2968 repo.ui.pushbuffer()
2970 repo.ui.pushbuffer()
2969 common, incoming, rheads = repo.findcommonincoming(other)
2971 common, incoming, rheads = repo.findcommonincoming(other)
2970 repo.ui.popbuffer()
2972 repo.ui.popbuffer()
2971 if incoming:
2973 if incoming:
2972 t.append(_('1 or more incoming'))
2974 t.append(_('1 or more incoming'))
2973
2975
2974 dest, revs, checkout = hg.parseurl(
2976 dest, revs, checkout = hg.parseurl(
2975 ui.expandpath('default-push', 'default'))
2977 ui.expandpath('default-push', 'default'))
2976 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
2978 other = hg.repository(cmdutil.remoteui(repo, {}), dest)
2977 ui.debug('comparing with %s\n' % url.hidepassword(dest))
2979 ui.debug('comparing with %s\n' % url.hidepassword(dest))
2978 repo.ui.pushbuffer()
2980 repo.ui.pushbuffer()
2979 o = repo.findoutgoing(other)
2981 o = repo.findoutgoing(other)
2980 repo.ui.popbuffer()
2982 repo.ui.popbuffer()
2981 o = repo.changelog.nodesbetween(o, revs)[0]
2983 o = repo.changelog.nodesbetween(o, revs)[0]
2982 if o:
2984 if o:
2983 t.append(_('%d outgoing') % len(o))
2985 t.append(_('%d outgoing') % len(o))
2984
2986
2985 if t:
2987 if t:
2986 ui.write(_('remote: %s\n') % (', '.join(t)))
2988 ui.write(_('remote: %s\n') % (', '.join(t)))
2987 else:
2989 else:
2988 ui.status(_('remote: (synced)\n'))
2990 ui.status(_('remote: (synced)\n'))
2989
2991
2990 def tag(ui, repo, name1, *names, **opts):
2992 def tag(ui, repo, name1, *names, **opts):
2991 """add one or more tags for the current or given revision
2993 """add one or more tags for the current or given revision
2992
2994
2993 Name a particular revision using <name>.
2995 Name a particular revision using <name>.
2994
2996
2995 Tags are used to name particular revisions of the repository and are
2997 Tags are used to name particular revisions of the repository and are
2996 very useful to compare different revisions, to go back to significant
2998 very useful to compare different revisions, to go back to significant
2997 earlier versions or to mark branch points as releases, etc.
2999 earlier versions or to mark branch points as releases, etc.
2998
3000
2999 If no revision is given, the parent of the working directory is
3001 If no revision is given, the parent of the working directory is
3000 used, or tip if no revision is checked out.
3002 used, or tip if no revision is checked out.
3001
3003
3002 To facilitate version control, distribution, and merging of tags,
3004 To facilitate version control, distribution, and merging of tags,
3003 they are stored as a file named ".hgtags" which is managed
3005 they are stored as a file named ".hgtags" which is managed
3004 similarly to other project files and can be hand-edited if
3006 similarly to other project files and can be hand-edited if
3005 necessary. The file '.hg/localtags' is used for local tags (not
3007 necessary. The file '.hg/localtags' is used for local tags (not
3006 shared among repositories).
3008 shared among repositories).
3007
3009
3008 See 'hg help dates' for a list of formats valid for -d/--date.
3010 See 'hg help dates' for a list of formats valid for -d/--date.
3009 """
3011 """
3010
3012
3011 rev_ = "."
3013 rev_ = "."
3012 names = (name1,) + names
3014 names = (name1,) + names
3013 if len(names) != len(set(names)):
3015 if len(names) != len(set(names)):
3014 raise util.Abort(_('tag names must be unique'))
3016 raise util.Abort(_('tag names must be unique'))
3015 for n in names:
3017 for n in names:
3016 if n in ['tip', '.', 'null']:
3018 if n in ['tip', '.', 'null']:
3017 raise util.Abort(_('the name \'%s\' is reserved') % n)
3019 raise util.Abort(_('the name \'%s\' is reserved') % n)
3018 if opts.get('rev') and opts.get('remove'):
3020 if opts.get('rev') and opts.get('remove'):
3019 raise util.Abort(_("--rev and --remove are incompatible"))
3021 raise util.Abort(_("--rev and --remove are incompatible"))
3020 if opts.get('rev'):
3022 if opts.get('rev'):
3021 rev_ = opts['rev']
3023 rev_ = opts['rev']
3022 message = opts.get('message')
3024 message = opts.get('message')
3023 if opts.get('remove'):
3025 if opts.get('remove'):
3024 expectedtype = opts.get('local') and 'local' or 'global'
3026 expectedtype = opts.get('local') and 'local' or 'global'
3025 for n in names:
3027 for n in names:
3026 if not repo.tagtype(n):
3028 if not repo.tagtype(n):
3027 raise util.Abort(_('tag \'%s\' does not exist') % n)
3029 raise util.Abort(_('tag \'%s\' does not exist') % n)
3028 if repo.tagtype(n) != expectedtype:
3030 if repo.tagtype(n) != expectedtype:
3029 if expectedtype == 'global':
3031 if expectedtype == 'global':
3030 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3032 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3031 else:
3033 else:
3032 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3034 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3033 rev_ = nullid
3035 rev_ = nullid
3034 if not message:
3036 if not message:
3035 # we don't translate commit messages
3037 # we don't translate commit messages
3036 message = 'Removed tag %s' % ', '.join(names)
3038 message = 'Removed tag %s' % ', '.join(names)
3037 elif not opts.get('force'):
3039 elif not opts.get('force'):
3038 for n in names:
3040 for n in names:
3039 if n in repo.tags():
3041 if n in repo.tags():
3040 raise util.Abort(_('tag \'%s\' already exists '
3042 raise util.Abort(_('tag \'%s\' already exists '
3041 '(use -f to force)') % n)
3043 '(use -f to force)') % n)
3042 if not rev_ and repo.dirstate.parents()[1] != nullid:
3044 if not rev_ and repo.dirstate.parents()[1] != nullid:
3043 raise util.Abort(_('uncommitted merge - please provide a '
3045 raise util.Abort(_('uncommitted merge - please provide a '
3044 'specific revision'))
3046 'specific revision'))
3045 r = repo[rev_].node()
3047 r = repo[rev_].node()
3046
3048
3047 if not message:
3049 if not message:
3048 # we don't translate commit messages
3050 # we don't translate commit messages
3049 message = ('Added tag %s for changeset %s' %
3051 message = ('Added tag %s for changeset %s' %
3050 (', '.join(names), short(r)))
3052 (', '.join(names), short(r)))
3051
3053
3052 date = opts.get('date')
3054 date = opts.get('date')
3053 if date:
3055 if date:
3054 date = util.parsedate(date)
3056 date = util.parsedate(date)
3055
3057
3056 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3058 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3057
3059
3058 def tags(ui, repo):
3060 def tags(ui, repo):
3059 """list repository tags
3061 """list repository tags
3060
3062
3061 This lists both regular and local tags. When the -v/--verbose
3063 This lists both regular and local tags. When the -v/--verbose
3062 switch is used, a third column "local" is printed for local tags.
3064 switch is used, a third column "local" is printed for local tags.
3063 """
3065 """
3064
3066
3065 hexfunc = ui.debugflag and hex or short
3067 hexfunc = ui.debugflag and hex or short
3066 tagtype = ""
3068 tagtype = ""
3067
3069
3068 for t, n in reversed(repo.tagslist()):
3070 for t, n in reversed(repo.tagslist()):
3069 if ui.quiet:
3071 if ui.quiet:
3070 ui.write("%s\n" % t)
3072 ui.write("%s\n" % t)
3071 continue
3073 continue
3072
3074
3073 try:
3075 try:
3074 hn = hexfunc(n)
3076 hn = hexfunc(n)
3075 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3077 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3076 except error.LookupError:
3078 except error.LookupError:
3077 r = " ?:%s" % hn
3079 r = " ?:%s" % hn
3078 else:
3080 else:
3079 spaces = " " * (30 - encoding.colwidth(t))
3081 spaces = " " * (30 - encoding.colwidth(t))
3080 if ui.verbose:
3082 if ui.verbose:
3081 if repo.tagtype(t) == 'local':
3083 if repo.tagtype(t) == 'local':
3082 tagtype = " local"
3084 tagtype = " local"
3083 else:
3085 else:
3084 tagtype = ""
3086 tagtype = ""
3085 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3087 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3086
3088
3087 def tip(ui, repo, **opts):
3089 def tip(ui, repo, **opts):
3088 """show the tip revision
3090 """show the tip revision
3089
3091
3090 The tip revision (usually just called the tip) is the changeset
3092 The tip revision (usually just called the tip) is the changeset
3091 most recently added to the repository (and therefore the most
3093 most recently added to the repository (and therefore the most
3092 recently changed head).
3094 recently changed head).
3093
3095
3094 If you have just made a commit, that commit will be the tip. If
3096 If you have just made a commit, that commit will be the tip. If
3095 you have just pulled changes from another repository, the tip of
3097 you have just pulled changes from another repository, the tip of
3096 that repository becomes the current tip. The "tip" tag is special
3098 that repository becomes the current tip. The "tip" tag is special
3097 and cannot be renamed or assigned to a different changeset.
3099 and cannot be renamed or assigned to a different changeset.
3098 """
3100 """
3099 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
3101 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
3100
3102
3101 def unbundle(ui, repo, fname1, *fnames, **opts):
3103 def unbundle(ui, repo, fname1, *fnames, **opts):
3102 """apply one or more changegroup files
3104 """apply one or more changegroup files
3103
3105
3104 Apply one or more compressed changegroup files generated by the
3106 Apply one or more compressed changegroup files generated by the
3105 bundle command.
3107 bundle command.
3106 """
3108 """
3107 fnames = (fname1,) + fnames
3109 fnames = (fname1,) + fnames
3108
3110
3109 lock = repo.lock()
3111 lock = repo.lock()
3110 try:
3112 try:
3111 for fname in fnames:
3113 for fname in fnames:
3112 f = url.open(ui, fname)
3114 f = url.open(ui, fname)
3113 gen = changegroup.readbundle(f, fname)
3115 gen = changegroup.readbundle(f, fname)
3114 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3116 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
3115 finally:
3117 finally:
3116 lock.release()
3118 lock.release()
3117
3119
3118 return postincoming(ui, repo, modheads, opts.get('update'), None)
3120 return postincoming(ui, repo, modheads, opts.get('update'), None)
3119
3121
3120 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3122 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3121 """update working directory
3123 """update working directory
3122
3124
3123 Update the repository's working directory to the specified
3125 Update the repository's working directory to the specified
3124 revision, or the tip of the current branch if none is specified.
3126 revision, or the tip of the current branch if none is specified.
3125 Use null as the revision to remove the working copy (like 'hg
3127 Use null as the revision to remove the working copy (like 'hg
3126 clone -U').
3128 clone -U').
3127
3129
3128 When the working directory contains no uncommitted changes, it
3130 When the working directory contains no uncommitted changes, it
3129 will be replaced by the state of the requested revision from the
3131 will be replaced by the state of the requested revision from the
3130 repository. When the requested revision is on a different branch,
3132 repository. When the requested revision is on a different branch,
3131 the working directory will additionally be switched to that
3133 the working directory will additionally be switched to that
3132 branch.
3134 branch.
3133
3135
3134 When there are uncommitted changes, use option -C/--clean to
3136 When there are uncommitted changes, use option -C/--clean to
3135 discard them, forcibly replacing the state of the working
3137 discard them, forcibly replacing the state of the working
3136 directory with the requested revision. Alternately, use -c/--check
3138 directory with the requested revision. Alternately, use -c/--check
3137 to abort.
3139 to abort.
3138
3140
3139 When there are uncommitted changes and option -C/--clean is not
3141 When there are uncommitted changes and option -C/--clean is not
3140 used, and the parent revision and requested revision are on the
3142 used, and the parent revision and requested revision are on the
3141 same branch, and one of them is an ancestor of the other, then the
3143 same branch, and one of them is an ancestor of the other, then the
3142 new working directory will contain the requested revision merged
3144 new working directory will contain the requested revision merged
3143 with the uncommitted changes. Otherwise, the update will fail with
3145 with the uncommitted changes. Otherwise, the update will fail with
3144 a suggestion to use 'merge' or 'update -C' instead.
3146 a suggestion to use 'merge' or 'update -C' instead.
3145
3147
3146 If you want to update just one file to an older revision, use
3148 If you want to update just one file to an older revision, use
3147 revert.
3149 revert.
3148
3150
3149 See 'hg help dates' for a list of formats valid for -d/--date.
3151 See 'hg help dates' for a list of formats valid for -d/--date.
3150 """
3152 """
3151 if rev and node:
3153 if rev and node:
3152 raise util.Abort(_("please specify just one revision"))
3154 raise util.Abort(_("please specify just one revision"))
3153
3155
3154 if not rev:
3156 if not rev:
3155 rev = node
3157 rev = node
3156
3158
3157 if check and clean:
3159 if check and clean:
3158 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3160 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3159
3161
3160 if check:
3162 if check:
3161 # we could use dirty() but we can ignore merge and branch trivia
3163 # we could use dirty() but we can ignore merge and branch trivia
3162 c = repo[None]
3164 c = repo[None]
3163 if c.modified() or c.added() or c.removed():
3165 if c.modified() or c.added() or c.removed():
3164 raise util.Abort(_("uncommitted local changes"))
3166 raise util.Abort(_("uncommitted local changes"))
3165
3167
3166 if date:
3168 if date:
3167 if rev:
3169 if rev:
3168 raise util.Abort(_("you can't specify a revision and a date"))
3170 raise util.Abort(_("you can't specify a revision and a date"))
3169 rev = cmdutil.finddate(ui, repo, date)
3171 rev = cmdutil.finddate(ui, repo, date)
3170
3172
3171 if clean or check:
3173 if clean or check:
3172 return hg.clean(repo, rev)
3174 return hg.clean(repo, rev)
3173 else:
3175 else:
3174 return hg.update(repo, rev)
3176 return hg.update(repo, rev)
3175
3177
3176 def verify(ui, repo):
3178 def verify(ui, repo):
3177 """verify the integrity of the repository
3179 """verify the integrity of the repository
3178
3180
3179 Verify the integrity of the current repository.
3181 Verify the integrity of the current repository.
3180
3182
3181 This will perform an extensive check of the repository's
3183 This will perform an extensive check of the repository's
3182 integrity, validating the hashes and checksums of each entry in
3184 integrity, validating the hashes and checksums of each entry in
3183 the changelog, manifest, and tracked files, as well as the
3185 the changelog, manifest, and tracked files, as well as the
3184 integrity of their crosslinks and indices.
3186 integrity of their crosslinks and indices.
3185 """
3187 """
3186 return hg.verify(repo)
3188 return hg.verify(repo)
3187
3189
3188 def version_(ui):
3190 def version_(ui):
3189 """output version and copyright information"""
3191 """output version and copyright information"""
3190 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3192 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3191 % util.version())
3193 % util.version())
3192 ui.status(_(
3194 ui.status(_(
3193 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
3195 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
3194 "This is free software; see the source for copying conditions. "
3196 "This is free software; see the source for copying conditions. "
3195 "There is NO\nwarranty; "
3197 "There is NO\nwarranty; "
3196 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3198 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3197 ))
3199 ))
3198
3200
3199 # Command options and aliases are listed here, alphabetically
3201 # Command options and aliases are listed here, alphabetically
3200
3202
3201 globalopts = [
3203 globalopts = [
3202 ('R', 'repository', '',
3204 ('R', 'repository', '',
3203 _('repository root directory or name of overlay bundle file')),
3205 _('repository root directory or name of overlay bundle file')),
3204 ('', 'cwd', '', _('change working directory')),
3206 ('', 'cwd', '', _('change working directory')),
3205 ('y', 'noninteractive', None,
3207 ('y', 'noninteractive', None,
3206 _('do not prompt, assume \'yes\' for any required answers')),
3208 _('do not prompt, assume \'yes\' for any required answers')),
3207 ('q', 'quiet', None, _('suppress output')),
3209 ('q', 'quiet', None, _('suppress output')),
3208 ('v', 'verbose', None, _('enable additional output')),
3210 ('v', 'verbose', None, _('enable additional output')),
3209 ('', 'config', [], _('set/override config option')),
3211 ('', 'config', [], _('set/override config option')),
3210 ('', 'debug', None, _('enable debugging output')),
3212 ('', 'debug', None, _('enable debugging output')),
3211 ('', 'debugger', None, _('start debugger')),
3213 ('', 'debugger', None, _('start debugger')),
3212 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3214 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3213 ('', 'encodingmode', encoding.encodingmode,
3215 ('', 'encodingmode', encoding.encodingmode,
3214 _('set the charset encoding mode')),
3216 _('set the charset encoding mode')),
3215 ('', 'traceback', None, _('print traceback on exception')),
3217 ('', 'traceback', None, _('print traceback on exception')),
3216 ('', 'time', None, _('time how long the command takes')),
3218 ('', 'time', None, _('time how long the command takes')),
3217 ('', 'profile', None, _('print command execution profile')),
3219 ('', 'profile', None, _('print command execution profile')),
3218 ('', 'version', None, _('output version information and exit')),
3220 ('', 'version', None, _('output version information and exit')),
3219 ('h', 'help', None, _('display help and exit')),
3221 ('h', 'help', None, _('display help and exit')),
3220 ]
3222 ]
3221
3223
3222 dryrunopts = [('n', 'dry-run', None,
3224 dryrunopts = [('n', 'dry-run', None,
3223 _('do not perform actions, just print output'))]
3225 _('do not perform actions, just print output'))]
3224
3226
3225 remoteopts = [
3227 remoteopts = [
3226 ('e', 'ssh', '', _('specify ssh command to use')),
3228 ('e', 'ssh', '', _('specify ssh command to use')),
3227 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3229 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3228 ]
3230 ]
3229
3231
3230 walkopts = [
3232 walkopts = [
3231 ('I', 'include', [], _('include names matching the given patterns')),
3233 ('I', 'include', [], _('include names matching the given patterns')),
3232 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3234 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3233 ]
3235 ]
3234
3236
3235 commitopts = [
3237 commitopts = [
3236 ('m', 'message', '', _('use <text> as commit message')),
3238 ('m', 'message', '', _('use <text> as commit message')),
3237 ('l', 'logfile', '', _('read commit message from <file>')),
3239 ('l', 'logfile', '', _('read commit message from <file>')),
3238 ]
3240 ]
3239
3241
3240 commitopts2 = [
3242 commitopts2 = [
3241 ('d', 'date', '', _('record datecode as commit date')),
3243 ('d', 'date', '', _('record datecode as commit date')),
3242 ('u', 'user', '', _('record the specified user as committer')),
3244 ('u', 'user', '', _('record the specified user as committer')),
3243 ]
3245 ]
3244
3246
3245 templateopts = [
3247 templateopts = [
3246 ('', 'style', '', _('display using template map file')),
3248 ('', 'style', '', _('display using template map file')),
3247 ('', 'template', '', _('display with template')),
3249 ('', 'template', '', _('display with template')),
3248 ]
3250 ]
3249
3251
3250 logopts = [
3252 logopts = [
3251 ('p', 'patch', None, _('show patch')),
3253 ('p', 'patch', None, _('show patch')),
3252 ('g', 'git', None, _('use git extended diff format')),
3254 ('g', 'git', None, _('use git extended diff format')),
3253 ('l', 'limit', '', _('limit number of changes displayed')),
3255 ('l', 'limit', '', _('limit number of changes displayed')),
3254 ('M', 'no-merges', None, _('do not show merges')),
3256 ('M', 'no-merges', None, _('do not show merges')),
3255 ] + templateopts
3257 ] + templateopts
3256
3258
3257 diffopts = [
3259 diffopts = [
3258 ('a', 'text', None, _('treat all files as text')),
3260 ('a', 'text', None, _('treat all files as text')),
3259 ('g', 'git', None, _('use git extended diff format')),
3261 ('g', 'git', None, _('use git extended diff format')),
3260 ('', 'nodates', None, _("don't include dates in diff headers"))
3262 ('', 'nodates', None, _("don't include dates in diff headers"))
3261 ]
3263 ]
3262
3264
3263 diffopts2 = [
3265 diffopts2 = [
3264 ('p', 'show-function', None, _('show which function each change is in')),
3266 ('p', 'show-function', None, _('show which function each change is in')),
3265 ('w', 'ignore-all-space', None,
3267 ('w', 'ignore-all-space', None,
3266 _('ignore white space when comparing lines')),
3268 _('ignore white space when comparing lines')),
3267 ('b', 'ignore-space-change', None,
3269 ('b', 'ignore-space-change', None,
3268 _('ignore changes in the amount of white space')),
3270 _('ignore changes in the amount of white space')),
3269 ('B', 'ignore-blank-lines', None,
3271 ('B', 'ignore-blank-lines', None,
3270 _('ignore changes whose lines are all blank')),
3272 _('ignore changes whose lines are all blank')),
3271 ('U', 'unified', '', _('number of lines of context to show')),
3273 ('U', 'unified', '', _('number of lines of context to show')),
3272 ('', 'stat', None, _('output diffstat-style summary of changes')),
3274 ('', 'stat', None, _('output diffstat-style summary of changes')),
3273 ]
3275 ]
3274
3276
3275 similarityopts = [
3277 similarityopts = [
3276 ('s', 'similarity', '',
3278 ('s', 'similarity', '',
3277 _('guess renamed files by similarity (0<=s<=100)'))
3279 _('guess renamed files by similarity (0<=s<=100)'))
3278 ]
3280 ]
3279
3281
3280 table = {
3282 table = {
3281 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3283 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3282 "addremove":
3284 "addremove":
3283 (addremove, similarityopts + walkopts + dryrunopts,
3285 (addremove, similarityopts + walkopts + dryrunopts,
3284 _('[OPTION]... [FILE]...')),
3286 _('[OPTION]... [FILE]...')),
3285 "^annotate|blame":
3287 "^annotate|blame":
3286 (annotate,
3288 (annotate,
3287 [('r', 'rev', '', _('annotate the specified revision')),
3289 [('r', 'rev', '', _('annotate the specified revision')),
3288 ('f', 'follow', None, _('follow file copies and renames')),
3290 ('f', 'follow', None, _('follow file copies and renames')),
3289 ('a', 'text', None, _('treat all files as text')),
3291 ('a', 'text', None, _('treat all files as text')),
3290 ('u', 'user', None, _('list the author (long with -v)')),
3292 ('u', 'user', None, _('list the author (long with -v)')),
3291 ('d', 'date', None, _('list the date (short with -q)')),
3293 ('d', 'date', None, _('list the date (short with -q)')),
3292 ('n', 'number', None, _('list the revision number (default)')),
3294 ('n', 'number', None, _('list the revision number (default)')),
3293 ('c', 'changeset', None, _('list the changeset')),
3295 ('c', 'changeset', None, _('list the changeset')),
3294 ('l', 'line-number', None,
3296 ('l', 'line-number', None,
3295 _('show line number at the first appearance'))
3297 _('show line number at the first appearance'))
3296 ] + walkopts,
3298 ] + walkopts,
3297 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3299 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3298 "archive":
3300 "archive":
3299 (archive,
3301 (archive,
3300 [('', 'no-decode', None, _('do not pass files through decoders')),
3302 [('', 'no-decode', None, _('do not pass files through decoders')),
3301 ('p', 'prefix', '', _('directory prefix for files in archive')),
3303 ('p', 'prefix', '', _('directory prefix for files in archive')),
3302 ('r', 'rev', '', _('revision to distribute')),
3304 ('r', 'rev', '', _('revision to distribute')),
3303 ('t', 'type', '', _('type of distribution to create')),
3305 ('t', 'type', '', _('type of distribution to create')),
3304 ] + walkopts,
3306 ] + walkopts,
3305 _('[OPTION]... DEST')),
3307 _('[OPTION]... DEST')),
3306 "backout":
3308 "backout":
3307 (backout,
3309 (backout,
3308 [('', 'merge', None,
3310 [('', 'merge', None,
3309 _('merge with old dirstate parent after backout')),
3311 _('merge with old dirstate parent after backout')),
3310 ('', 'parent', '', _('parent to choose when backing out merge')),
3312 ('', 'parent', '', _('parent to choose when backing out merge')),
3311 ('r', 'rev', '', _('revision to backout')),
3313 ('r', 'rev', '', _('revision to backout')),
3312 ] + walkopts + commitopts + commitopts2,
3314 ] + walkopts + commitopts + commitopts2,
3313 _('[OPTION]... [-r] REV')),
3315 _('[OPTION]... [-r] REV')),
3314 "bisect":
3316 "bisect":
3315 (bisect,
3317 (bisect,
3316 [('r', 'reset', False, _('reset bisect state')),
3318 [('r', 'reset', False, _('reset bisect state')),
3317 ('g', 'good', False, _('mark changeset good')),
3319 ('g', 'good', False, _('mark changeset good')),
3318 ('b', 'bad', False, _('mark changeset bad')),
3320 ('b', 'bad', False, _('mark changeset bad')),
3319 ('s', 'skip', False, _('skip testing changeset')),
3321 ('s', 'skip', False, _('skip testing changeset')),
3320 ('c', 'command', '', _('use command to check changeset state')),
3322 ('c', 'command', '', _('use command to check changeset state')),
3321 ('U', 'noupdate', False, _('do not update to target'))],
3323 ('U', 'noupdate', False, _('do not update to target'))],
3322 _("[-gbsr] [-c CMD] [REV]")),
3324 _("[-gbsr] [-c CMD] [REV]")),
3323 "branch":
3325 "branch":
3324 (branch,
3326 (branch,
3325 [('f', 'force', None,
3327 [('f', 'force', None,
3326 _('set branch name even if it shadows an existing branch')),
3328 _('set branch name even if it shadows an existing branch')),
3327 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3329 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3328 _('[-fC] [NAME]')),
3330 _('[-fC] [NAME]')),
3329 "branches":
3331 "branches":
3330 (branches,
3332 (branches,
3331 [('a', 'active', False,
3333 [('a', 'active', False,
3332 _('show only branches that have unmerged heads')),
3334 _('show only branches that have unmerged heads')),
3333 ('c', 'closed', False,
3335 ('c', 'closed', False,
3334 _('show normal and closed branches'))],
3336 _('show normal and closed branches'))],
3335 _('[-a]')),
3337 _('[-a]')),
3336 "bundle":
3338 "bundle":
3337 (bundle,
3339 (bundle,
3338 [('f', 'force', None,
3340 [('f', 'force', None,
3339 _('run even when remote repository is unrelated')),
3341 _('run even when remote repository is unrelated')),
3340 ('r', 'rev', [],
3342 ('r', 'rev', [],
3341 _('a changeset up to which you would like to bundle')),
3343 _('a changeset up to which you would like to bundle')),
3342 ('', 'base', [],
3344 ('', 'base', [],
3343 _('a base changeset to specify instead of a destination')),
3345 _('a base changeset to specify instead of a destination')),
3344 ('a', 'all', None, _('bundle all changesets in the repository')),
3346 ('a', 'all', None, _('bundle all changesets in the repository')),
3345 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3347 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3346 ] + remoteopts,
3348 ] + remoteopts,
3347 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3349 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3348 "cat":
3350 "cat":
3349 (cat,
3351 (cat,
3350 [('o', 'output', '', _('print output to file with formatted name')),
3352 [('o', 'output', '', _('print output to file with formatted name')),
3351 ('r', 'rev', '', _('print the given revision')),
3353 ('r', 'rev', '', _('print the given revision')),
3352 ('', 'decode', None, _('apply any matching decode filter')),
3354 ('', 'decode', None, _('apply any matching decode filter')),
3353 ] + walkopts,
3355 ] + walkopts,
3354 _('[OPTION]... FILE...')),
3356 _('[OPTION]... FILE...')),
3355 "^clone":
3357 "^clone":
3356 (clone,
3358 (clone,
3357 [('U', 'noupdate', None,
3359 [('U', 'noupdate', None,
3358 _('the clone will only contain a repository (no working copy)')),
3360 _('the clone will only contain a repository (no working copy)')),
3359 ('r', 'rev', [],
3361 ('r', 'rev', [],
3360 _('a changeset you would like to have after cloning')),
3362 _('a changeset you would like to have after cloning')),
3361 ('', 'pull', None, _('use pull protocol to copy metadata')),
3363 ('', 'pull', None, _('use pull protocol to copy metadata')),
3362 ('', 'uncompressed', None,
3364 ('', 'uncompressed', None,
3363 _('use uncompressed transfer (fast over LAN)')),
3365 _('use uncompressed transfer (fast over LAN)')),
3364 ] + remoteopts,
3366 ] + remoteopts,
3365 _('[OPTION]... SOURCE [DEST]')),
3367 _('[OPTION]... SOURCE [DEST]')),
3366 "^commit|ci":
3368 "^commit|ci":
3367 (commit,
3369 (commit,
3368 [('A', 'addremove', None,
3370 [('A', 'addremove', None,
3369 _('mark new/missing files as added/removed before committing')),
3371 _('mark new/missing files as added/removed before committing')),
3370 ('', 'close-branch', None,
3372 ('', 'close-branch', None,
3371 _('mark a branch as closed, hiding it from the branch list')),
3373 _('mark a branch as closed, hiding it from the branch list')),
3372 ] + walkopts + commitopts + commitopts2,
3374 ] + walkopts + commitopts + commitopts2,
3373 _('[OPTION]... [FILE]...')),
3375 _('[OPTION]... [FILE]...')),
3374 "copy|cp":
3376 "copy|cp":
3375 (copy,
3377 (copy,
3376 [('A', 'after', None, _('record a copy that has already occurred')),
3378 [('A', 'after', None, _('record a copy that has already occurred')),
3377 ('f', 'force', None,
3379 ('f', 'force', None,
3378 _('forcibly copy over an existing managed file')),
3380 _('forcibly copy over an existing managed file')),
3379 ] + walkopts + dryrunopts,
3381 ] + walkopts + dryrunopts,
3380 _('[OPTION]... [SOURCE]... DEST')),
3382 _('[OPTION]... [SOURCE]... DEST')),
3381 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3383 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3382 "debugcheckstate": (debugcheckstate, [], ''),
3384 "debugcheckstate": (debugcheckstate, [], ''),
3383 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3385 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3384 "debugcomplete":
3386 "debugcomplete":
3385 (debugcomplete,
3387 (debugcomplete,
3386 [('o', 'options', None, _('show the command options'))],
3388 [('o', 'options', None, _('show the command options'))],
3387 _('[-o] CMD')),
3389 _('[-o] CMD')),
3388 "debugdate":
3390 "debugdate":
3389 (debugdate,
3391 (debugdate,
3390 [('e', 'extended', None, _('try extended date formats'))],
3392 [('e', 'extended', None, _('try extended date formats'))],
3391 _('[-e] DATE [RANGE]')),
3393 _('[-e] DATE [RANGE]')),
3392 "debugdata": (debugdata, [], _('FILE REV')),
3394 "debugdata": (debugdata, [], _('FILE REV')),
3393 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3395 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3394 "debugindex": (debugindex, [], _('FILE')),
3396 "debugindex": (debugindex, [], _('FILE')),
3395 "debugindexdot": (debugindexdot, [], _('FILE')),
3397 "debugindexdot": (debugindexdot, [], _('FILE')),
3396 "debuginstall": (debuginstall, [], ''),
3398 "debuginstall": (debuginstall, [], ''),
3397 "debugrebuildstate":
3399 "debugrebuildstate":
3398 (debugrebuildstate,
3400 (debugrebuildstate,
3399 [('r', 'rev', '', _('revision to rebuild to'))],
3401 [('r', 'rev', '', _('revision to rebuild to'))],
3400 _('[-r REV] [REV]')),
3402 _('[-r REV] [REV]')),
3401 "debugrename":
3403 "debugrename":
3402 (debugrename,
3404 (debugrename,
3403 [('r', 'rev', '', _('revision to debug'))],
3405 [('r', 'rev', '', _('revision to debug'))],
3404 _('[-r REV] FILE')),
3406 _('[-r REV] FILE')),
3405 "debugsetparents":
3407 "debugsetparents":
3406 (debugsetparents, [], _('REV1 [REV2]')),
3408 (debugsetparents, [], _('REV1 [REV2]')),
3407 "debugstate":
3409 "debugstate":
3408 (debugstate,
3410 (debugstate,
3409 [('', 'nodates', None, _('do not display the saved mtime'))],
3411 [('', 'nodates', None, _('do not display the saved mtime'))],
3410 _('[OPTION]...')),
3412 _('[OPTION]...')),
3411 "debugsub":
3413 "debugsub":
3412 (debugsub,
3414 (debugsub,
3413 [('r', 'rev', '', _('revision to check'))],
3415 [('r', 'rev', '', _('revision to check'))],
3414 _('[-r REV] [REV]')),
3416 _('[-r REV] [REV]')),
3415 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3417 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3416 "^diff":
3418 "^diff":
3417 (diff,
3419 (diff,
3418 [('r', 'rev', [], _('revision')),
3420 [('r', 'rev', [], _('revision')),
3419 ('c', 'change', '', _('change made by revision'))
3421 ('c', 'change', '', _('change made by revision'))
3420 ] + diffopts + diffopts2 + walkopts,
3422 ] + diffopts + diffopts2 + walkopts,
3421 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3423 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3422 "^export":
3424 "^export":
3423 (export,
3425 (export,
3424 [('o', 'output', '', _('print output to file with formatted name')),
3426 [('o', 'output', '', _('print output to file with formatted name')),
3425 ('', 'switch-parent', None, _('diff against the second parent'))
3427 ('', 'switch-parent', None, _('diff against the second parent'))
3426 ] + diffopts,
3428 ] + diffopts,
3427 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3429 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3428 "^forget":
3430 "^forget":
3429 (forget,
3431 (forget,
3430 [] + walkopts,
3432 [] + walkopts,
3431 _('[OPTION]... FILE...')),
3433 _('[OPTION]... FILE...')),
3432 "grep":
3434 "grep":
3433 (grep,
3435 (grep,
3434 [('0', 'print0', None, _('end fields with NUL')),
3436 [('0', 'print0', None, _('end fields with NUL')),
3435 ('', 'all', None, _('print all revisions that match')),
3437 ('', 'all', None, _('print all revisions that match')),
3436 ('f', 'follow', None,
3438 ('f', 'follow', None,
3437 _('follow changeset history, or file history across copies and renames')),
3439 _('follow changeset history, or file history across copies and renames')),
3438 ('i', 'ignore-case', None, _('ignore case when matching')),
3440 ('i', 'ignore-case', None, _('ignore case when matching')),
3439 ('l', 'files-with-matches', None,
3441 ('l', 'files-with-matches', None,
3440 _('print only filenames and revisions that match')),
3442 _('print only filenames and revisions that match')),
3441 ('n', 'line-number', None, _('print matching line numbers')),
3443 ('n', 'line-number', None, _('print matching line numbers')),
3442 ('r', 'rev', [], _('search in given revision range')),
3444 ('r', 'rev', [], _('search in given revision range')),
3443 ('u', 'user', None, _('list the author (long with -v)')),
3445 ('u', 'user', None, _('list the author (long with -v)')),
3444 ('d', 'date', None, _('list the date (short with -q)')),
3446 ('d', 'date', None, _('list the date (short with -q)')),
3445 ] + walkopts,
3447 ] + walkopts,
3446 _('[OPTION]... PATTERN [FILE]...')),
3448 _('[OPTION]... PATTERN [FILE]...')),
3447 "heads":
3449 "heads":
3448 (heads,
3450 (heads,
3449 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3451 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3450 ('a', 'active', False,
3452 ('a', 'active', False,
3451 _('show only the active branch heads from open branches')),
3453 _('show only the active branch heads from open branches')),
3452 ('c', 'closed', False,
3454 ('c', 'closed', False,
3453 _('show normal and closed branch heads')),
3455 _('show normal and closed branch heads')),
3454 ] + templateopts,
3456 ] + templateopts,
3455 _('[-r STARTREV] [REV]...')),
3457 _('[-r STARTREV] [REV]...')),
3456 "help": (help_, [], _('[TOPIC]')),
3458 "help": (help_, [], _('[TOPIC]')),
3457 "identify|id":
3459 "identify|id":
3458 (identify,
3460 (identify,
3459 [('r', 'rev', '', _('identify the specified revision')),
3461 [('r', 'rev', '', _('identify the specified revision')),
3460 ('n', 'num', None, _('show local revision number')),
3462 ('n', 'num', None, _('show local revision number')),
3461 ('i', 'id', None, _('show global revision id')),
3463 ('i', 'id', None, _('show global revision id')),
3462 ('b', 'branch', None, _('show branch')),
3464 ('b', 'branch', None, _('show branch')),
3463 ('t', 'tags', None, _('show tags'))],
3465 ('t', 'tags', None, _('show tags'))],
3464 _('[-nibt] [-r REV] [SOURCE]')),
3466 _('[-nibt] [-r REV] [SOURCE]')),
3465 "import|patch":
3467 "import|patch":
3466 (import_,
3468 (import_,
3467 [('p', 'strip', 1,
3469 [('p', 'strip', 1,
3468 _('directory strip option for patch. This has the same '
3470 _('directory strip option for patch. This has the same '
3469 'meaning as the corresponding patch option')),
3471 'meaning as the corresponding patch option')),
3470 ('b', 'base', '', _('base path')),
3472 ('b', 'base', '', _('base path')),
3471 ('f', 'force', None,
3473 ('f', 'force', None,
3472 _('skip check for outstanding uncommitted changes')),
3474 _('skip check for outstanding uncommitted changes')),
3473 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3475 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3474 ('', 'exact', None,
3476 ('', 'exact', None,
3475 _('apply patch to the nodes from which it was generated')),
3477 _('apply patch to the nodes from which it was generated')),
3476 ('', 'import-branch', None,
3478 ('', 'import-branch', None,
3477 _('use any branch information in patch (implied by --exact)'))] +
3479 _('use any branch information in patch (implied by --exact)'))] +
3478 commitopts + commitopts2 + similarityopts,
3480 commitopts + commitopts2 + similarityopts,
3479 _('[OPTION]... PATCH...')),
3481 _('[OPTION]... PATCH...')),
3480 "incoming|in":
3482 "incoming|in":
3481 (incoming,
3483 (incoming,
3482 [('f', 'force', None,
3484 [('f', 'force', None,
3483 _('run even when remote repository is unrelated')),
3485 _('run even when remote repository is unrelated')),
3484 ('n', 'newest-first', None, _('show newest record first')),
3486 ('n', 'newest-first', None, _('show newest record first')),
3485 ('', 'bundle', '', _('file to store the bundles into')),
3487 ('', 'bundle', '', _('file to store the bundles into')),
3486 ('r', 'rev', [],
3488 ('r', 'rev', [],
3487 _('a specific revision up to which you would like to pull')),
3489 _('a specific revision up to which you would like to pull')),
3488 ] + logopts + remoteopts,
3490 ] + logopts + remoteopts,
3489 _('[-p] [-n] [-M] [-f] [-r REV]...'
3491 _('[-p] [-n] [-M] [-f] [-r REV]...'
3490 ' [--bundle FILENAME] [SOURCE]')),
3492 ' [--bundle FILENAME] [SOURCE]')),
3491 "^init":
3493 "^init":
3492 (init,
3494 (init,
3493 remoteopts,
3495 remoteopts,
3494 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3496 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3495 "locate":
3497 "locate":
3496 (locate,
3498 (locate,
3497 [('r', 'rev', '', _('search the repository as it stood at REV')),
3499 [('r', 'rev', '', _('search the repository as it stood at REV')),
3498 ('0', 'print0', None,
3500 ('0', 'print0', None,
3499 _('end filenames with NUL, for use with xargs')),
3501 _('end filenames with NUL, for use with xargs')),
3500 ('f', 'fullpath', None,
3502 ('f', 'fullpath', None,
3501 _('print complete paths from the filesystem root')),
3503 _('print complete paths from the filesystem root')),
3502 ] + walkopts,
3504 ] + walkopts,
3503 _('[OPTION]... [PATTERN]...')),
3505 _('[OPTION]... [PATTERN]...')),
3504 "^log|history":
3506 "^log|history":
3505 (log,
3507 (log,
3506 [('f', 'follow', None,
3508 [('f', 'follow', None,
3507 _('follow changeset history, or file history across copies and renames')),
3509 _('follow changeset history, or file history across copies and renames')),
3508 ('', 'follow-first', None,
3510 ('', 'follow-first', None,
3509 _('only follow the first parent of merge changesets')),
3511 _('only follow the first parent of merge changesets')),
3510 ('d', 'date', '', _('show revisions matching date spec')),
3512 ('d', 'date', '', _('show revisions matching date spec')),
3511 ('C', 'copies', None, _('show copied files')),
3513 ('C', 'copies', None, _('show copied files')),
3512 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3514 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3513 ('r', 'rev', [], _('show the specified revision or range')),
3515 ('r', 'rev', [], _('show the specified revision or range')),
3514 ('', 'removed', None, _('include revisions where files were removed')),
3516 ('', 'removed', None, _('include revisions where files were removed')),
3515 ('m', 'only-merges', None, _('show only merges')),
3517 ('m', 'only-merges', None, _('show only merges')),
3516 ('u', 'user', [], _('revisions committed by user')),
3518 ('u', 'user', [], _('revisions committed by user')),
3517 ('b', 'only-branch', [],
3519 ('b', 'only-branch', [],
3518 _('show only changesets within the given named branch')),
3520 _('show only changesets within the given named branch')),
3519 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3521 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3520 ] + logopts + walkopts,
3522 ] + logopts + walkopts,
3521 _('[OPTION]... [FILE]')),
3523 _('[OPTION]... [FILE]')),
3522 "manifest":
3524 "manifest":
3523 (manifest,
3525 (manifest,
3524 [('r', 'rev', '', _('revision to display'))],
3526 [('r', 'rev', '', _('revision to display'))],
3525 _('[-r REV]')),
3527 _('[-r REV]')),
3526 "^merge":
3528 "^merge":
3527 (merge,
3529 (merge,
3528 [('f', 'force', None, _('force a merge with outstanding changes')),
3530 [('f', 'force', None, _('force a merge with outstanding changes')),
3529 ('r', 'rev', '', _('revision to merge')),
3531 ('r', 'rev', '', _('revision to merge')),
3530 ('P', 'preview', None,
3532 ('P', 'preview', None,
3531 _('review revisions to merge (no merge is performed)'))],
3533 _('review revisions to merge (no merge is performed)'))],
3532 _('[-f] [[-r] REV]')),
3534 _('[-f] [[-r] REV]')),
3533 "outgoing|out":
3535 "outgoing|out":
3534 (outgoing,
3536 (outgoing,
3535 [('f', 'force', None,
3537 [('f', 'force', None,
3536 _('run even when remote repository is unrelated')),
3538 _('run even when remote repository is unrelated')),
3537 ('r', 'rev', [],
3539 ('r', 'rev', [],
3538 _('a specific revision up to which you would like to push')),
3540 _('a specific revision up to which you would like to push')),
3539 ('n', 'newest-first', None, _('show newest record first')),
3541 ('n', 'newest-first', None, _('show newest record first')),
3540 ] + logopts + remoteopts,
3542 ] + logopts + remoteopts,
3541 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3543 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3542 "parents":
3544 "parents":
3543 (parents,
3545 (parents,
3544 [('r', 'rev', '', _('show parents from the specified revision')),
3546 [('r', 'rev', '', _('show parents from the specified revision')),
3545 ] + templateopts,
3547 ] + templateopts,
3546 _('[-r REV] [FILE]')),
3548 _('[-r REV] [FILE]')),
3547 "paths": (paths, [], _('[NAME]')),
3549 "paths": (paths, [], _('[NAME]')),
3548 "^pull":
3550 "^pull":
3549 (pull,
3551 (pull,
3550 [('u', 'update', None,
3552 [('u', 'update', None,
3551 _('update to new tip if changesets were pulled')),
3553 _('update to new tip if changesets were pulled')),
3552 ('f', 'force', None,
3554 ('f', 'force', None,
3553 _('run even when remote repository is unrelated')),
3555 _('run even when remote repository is unrelated')),
3554 ('r', 'rev', [],
3556 ('r', 'rev', [],
3555 _('a specific revision up to which you would like to pull')),
3557 _('a specific revision up to which you would like to pull')),
3556 ] + remoteopts,
3558 ] + remoteopts,
3557 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3559 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3558 "^push":
3560 "^push":
3559 (push,
3561 (push,
3560 [('f', 'force', None, _('force push')),
3562 [('f', 'force', None, _('force push')),
3561 ('r', 'rev', [],
3563 ('r', 'rev', [],
3562 _('a specific revision up to which you would like to push')),
3564 _('a specific revision up to which you would like to push')),
3563 ] + remoteopts,
3565 ] + remoteopts,
3564 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3566 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3565 "recover": (recover, []),
3567 "recover": (recover, []),
3566 "^remove|rm":
3568 "^remove|rm":
3567 (remove,
3569 (remove,
3568 [('A', 'after', None, _('record delete for missing files')),
3570 [('A', 'after', None, _('record delete for missing files')),
3569 ('f', 'force', None,
3571 ('f', 'force', None,
3570 _('remove (and delete) file even if added or modified')),
3572 _('remove (and delete) file even if added or modified')),
3571 ] + walkopts,
3573 ] + walkopts,
3572 _('[OPTION]... FILE...')),
3574 _('[OPTION]... FILE...')),
3573 "rename|mv":
3575 "rename|mv":
3574 (rename,
3576 (rename,
3575 [('A', 'after', None, _('record a rename that has already occurred')),
3577 [('A', 'after', None, _('record a rename that has already occurred')),
3576 ('f', 'force', None,
3578 ('f', 'force', None,
3577 _('forcibly copy over an existing managed file')),
3579 _('forcibly copy over an existing managed file')),
3578 ] + walkopts + dryrunopts,
3580 ] + walkopts + dryrunopts,
3579 _('[OPTION]... SOURCE... DEST')),
3581 _('[OPTION]... SOURCE... DEST')),
3580 "resolve":
3582 "resolve":
3581 (resolve,
3583 (resolve,
3582 [('a', 'all', None, _('remerge all unresolved files')),
3584 [('a', 'all', None, _('remerge all unresolved files')),
3583 ('l', 'list', None, _('list state of files needing merge')),
3585 ('l', 'list', None, _('list state of files needing merge')),
3584 ('m', 'mark', None, _('mark files as resolved')),
3586 ('m', 'mark', None, _('mark files as resolved')),
3585 ('u', 'unmark', None, _('unmark files as resolved'))]
3587 ('u', 'unmark', None, _('unmark files as resolved'))]
3586 + walkopts,
3588 + walkopts,
3587 _('[OPTION]... [FILE]...')),
3589 _('[OPTION]... [FILE]...')),
3588 "revert":
3590 "revert":
3589 (revert,
3591 (revert,
3590 [('a', 'all', None, _('revert all changes when no arguments given')),
3592 [('a', 'all', None, _('revert all changes when no arguments given')),
3591 ('d', 'date', '', _('tipmost revision matching date')),
3593 ('d', 'date', '', _('tipmost revision matching date')),
3592 ('r', 'rev', '', _('revision to revert to')),
3594 ('r', 'rev', '', _('revision to revert to')),
3593 ('', 'no-backup', None, _('do not save backup copies of files')),
3595 ('', 'no-backup', None, _('do not save backup copies of files')),
3594 ] + walkopts + dryrunopts,
3596 ] + walkopts + dryrunopts,
3595 _('[OPTION]... [-r REV] [NAME]...')),
3597 _('[OPTION]... [-r REV] [NAME]...')),
3596 "rollback": (rollback, []),
3598 "rollback": (rollback, []),
3597 "root": (root, []),
3599 "root": (root, []),
3598 "^serve":
3600 "^serve":
3599 (serve,
3601 (serve,
3600 [('A', 'accesslog', '', _('name of access log file to write to')),
3602 [('A', 'accesslog', '', _('name of access log file to write to')),
3601 ('d', 'daemon', None, _('run server in background')),
3603 ('d', 'daemon', None, _('run server in background')),
3602 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3604 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3603 ('E', 'errorlog', '', _('name of error log file to write to')),
3605 ('E', 'errorlog', '', _('name of error log file to write to')),
3604 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3606 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3605 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3607 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3606 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3608 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3607 ('n', 'name', '',
3609 ('n', 'name', '',
3608 _('name to show in web pages (default: working directory)')),
3610 _('name to show in web pages (default: working directory)')),
3609 ('', 'webdir-conf', '', _('name of the webdir config file'
3611 ('', 'webdir-conf', '', _('name of the webdir config file'
3610 ' (serve more than one repository)')),
3612 ' (serve more than one repository)')),
3611 ('', 'pid-file', '', _('name of file to write process ID to')),
3613 ('', 'pid-file', '', _('name of file to write process ID to')),
3612 ('', 'stdio', None, _('for remote clients')),
3614 ('', 'stdio', None, _('for remote clients')),
3613 ('t', 'templates', '', _('web templates to use')),
3615 ('t', 'templates', '', _('web templates to use')),
3614 ('', 'style', '', _('template style to use')),
3616 ('', 'style', '', _('template style to use')),
3615 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3617 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3616 ('', 'certificate', '', _('SSL certificate file'))],
3618 ('', 'certificate', '', _('SSL certificate file'))],
3617 _('[OPTION]...')),
3619 _('[OPTION]...')),
3618 "showconfig|debugconfig":
3620 "showconfig|debugconfig":
3619 (showconfig,
3621 (showconfig,
3620 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3622 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3621 _('[-u] [NAME]...')),
3623 _('[-u] [NAME]...')),
3622 "^summary|sum":
3624 "^summary|sum":
3623 (summary,
3625 (summary,
3624 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3626 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
3625 "^status|st":
3627 "^status|st":
3626 (status,
3628 (status,
3627 [('A', 'all', None, _('show status of all files')),
3629 [('A', 'all', None, _('show status of all files')),
3628 ('m', 'modified', None, _('show only modified files')),
3630 ('m', 'modified', None, _('show only modified files')),
3629 ('a', 'added', None, _('show only added files')),
3631 ('a', 'added', None, _('show only added files')),
3630 ('r', 'removed', None, _('show only removed files')),
3632 ('r', 'removed', None, _('show only removed files')),
3631 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3633 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3632 ('c', 'clean', None, _('show only files without changes')),
3634 ('c', 'clean', None, _('show only files without changes')),
3633 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3635 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3634 ('i', 'ignored', None, _('show only ignored files')),
3636 ('i', 'ignored', None, _('show only ignored files')),
3635 ('n', 'no-status', None, _('hide status prefix')),
3637 ('n', 'no-status', None, _('hide status prefix')),
3636 ('C', 'copies', None, _('show source of copied files')),
3638 ('C', 'copies', None, _('show source of copied files')),
3637 ('0', 'print0', None,
3639 ('0', 'print0', None,
3638 _('end filenames with NUL, for use with xargs')),
3640 _('end filenames with NUL, for use with xargs')),
3639 ('', 'rev', [], _('show difference from revision')),
3641 ('', 'rev', [], _('show difference from revision')),
3640 ] + walkopts,
3642 ] + walkopts,
3641 _('[OPTION]... [FILE]...')),
3643 _('[OPTION]... [FILE]...')),
3642 "tag":
3644 "tag":
3643 (tag,
3645 (tag,
3644 [('f', 'force', None, _('replace existing tag')),
3646 [('f', 'force', None, _('replace existing tag')),
3645 ('l', 'local', None, _('make the tag local')),
3647 ('l', 'local', None, _('make the tag local')),
3646 ('r', 'rev', '', _('revision to tag')),
3648 ('r', 'rev', '', _('revision to tag')),
3647 ('', 'remove', None, _('remove a tag')),
3649 ('', 'remove', None, _('remove a tag')),
3648 # -l/--local is already there, commitopts cannot be used
3650 # -l/--local is already there, commitopts cannot be used
3649 ('m', 'message', '', _('use <text> as commit message')),
3651 ('m', 'message', '', _('use <text> as commit message')),
3650 ] + commitopts2,
3652 ] + commitopts2,
3651 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3653 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3652 "tags": (tags, [], ''),
3654 "tags": (tags, [], ''),
3653 "tip":
3655 "tip":
3654 (tip,
3656 (tip,
3655 [('p', 'patch', None, _('show patch')),
3657 [('p', 'patch', None, _('show patch')),
3656 ('g', 'git', None, _('use git extended diff format')),
3658 ('g', 'git', None, _('use git extended diff format')),
3657 ] + templateopts,
3659 ] + templateopts,
3658 _('[-p]')),
3660 _('[-p]')),
3659 "unbundle":
3661 "unbundle":
3660 (unbundle,
3662 (unbundle,
3661 [('u', 'update', None,
3663 [('u', 'update', None,
3662 _('update to new tip if changesets were unbundled'))],
3664 _('update to new tip if changesets were unbundled'))],
3663 _('[-u] FILE...')),
3665 _('[-u] FILE...')),
3664 "^update|up|checkout|co":
3666 "^update|up|checkout|co":
3665 (update,
3667 (update,
3666 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3668 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3667 ('c', 'check', None, _('check for uncommitted changes')),
3669 ('c', 'check', None, _('check for uncommitted changes')),
3668 ('d', 'date', '', _('tipmost revision matching date')),
3670 ('d', 'date', '', _('tipmost revision matching date')),
3669 ('r', 'rev', '', _('revision'))],
3671 ('r', 'rev', '', _('revision'))],
3670 _('[-C] [-d DATE] [[-r] REV]')),
3672 _('[-C] [-d DATE] [[-r] REV]')),
3671 "verify": (verify, []),
3673 "verify": (verify, []),
3672 "version": (version_, []),
3674 "version": (version_, []),
3673 }
3675 }
3674
3676
3675 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3677 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3676 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3678 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3677 optionalrepo = ("identify paths serve showconfig debugancestor")
3679 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,1417 +1,1428 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 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2, incorporated herein by reference.
7 # GNU General Public License version 2, incorporated herein by reference.
8
8
9 from i18n import _
9 from i18n import _
10 from node import hex, nullid, short
10 from node import hex, nullid, short
11 import base85, cmdutil, mdiff, util, diffhelpers, copies
11 import base85, cmdutil, mdiff, util, diffhelpers, copies
12 import cStringIO, email.Parser, os, re
12 import cStringIO, email.Parser, os, re
13 import sys, tempfile, zlib
13 import sys, tempfile, zlib
14
14
15 gitre = re.compile('diff --git a/(.*) b/(.*)')
15 gitre = re.compile('diff --git a/(.*) b/(.*)')
16
16
17 class PatchError(Exception):
17 class PatchError(Exception):
18 pass
18 pass
19
19
20 class NoHunks(PatchError):
20 class NoHunks(PatchError):
21 pass
21 pass
22
22
23 # helper functions
23 # helper functions
24
24
25 def copyfile(src, dst, basedir):
25 def copyfile(src, dst, basedir):
26 abssrc, absdst = [util.canonpath(basedir, basedir, x) for x in [src, dst]]
26 abssrc, absdst = [util.canonpath(basedir, basedir, x) for x in [src, dst]]
27 if os.path.exists(absdst):
27 if os.path.exists(absdst):
28 raise util.Abort(_("cannot create %s: destination already exists") %
28 raise util.Abort(_("cannot create %s: destination already exists") %
29 dst)
29 dst)
30
30
31 dstdir = os.path.dirname(absdst)
31 dstdir = os.path.dirname(absdst)
32 if dstdir and not os.path.isdir(dstdir):
32 if dstdir and not os.path.isdir(dstdir):
33 try:
33 try:
34 os.makedirs(dstdir)
34 os.makedirs(dstdir)
35 except IOError:
35 except IOError:
36 raise util.Abort(
36 raise util.Abort(
37 _("cannot create %s: unable to create destination directory")
37 _("cannot create %s: unable to create destination directory")
38 % dst)
38 % dst)
39
39
40 util.copyfile(abssrc, absdst)
40 util.copyfile(abssrc, absdst)
41
41
42 # public functions
42 # public functions
43
43
44 def extract(ui, fileobj):
44 def extract(ui, fileobj):
45 '''extract patch from data read from fileobj.
45 '''extract patch from data read from fileobj.
46
46
47 patch can be a normal patch or contained in an email message.
47 patch can be a normal patch or contained in an email message.
48
48
49 return tuple (filename, message, user, date, node, p1, p2).
49 return tuple (filename, message, user, date, node, p1, p2).
50 Any item in the returned tuple can be None. If filename is None,
50 Any item in the returned tuple can be None. If filename is None,
51 fileobj did not contain a patch. Caller must unlink filename when done.'''
51 fileobj did not contain a patch. Caller must unlink filename when done.'''
52
52
53 # attempt to detect the start of a patch
53 # attempt to detect the start of a patch
54 # (this heuristic is borrowed from quilt)
54 # (this heuristic is borrowed from quilt)
55 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
55 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
56 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
56 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
57 r'(---|\*\*\*)[ \t])', re.MULTILINE)
57 r'(---|\*\*\*)[ \t])', re.MULTILINE)
58
58
59 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
59 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
60 tmpfp = os.fdopen(fd, 'w')
60 tmpfp = os.fdopen(fd, 'w')
61 try:
61 try:
62 msg = email.Parser.Parser().parse(fileobj)
62 msg = email.Parser.Parser().parse(fileobj)
63
63
64 subject = msg['Subject']
64 subject = msg['Subject']
65 user = msg['From']
65 user = msg['From']
66 if not subject and not user:
66 if not subject and not user:
67 # Not an email, restore parsed headers if any
67 # Not an email, restore parsed headers if any
68 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
68 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
69
69
70 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
70 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
71 # should try to parse msg['Date']
71 # should try to parse msg['Date']
72 date = None
72 date = None
73 nodeid = None
73 nodeid = None
74 branch = None
74 branch = None
75 parents = []
75 parents = []
76
76
77 if subject:
77 if subject:
78 if subject.startswith('[PATCH'):
78 if subject.startswith('[PATCH'):
79 pend = subject.find(']')
79 pend = subject.find(']')
80 if pend >= 0:
80 if pend >= 0:
81 subject = subject[pend+1:].lstrip()
81 subject = subject[pend+1:].lstrip()
82 subject = subject.replace('\n\t', ' ')
82 subject = subject.replace('\n\t', ' ')
83 ui.debug('Subject: %s\n' % subject)
83 ui.debug('Subject: %s\n' % subject)
84 if user:
84 if user:
85 ui.debug('From: %s\n' % user)
85 ui.debug('From: %s\n' % user)
86 diffs_seen = 0
86 diffs_seen = 0
87 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
87 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
88 message = ''
88 message = ''
89 for part in msg.walk():
89 for part in msg.walk():
90 content_type = part.get_content_type()
90 content_type = part.get_content_type()
91 ui.debug('Content-Type: %s\n' % content_type)
91 ui.debug('Content-Type: %s\n' % content_type)
92 if content_type not in ok_types:
92 if content_type not in ok_types:
93 continue
93 continue
94 payload = part.get_payload(decode=True)
94 payload = part.get_payload(decode=True)
95 m = diffre.search(payload)
95 m = diffre.search(payload)
96 if m:
96 if m:
97 hgpatch = False
97 hgpatch = False
98 ignoretext = False
98 ignoretext = False
99
99
100 ui.debug('found patch at byte %d\n' % m.start(0))
100 ui.debug('found patch at byte %d\n' % m.start(0))
101 diffs_seen += 1
101 diffs_seen += 1
102 cfp = cStringIO.StringIO()
102 cfp = cStringIO.StringIO()
103 for line in payload[:m.start(0)].splitlines():
103 for line in payload[:m.start(0)].splitlines():
104 if line.startswith('# HG changeset patch'):
104 if line.startswith('# HG changeset patch'):
105 ui.debug('patch generated by hg export\n')
105 ui.debug('patch generated by hg export\n')
106 hgpatch = True
106 hgpatch = True
107 # drop earlier commit message content
107 # drop earlier commit message content
108 cfp.seek(0)
108 cfp.seek(0)
109 cfp.truncate()
109 cfp.truncate()
110 subject = None
110 subject = None
111 elif hgpatch:
111 elif hgpatch:
112 if line.startswith('# User '):
112 if line.startswith('# User '):
113 user = line[7:]
113 user = line[7:]
114 ui.debug('From: %s\n' % user)
114 ui.debug('From: %s\n' % user)
115 elif line.startswith("# Date "):
115 elif line.startswith("# Date "):
116 date = line[7:]
116 date = line[7:]
117 elif line.startswith("# Branch "):
117 elif line.startswith("# Branch "):
118 branch = line[9:]
118 branch = line[9:]
119 elif line.startswith("# Node ID "):
119 elif line.startswith("# Node ID "):
120 nodeid = line[10:]
120 nodeid = line[10:]
121 elif line.startswith("# Parent "):
121 elif line.startswith("# Parent "):
122 parents.append(line[10:])
122 parents.append(line[10:])
123 elif line == '---' and gitsendmail:
123 elif line == '---' and gitsendmail:
124 ignoretext = True
124 ignoretext = True
125 if not line.startswith('# ') and not ignoretext:
125 if not line.startswith('# ') and not ignoretext:
126 cfp.write(line)
126 cfp.write(line)
127 cfp.write('\n')
127 cfp.write('\n')
128 message = cfp.getvalue()
128 message = cfp.getvalue()
129 if tmpfp:
129 if tmpfp:
130 tmpfp.write(payload)
130 tmpfp.write(payload)
131 if not payload.endswith('\n'):
131 if not payload.endswith('\n'):
132 tmpfp.write('\n')
132 tmpfp.write('\n')
133 elif not diffs_seen and message and content_type == 'text/plain':
133 elif not diffs_seen and message and content_type == 'text/plain':
134 message += '\n' + payload
134 message += '\n' + payload
135 except:
135 except:
136 tmpfp.close()
136 tmpfp.close()
137 os.unlink(tmpname)
137 os.unlink(tmpname)
138 raise
138 raise
139
139
140 if subject and not message.startswith(subject):
140 if subject and not message.startswith(subject):
141 message = '%s\n%s' % (subject, message)
141 message = '%s\n%s' % (subject, message)
142 tmpfp.close()
142 tmpfp.close()
143 if not diffs_seen:
143 if not diffs_seen:
144 os.unlink(tmpname)
144 os.unlink(tmpname)
145 return None, message, user, date, branch, None, None, None
145 return None, message, user, date, branch, None, None, None
146 p1 = parents and parents.pop(0) or None
146 p1 = parents and parents.pop(0) or None
147 p2 = parents and parents.pop(0) or None
147 p2 = parents and parents.pop(0) or None
148 return tmpname, message, user, date, branch, nodeid, p1, p2
148 return tmpname, message, user, date, branch, nodeid, p1, p2
149
149
150 GP_PATCH = 1 << 0 # we have to run patch
150 GP_PATCH = 1 << 0 # we have to run patch
151 GP_FILTER = 1 << 1 # there's some copy/rename operation
151 GP_FILTER = 1 << 1 # there's some copy/rename operation
152 GP_BINARY = 1 << 2 # there's a binary patch
152 GP_BINARY = 1 << 2 # there's a binary patch
153
153
154 class patchmeta(object):
154 class patchmeta(object):
155 """Patched file metadata
155 """Patched file metadata
156
156
157 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
157 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
158 or COPY. 'path' is patched file path. 'oldpath' is set to the
158 or COPY. 'path' is patched file path. 'oldpath' is set to the
159 origin file when 'op' is either COPY or RENAME, None otherwise. If
159 origin file when 'op' is either COPY or RENAME, None otherwise. If
160 file mode is changed, 'mode' is a tuple (islink, isexec) where
160 file mode is changed, 'mode' is a tuple (islink, isexec) where
161 'islink' is True if the file is a symlink and 'isexec' is True if
161 'islink' is True if the file is a symlink and 'isexec' is True if
162 the file is executable. Otherwise, 'mode' is None.
162 the file is executable. Otherwise, 'mode' is None.
163 """
163 """
164 def __init__(self, path):
164 def __init__(self, path):
165 self.path = path
165 self.path = path
166 self.oldpath = None
166 self.oldpath = None
167 self.mode = None
167 self.mode = None
168 self.op = 'MODIFY'
168 self.op = 'MODIFY'
169 self.lineno = 0
169 self.lineno = 0
170 self.binary = False
170 self.binary = False
171
171
172 def setmode(self, mode):
172 def setmode(self, mode):
173 islink = mode & 020000
173 islink = mode & 020000
174 isexec = mode & 0100
174 isexec = mode & 0100
175 self.mode = (islink, isexec)
175 self.mode = (islink, isexec)
176
176
177 def readgitpatch(lr):
177 def readgitpatch(lr):
178 """extract git-style metadata about patches from <patchname>"""
178 """extract git-style metadata about patches from <patchname>"""
179
179
180 # Filter patch for git information
180 # Filter patch for git information
181 gp = None
181 gp = None
182 gitpatches = []
182 gitpatches = []
183 # Can have a git patch with only metadata, causing patch to complain
183 # Can have a git patch with only metadata, causing patch to complain
184 dopatch = 0
184 dopatch = 0
185
185
186 lineno = 0
186 lineno = 0
187 for line in lr:
187 for line in lr:
188 lineno += 1
188 lineno += 1
189 line = line.rstrip(' \r\n')
189 line = line.rstrip(' \r\n')
190 if line.startswith('diff --git'):
190 if line.startswith('diff --git'):
191 m = gitre.match(line)
191 m = gitre.match(line)
192 if m:
192 if m:
193 if gp:
193 if gp:
194 gitpatches.append(gp)
194 gitpatches.append(gp)
195 dst = m.group(2)
195 dst = m.group(2)
196 gp = patchmeta(dst)
196 gp = patchmeta(dst)
197 gp.lineno = lineno
197 gp.lineno = lineno
198 elif gp:
198 elif gp:
199 if line.startswith('--- '):
199 if line.startswith('--- '):
200 if gp.op in ('COPY', 'RENAME'):
200 if gp.op in ('COPY', 'RENAME'):
201 dopatch |= GP_FILTER
201 dopatch |= GP_FILTER
202 gitpatches.append(gp)
202 gitpatches.append(gp)
203 gp = None
203 gp = None
204 dopatch |= GP_PATCH
204 dopatch |= GP_PATCH
205 continue
205 continue
206 if line.startswith('rename from '):
206 if line.startswith('rename from '):
207 gp.op = 'RENAME'
207 gp.op = 'RENAME'
208 gp.oldpath = line[12:]
208 gp.oldpath = line[12:]
209 elif line.startswith('rename to '):
209 elif line.startswith('rename to '):
210 gp.path = line[10:]
210 gp.path = line[10:]
211 elif line.startswith('copy from '):
211 elif line.startswith('copy from '):
212 gp.op = 'COPY'
212 gp.op = 'COPY'
213 gp.oldpath = line[10:]
213 gp.oldpath = line[10:]
214 elif line.startswith('copy to '):
214 elif line.startswith('copy to '):
215 gp.path = line[8:]
215 gp.path = line[8:]
216 elif line.startswith('deleted file'):
216 elif line.startswith('deleted file'):
217 gp.op = 'DELETE'
217 gp.op = 'DELETE'
218 # is the deleted file a symlink?
218 # is the deleted file a symlink?
219 gp.setmode(int(line[-6:], 8))
219 gp.setmode(int(line[-6:], 8))
220 elif line.startswith('new file mode '):
220 elif line.startswith('new file mode '):
221 gp.op = 'ADD'
221 gp.op = 'ADD'
222 gp.setmode(int(line[-6:], 8))
222 gp.setmode(int(line[-6:], 8))
223 elif line.startswith('new mode '):
223 elif line.startswith('new mode '):
224 gp.setmode(int(line[-6:], 8))
224 gp.setmode(int(line[-6:], 8))
225 elif line.startswith('GIT binary patch'):
225 elif line.startswith('GIT binary patch'):
226 dopatch |= GP_BINARY
226 dopatch |= GP_BINARY
227 gp.binary = True
227 gp.binary = True
228 if gp:
228 if gp:
229 gitpatches.append(gp)
229 gitpatches.append(gp)
230
230
231 if not gitpatches:
231 if not gitpatches:
232 dopatch = GP_PATCH
232 dopatch = GP_PATCH
233
233
234 return (dopatch, gitpatches)
234 return (dopatch, gitpatches)
235
235
236 class linereader(object):
236 class linereader(object):
237 # simple class to allow pushing lines back into the input stream
237 # simple class to allow pushing lines back into the input stream
238 def __init__(self, fp, textmode=False):
238 def __init__(self, fp, textmode=False):
239 self.fp = fp
239 self.fp = fp
240 self.buf = []
240 self.buf = []
241 self.textmode = textmode
241 self.textmode = textmode
242
242
243 def push(self, line):
243 def push(self, line):
244 if line is not None:
244 if line is not None:
245 self.buf.append(line)
245 self.buf.append(line)
246
246
247 def readline(self):
247 def readline(self):
248 if self.buf:
248 if self.buf:
249 l = self.buf[0]
249 l = self.buf[0]
250 del self.buf[0]
250 del self.buf[0]
251 return l
251 return l
252 l = self.fp.readline()
252 l = self.fp.readline()
253 if self.textmode and l.endswith('\r\n'):
253 if self.textmode and l.endswith('\r\n'):
254 l = l[:-2] + '\n'
254 l = l[:-2] + '\n'
255 return l
255 return l
256
256
257 def __iter__(self):
257 def __iter__(self):
258 while 1:
258 while 1:
259 l = self.readline()
259 l = self.readline()
260 if not l:
260 if not l:
261 break
261 break
262 yield l
262 yield l
263
263
264 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
264 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
265 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
265 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
266 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
266 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
267
267
268 class patchfile(object):
268 class patchfile(object):
269 def __init__(self, ui, fname, opener, missing=False, eol=None):
269 def __init__(self, ui, fname, opener, missing=False, eol=None):
270 self.fname = fname
270 self.fname = fname
271 self.eol = eol
271 self.eol = eol
272 self.opener = opener
272 self.opener = opener
273 self.ui = ui
273 self.ui = ui
274 self.lines = []
274 self.lines = []
275 self.exists = False
275 self.exists = False
276 self.missing = missing
276 self.missing = missing
277 if not missing:
277 if not missing:
278 try:
278 try:
279 self.lines = self.readlines(fname)
279 self.lines = self.readlines(fname)
280 self.exists = True
280 self.exists = True
281 except IOError:
281 except IOError:
282 pass
282 pass
283 else:
283 else:
284 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
284 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
285
285
286 self.hash = {}
286 self.hash = {}
287 self.dirty = 0
287 self.dirty = 0
288 self.offset = 0
288 self.offset = 0
289 self.rej = []
289 self.rej = []
290 self.fileprinted = False
290 self.fileprinted = False
291 self.printfile(False)
291 self.printfile(False)
292 self.hunks = 0
292 self.hunks = 0
293
293
294 def readlines(self, fname):
294 def readlines(self, fname):
295 if os.path.islink(fname):
295 if os.path.islink(fname):
296 return [os.readlink(fname)]
296 return [os.readlink(fname)]
297 fp = self.opener(fname, 'r')
297 fp = self.opener(fname, 'r')
298 try:
298 try:
299 return list(linereader(fp, self.eol is not None))
299 return list(linereader(fp, self.eol is not None))
300 finally:
300 finally:
301 fp.close()
301 fp.close()
302
302
303 def writelines(self, fname, lines):
303 def writelines(self, fname, lines):
304 # Ensure supplied data ends in fname, being a regular file or
304 # Ensure supplied data ends in fname, being a regular file or
305 # a symlink. updatedir() will -too magically- take care of
305 # a symlink. updatedir() will -too magically- take care of
306 # setting it to the proper type afterwards.
306 # setting it to the proper type afterwards.
307 islink = os.path.islink(fname)
307 islink = os.path.islink(fname)
308 if islink:
308 if islink:
309 fp = cStringIO.StringIO()
309 fp = cStringIO.StringIO()
310 else:
310 else:
311 fp = self.opener(fname, 'w')
311 fp = self.opener(fname, 'w')
312 try:
312 try:
313 if self.eol and self.eol != '\n':
313 if self.eol and self.eol != '\n':
314 for l in lines:
314 for l in lines:
315 if l and l[-1] == '\n':
315 if l and l[-1] == '\n':
316 l = l[:-1] + self.eol
316 l = l[:-1] + self.eol
317 fp.write(l)
317 fp.write(l)
318 else:
318 else:
319 fp.writelines(lines)
319 fp.writelines(lines)
320 if islink:
320 if islink:
321 self.opener.symlink(fp.getvalue(), fname)
321 self.opener.symlink(fp.getvalue(), fname)
322 finally:
322 finally:
323 fp.close()
323 fp.close()
324
324
325 def unlink(self, fname):
325 def unlink(self, fname):
326 os.unlink(fname)
326 os.unlink(fname)
327
327
328 def printfile(self, warn):
328 def printfile(self, warn):
329 if self.fileprinted:
329 if self.fileprinted:
330 return
330 return
331 if warn or self.ui.verbose:
331 if warn or self.ui.verbose:
332 self.fileprinted = True
332 self.fileprinted = True
333 s = _("patching file %s\n") % self.fname
333 s = _("patching file %s\n") % self.fname
334 if warn:
334 if warn:
335 self.ui.warn(s)
335 self.ui.warn(s)
336 else:
336 else:
337 self.ui.note(s)
337 self.ui.note(s)
338
338
339
339
340 def findlines(self, l, linenum):
340 def findlines(self, l, linenum):
341 # looks through the hash and finds candidate lines. The
341 # looks through the hash and finds candidate lines. The
342 # result is a list of line numbers sorted based on distance
342 # result is a list of line numbers sorted based on distance
343 # from linenum
343 # from linenum
344
344
345 try:
345 try:
346 cand = self.hash[l]
346 cand = self.hash[l]
347 except:
347 except:
348 return []
348 return []
349
349
350 if len(cand) > 1:
350 if len(cand) > 1:
351 # resort our list of potentials forward then back.
351 # resort our list of potentials forward then back.
352 cand.sort(key=lambda x: abs(x - linenum))
352 cand.sort(key=lambda x: abs(x - linenum))
353 return cand
353 return cand
354
354
355 def hashlines(self):
355 def hashlines(self):
356 self.hash = {}
356 self.hash = {}
357 for x, s in enumerate(self.lines):
357 for x, s in enumerate(self.lines):
358 self.hash.setdefault(s, []).append(x)
358 self.hash.setdefault(s, []).append(x)
359
359
360 def write_rej(self):
360 def write_rej(self):
361 # our rejects are a little different from patch(1). This always
361 # our rejects are a little different from patch(1). This always
362 # creates rejects in the same form as the original patch. A file
362 # creates rejects in the same form as the original patch. A file
363 # header is inserted so that you can run the reject through patch again
363 # header is inserted so that you can run the reject through patch again
364 # without having to type the filename.
364 # without having to type the filename.
365
365
366 if not self.rej:
366 if not self.rej:
367 return
367 return
368
368
369 fname = self.fname + ".rej"
369 fname = self.fname + ".rej"
370 self.ui.warn(
370 self.ui.warn(
371 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
371 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
372 (len(self.rej), self.hunks, fname))
372 (len(self.rej), self.hunks, fname))
373
373
374 def rejlines():
374 def rejlines():
375 base = os.path.basename(self.fname)
375 base = os.path.basename(self.fname)
376 yield "--- %s\n+++ %s\n" % (base, base)
376 yield "--- %s\n+++ %s\n" % (base, base)
377 for x in self.rej:
377 for x in self.rej:
378 for l in x.hunk:
378 for l in x.hunk:
379 yield l
379 yield l
380 if l[-1] != '\n':
380 if l[-1] != '\n':
381 yield "\n\ No newline at end of file\n"
381 yield "\n\ No newline at end of file\n"
382
382
383 self.writelines(fname, rejlines())
383 self.writelines(fname, rejlines())
384
384
385 def write(self, dest=None):
385 def write(self, dest=None):
386 if not self.dirty:
386 if not self.dirty:
387 return
387 return
388 if not dest:
388 if not dest:
389 dest = self.fname
389 dest = self.fname
390 self.writelines(dest, self.lines)
390 self.writelines(dest, self.lines)
391
391
392 def close(self):
392 def close(self):
393 self.write()
393 self.write()
394 self.write_rej()
394 self.write_rej()
395
395
396 def apply(self, h):
396 def apply(self, h):
397 if not h.complete():
397 if not h.complete():
398 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
398 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
399 (h.number, h.desc, len(h.a), h.lena, len(h.b),
399 (h.number, h.desc, len(h.a), h.lena, len(h.b),
400 h.lenb))
400 h.lenb))
401
401
402 self.hunks += 1
402 self.hunks += 1
403
403
404 if self.missing:
404 if self.missing:
405 self.rej.append(h)
405 self.rej.append(h)
406 return -1
406 return -1
407
407
408 if self.exists and h.createfile():
408 if self.exists and h.createfile():
409 self.ui.warn(_("file %s already exists\n") % self.fname)
409 self.ui.warn(_("file %s already exists\n") % self.fname)
410 self.rej.append(h)
410 self.rej.append(h)
411 return -1
411 return -1
412
412
413 if isinstance(h, binhunk):
413 if isinstance(h, binhunk):
414 if h.rmfile():
414 if h.rmfile():
415 self.unlink(self.fname)
415 self.unlink(self.fname)
416 else:
416 else:
417 self.lines[:] = h.new()
417 self.lines[:] = h.new()
418 self.offset += len(h.new())
418 self.offset += len(h.new())
419 self.dirty = 1
419 self.dirty = 1
420 return 0
420 return 0
421
421
422 # fast case first, no offsets, no fuzz
422 # fast case first, no offsets, no fuzz
423 old = h.old()
423 old = h.old()
424 # patch starts counting at 1 unless we are adding the file
424 # patch starts counting at 1 unless we are adding the file
425 if h.starta == 0:
425 if h.starta == 0:
426 start = 0
426 start = 0
427 else:
427 else:
428 start = h.starta + self.offset - 1
428 start = h.starta + self.offset - 1
429 orig_start = start
429 orig_start = start
430 if diffhelpers.testhunk(old, self.lines, start) == 0:
430 if diffhelpers.testhunk(old, self.lines, start) == 0:
431 if h.rmfile():
431 if h.rmfile():
432 self.unlink(self.fname)
432 self.unlink(self.fname)
433 else:
433 else:
434 self.lines[start : start + h.lena] = h.new()
434 self.lines[start : start + h.lena] = h.new()
435 self.offset += h.lenb - h.lena
435 self.offset += h.lenb - h.lena
436 self.dirty = 1
436 self.dirty = 1
437 return 0
437 return 0
438
438
439 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
439 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
440 self.hashlines()
440 self.hashlines()
441 if h.hunk[-1][0] != ' ':
441 if h.hunk[-1][0] != ' ':
442 # if the hunk tried to put something at the bottom of the file
442 # if the hunk tried to put something at the bottom of the file
443 # override the start line and use eof here
443 # override the start line and use eof here
444 search_start = len(self.lines)
444 search_start = len(self.lines)
445 else:
445 else:
446 search_start = orig_start
446 search_start = orig_start
447
447
448 for fuzzlen in xrange(3):
448 for fuzzlen in xrange(3):
449 for toponly in [ True, False ]:
449 for toponly in [ True, False ]:
450 old = h.old(fuzzlen, toponly)
450 old = h.old(fuzzlen, toponly)
451
451
452 cand = self.findlines(old[0][1:], search_start)
452 cand = self.findlines(old[0][1:], search_start)
453 for l in cand:
453 for l in cand:
454 if diffhelpers.testhunk(old, self.lines, l) == 0:
454 if diffhelpers.testhunk(old, self.lines, l) == 0:
455 newlines = h.new(fuzzlen, toponly)
455 newlines = h.new(fuzzlen, toponly)
456 self.lines[l : l + len(old)] = newlines
456 self.lines[l : l + len(old)] = newlines
457 self.offset += len(newlines) - len(old)
457 self.offset += len(newlines) - len(old)
458 self.dirty = 1
458 self.dirty = 1
459 if fuzzlen:
459 if fuzzlen:
460 fuzzstr = "with fuzz %d " % fuzzlen
460 fuzzstr = "with fuzz %d " % fuzzlen
461 f = self.ui.warn
461 f = self.ui.warn
462 self.printfile(True)
462 self.printfile(True)
463 else:
463 else:
464 fuzzstr = ""
464 fuzzstr = ""
465 f = self.ui.note
465 f = self.ui.note
466 offset = l - orig_start - fuzzlen
466 offset = l - orig_start - fuzzlen
467 if offset == 1:
467 if offset == 1:
468 msg = _("Hunk #%d succeeded at %d %s"
468 msg = _("Hunk #%d succeeded at %d %s"
469 "(offset %d line).\n")
469 "(offset %d line).\n")
470 else:
470 else:
471 msg = _("Hunk #%d succeeded at %d %s"
471 msg = _("Hunk #%d succeeded at %d %s"
472 "(offset %d lines).\n")
472 "(offset %d lines).\n")
473 f(msg % (h.number, l+1, fuzzstr, offset))
473 f(msg % (h.number, l+1, fuzzstr, offset))
474 return fuzzlen
474 return fuzzlen
475 self.printfile(True)
475 self.printfile(True)
476 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
476 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
477 self.rej.append(h)
477 self.rej.append(h)
478 return -1
478 return -1
479
479
480 class hunk(object):
480 class hunk(object):
481 def __init__(self, desc, num, lr, context, create=False, remove=False):
481 def __init__(self, desc, num, lr, context, create=False, remove=False):
482 self.number = num
482 self.number = num
483 self.desc = desc
483 self.desc = desc
484 self.hunk = [ desc ]
484 self.hunk = [ desc ]
485 self.a = []
485 self.a = []
486 self.b = []
486 self.b = []
487 if context:
487 if context:
488 self.read_context_hunk(lr)
488 self.read_context_hunk(lr)
489 else:
489 else:
490 self.read_unified_hunk(lr)
490 self.read_unified_hunk(lr)
491 self.create = create
491 self.create = create
492 self.remove = remove and not create
492 self.remove = remove and not create
493
493
494 def read_unified_hunk(self, lr):
494 def read_unified_hunk(self, lr):
495 m = unidesc.match(self.desc)
495 m = unidesc.match(self.desc)
496 if not m:
496 if not m:
497 raise PatchError(_("bad hunk #%d") % self.number)
497 raise PatchError(_("bad hunk #%d") % self.number)
498 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
498 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
499 if self.lena is None:
499 if self.lena is None:
500 self.lena = 1
500 self.lena = 1
501 else:
501 else:
502 self.lena = int(self.lena)
502 self.lena = int(self.lena)
503 if self.lenb is None:
503 if self.lenb is None:
504 self.lenb = 1
504 self.lenb = 1
505 else:
505 else:
506 self.lenb = int(self.lenb)
506 self.lenb = int(self.lenb)
507 self.starta = int(self.starta)
507 self.starta = int(self.starta)
508 self.startb = int(self.startb)
508 self.startb = int(self.startb)
509 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
509 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
510 # if we hit eof before finishing out the hunk, the last line will
510 # if we hit eof before finishing out the hunk, the last line will
511 # be zero length. Lets try to fix it up.
511 # be zero length. Lets try to fix it up.
512 while len(self.hunk[-1]) == 0:
512 while len(self.hunk[-1]) == 0:
513 del self.hunk[-1]
513 del self.hunk[-1]
514 del self.a[-1]
514 del self.a[-1]
515 del self.b[-1]
515 del self.b[-1]
516 self.lena -= 1
516 self.lena -= 1
517 self.lenb -= 1
517 self.lenb -= 1
518
518
519 def read_context_hunk(self, lr):
519 def read_context_hunk(self, lr):
520 self.desc = lr.readline()
520 self.desc = lr.readline()
521 m = contextdesc.match(self.desc)
521 m = contextdesc.match(self.desc)
522 if not m:
522 if not m:
523 raise PatchError(_("bad hunk #%d") % self.number)
523 raise PatchError(_("bad hunk #%d") % self.number)
524 foo, self.starta, foo2, aend, foo3 = m.groups()
524 foo, self.starta, foo2, aend, foo3 = m.groups()
525 self.starta = int(self.starta)
525 self.starta = int(self.starta)
526 if aend is None:
526 if aend is None:
527 aend = self.starta
527 aend = self.starta
528 self.lena = int(aend) - self.starta
528 self.lena = int(aend) - self.starta
529 if self.starta:
529 if self.starta:
530 self.lena += 1
530 self.lena += 1
531 for x in xrange(self.lena):
531 for x in xrange(self.lena):
532 l = lr.readline()
532 l = lr.readline()
533 if l.startswith('---'):
533 if l.startswith('---'):
534 lr.push(l)
534 lr.push(l)
535 break
535 break
536 s = l[2:]
536 s = l[2:]
537 if l.startswith('- ') or l.startswith('! '):
537 if l.startswith('- ') or l.startswith('! '):
538 u = '-' + s
538 u = '-' + s
539 elif l.startswith(' '):
539 elif l.startswith(' '):
540 u = ' ' + s
540 u = ' ' + s
541 else:
541 else:
542 raise PatchError(_("bad hunk #%d old text line %d") %
542 raise PatchError(_("bad hunk #%d old text line %d") %
543 (self.number, x))
543 (self.number, x))
544 self.a.append(u)
544 self.a.append(u)
545 self.hunk.append(u)
545 self.hunk.append(u)
546
546
547 l = lr.readline()
547 l = lr.readline()
548 if l.startswith('\ '):
548 if l.startswith('\ '):
549 s = self.a[-1][:-1]
549 s = self.a[-1][:-1]
550 self.a[-1] = s
550 self.a[-1] = s
551 self.hunk[-1] = s
551 self.hunk[-1] = s
552 l = lr.readline()
552 l = lr.readline()
553 m = contextdesc.match(l)
553 m = contextdesc.match(l)
554 if not m:
554 if not m:
555 raise PatchError(_("bad hunk #%d") % self.number)
555 raise PatchError(_("bad hunk #%d") % self.number)
556 foo, self.startb, foo2, bend, foo3 = m.groups()
556 foo, self.startb, foo2, bend, foo3 = m.groups()
557 self.startb = int(self.startb)
557 self.startb = int(self.startb)
558 if bend is None:
558 if bend is None:
559 bend = self.startb
559 bend = self.startb
560 self.lenb = int(bend) - self.startb
560 self.lenb = int(bend) - self.startb
561 if self.startb:
561 if self.startb:
562 self.lenb += 1
562 self.lenb += 1
563 hunki = 1
563 hunki = 1
564 for x in xrange(self.lenb):
564 for x in xrange(self.lenb):
565 l = lr.readline()
565 l = lr.readline()
566 if l.startswith('\ '):
566 if l.startswith('\ '):
567 s = self.b[-1][:-1]
567 s = self.b[-1][:-1]
568 self.b[-1] = s
568 self.b[-1] = s
569 self.hunk[hunki-1] = s
569 self.hunk[hunki-1] = s
570 continue
570 continue
571 if not l:
571 if not l:
572 lr.push(l)
572 lr.push(l)
573 break
573 break
574 s = l[2:]
574 s = l[2:]
575 if l.startswith('+ ') or l.startswith('! '):
575 if l.startswith('+ ') or l.startswith('! '):
576 u = '+' + s
576 u = '+' + s
577 elif l.startswith(' '):
577 elif l.startswith(' '):
578 u = ' ' + s
578 u = ' ' + s
579 elif len(self.b) == 0:
579 elif len(self.b) == 0:
580 # this can happen when the hunk does not add any lines
580 # this can happen when the hunk does not add any lines
581 lr.push(l)
581 lr.push(l)
582 break
582 break
583 else:
583 else:
584 raise PatchError(_("bad hunk #%d old text line %d") %
584 raise PatchError(_("bad hunk #%d old text line %d") %
585 (self.number, x))
585 (self.number, x))
586 self.b.append(s)
586 self.b.append(s)
587 while True:
587 while True:
588 if hunki >= len(self.hunk):
588 if hunki >= len(self.hunk):
589 h = ""
589 h = ""
590 else:
590 else:
591 h = self.hunk[hunki]
591 h = self.hunk[hunki]
592 hunki += 1
592 hunki += 1
593 if h == u:
593 if h == u:
594 break
594 break
595 elif h.startswith('-'):
595 elif h.startswith('-'):
596 continue
596 continue
597 else:
597 else:
598 self.hunk.insert(hunki-1, u)
598 self.hunk.insert(hunki-1, u)
599 break
599 break
600
600
601 if not self.a:
601 if not self.a:
602 # this happens when lines were only added to the hunk
602 # this happens when lines were only added to the hunk
603 for x in self.hunk:
603 for x in self.hunk:
604 if x.startswith('-') or x.startswith(' '):
604 if x.startswith('-') or x.startswith(' '):
605 self.a.append(x)
605 self.a.append(x)
606 if not self.b:
606 if not self.b:
607 # this happens when lines were only deleted from the hunk
607 # this happens when lines were only deleted from the hunk
608 for x in self.hunk:
608 for x in self.hunk:
609 if x.startswith('+') or x.startswith(' '):
609 if x.startswith('+') or x.startswith(' '):
610 self.b.append(x[1:])
610 self.b.append(x[1:])
611 # @@ -start,len +start,len @@
611 # @@ -start,len +start,len @@
612 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
612 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
613 self.startb, self.lenb)
613 self.startb, self.lenb)
614 self.hunk[0] = self.desc
614 self.hunk[0] = self.desc
615
615
616 def fix_newline(self):
616 def fix_newline(self):
617 diffhelpers.fix_newline(self.hunk, self.a, self.b)
617 diffhelpers.fix_newline(self.hunk, self.a, self.b)
618
618
619 def complete(self):
619 def complete(self):
620 return len(self.a) == self.lena and len(self.b) == self.lenb
620 return len(self.a) == self.lena and len(self.b) == self.lenb
621
621
622 def createfile(self):
622 def createfile(self):
623 return self.starta == 0 and self.lena == 0 and self.create
623 return self.starta == 0 and self.lena == 0 and self.create
624
624
625 def rmfile(self):
625 def rmfile(self):
626 return self.startb == 0 and self.lenb == 0 and self.remove
626 return self.startb == 0 and self.lenb == 0 and self.remove
627
627
628 def fuzzit(self, l, fuzz, toponly):
628 def fuzzit(self, l, fuzz, toponly):
629 # this removes context lines from the top and bottom of list 'l'. It
629 # this removes context lines from the top and bottom of list 'l'. It
630 # checks the hunk to make sure only context lines are removed, and then
630 # checks the hunk to make sure only context lines are removed, and then
631 # returns a new shortened list of lines.
631 # returns a new shortened list of lines.
632 fuzz = min(fuzz, len(l)-1)
632 fuzz = min(fuzz, len(l)-1)
633 if fuzz:
633 if fuzz:
634 top = 0
634 top = 0
635 bot = 0
635 bot = 0
636 hlen = len(self.hunk)
636 hlen = len(self.hunk)
637 for x in xrange(hlen-1):
637 for x in xrange(hlen-1):
638 # the hunk starts with the @@ line, so use x+1
638 # the hunk starts with the @@ line, so use x+1
639 if self.hunk[x+1][0] == ' ':
639 if self.hunk[x+1][0] == ' ':
640 top += 1
640 top += 1
641 else:
641 else:
642 break
642 break
643 if not toponly:
643 if not toponly:
644 for x in xrange(hlen-1):
644 for x in xrange(hlen-1):
645 if self.hunk[hlen-bot-1][0] == ' ':
645 if self.hunk[hlen-bot-1][0] == ' ':
646 bot += 1
646 bot += 1
647 else:
647 else:
648 break
648 break
649
649
650 # top and bot now count context in the hunk
650 # top and bot now count context in the hunk
651 # adjust them if either one is short
651 # adjust them if either one is short
652 context = max(top, bot, 3)
652 context = max(top, bot, 3)
653 if bot < context:
653 if bot < context:
654 bot = max(0, fuzz - (context - bot))
654 bot = max(0, fuzz - (context - bot))
655 else:
655 else:
656 bot = min(fuzz, bot)
656 bot = min(fuzz, bot)
657 if top < context:
657 if top < context:
658 top = max(0, fuzz - (context - top))
658 top = max(0, fuzz - (context - top))
659 else:
659 else:
660 top = min(fuzz, top)
660 top = min(fuzz, top)
661
661
662 return l[top:len(l)-bot]
662 return l[top:len(l)-bot]
663 return l
663 return l
664
664
665 def old(self, fuzz=0, toponly=False):
665 def old(self, fuzz=0, toponly=False):
666 return self.fuzzit(self.a, fuzz, toponly)
666 return self.fuzzit(self.a, fuzz, toponly)
667
667
668 def newctrl(self):
668 def newctrl(self):
669 res = []
669 res = []
670 for x in self.hunk:
670 for x in self.hunk:
671 c = x[0]
671 c = x[0]
672 if c == ' ' or c == '+':
672 if c == ' ' or c == '+':
673 res.append(x)
673 res.append(x)
674 return res
674 return res
675
675
676 def new(self, fuzz=0, toponly=False):
676 def new(self, fuzz=0, toponly=False):
677 return self.fuzzit(self.b, fuzz, toponly)
677 return self.fuzzit(self.b, fuzz, toponly)
678
678
679 class binhunk:
679 class binhunk:
680 'A binary patch file. Only understands literals so far.'
680 'A binary patch file. Only understands literals so far.'
681 def __init__(self, gitpatch):
681 def __init__(self, gitpatch):
682 self.gitpatch = gitpatch
682 self.gitpatch = gitpatch
683 self.text = None
683 self.text = None
684 self.hunk = ['GIT binary patch\n']
684 self.hunk = ['GIT binary patch\n']
685
685
686 def createfile(self):
686 def createfile(self):
687 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
687 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
688
688
689 def rmfile(self):
689 def rmfile(self):
690 return self.gitpatch.op == 'DELETE'
690 return self.gitpatch.op == 'DELETE'
691
691
692 def complete(self):
692 def complete(self):
693 return self.text is not None
693 return self.text is not None
694
694
695 def new(self):
695 def new(self):
696 return [self.text]
696 return [self.text]
697
697
698 def extract(self, lr):
698 def extract(self, lr):
699 line = lr.readline()
699 line = lr.readline()
700 self.hunk.append(line)
700 self.hunk.append(line)
701 while line and not line.startswith('literal '):
701 while line and not line.startswith('literal '):
702 line = lr.readline()
702 line = lr.readline()
703 self.hunk.append(line)
703 self.hunk.append(line)
704 if not line:
704 if not line:
705 raise PatchError(_('could not extract binary patch'))
705 raise PatchError(_('could not extract binary patch'))
706 size = int(line[8:].rstrip())
706 size = int(line[8:].rstrip())
707 dec = []
707 dec = []
708 line = lr.readline()
708 line = lr.readline()
709 self.hunk.append(line)
709 self.hunk.append(line)
710 while len(line) > 1:
710 while len(line) > 1:
711 l = line[0]
711 l = line[0]
712 if l <= 'Z' and l >= 'A':
712 if l <= 'Z' and l >= 'A':
713 l = ord(l) - ord('A') + 1
713 l = ord(l) - ord('A') + 1
714 else:
714 else:
715 l = ord(l) - ord('a') + 27
715 l = ord(l) - ord('a') + 27
716 dec.append(base85.b85decode(line[1:-1])[:l])
716 dec.append(base85.b85decode(line[1:-1])[:l])
717 line = lr.readline()
717 line = lr.readline()
718 self.hunk.append(line)
718 self.hunk.append(line)
719 text = zlib.decompress(''.join(dec))
719 text = zlib.decompress(''.join(dec))
720 if len(text) != size:
720 if len(text) != size:
721 raise PatchError(_('binary patch is %d bytes, not %d') %
721 raise PatchError(_('binary patch is %d bytes, not %d') %
722 len(text), size)
722 len(text), size)
723 self.text = text
723 self.text = text
724
724
725 def parsefilename(str):
725 def parsefilename(str):
726 # --- filename \t|space stuff
726 # --- filename \t|space stuff
727 s = str[4:].rstrip('\r\n')
727 s = str[4:].rstrip('\r\n')
728 i = s.find('\t')
728 i = s.find('\t')
729 if i < 0:
729 if i < 0:
730 i = s.find(' ')
730 i = s.find(' ')
731 if i < 0:
731 if i < 0:
732 return s
732 return s
733 return s[:i]
733 return s[:i]
734
734
735 def selectfile(afile_orig, bfile_orig, hunk, strip):
735 def selectfile(afile_orig, bfile_orig, hunk, strip):
736 def pathstrip(path, count=1):
736 def pathstrip(path, count=1):
737 pathlen = len(path)
737 pathlen = len(path)
738 i = 0
738 i = 0
739 if count == 0:
739 if count == 0:
740 return '', path.rstrip()
740 return '', path.rstrip()
741 while count > 0:
741 while count > 0:
742 i = path.find('/', i)
742 i = path.find('/', i)
743 if i == -1:
743 if i == -1:
744 raise PatchError(_("unable to strip away %d dirs from %s") %
744 raise PatchError(_("unable to strip away %d dirs from %s") %
745 (count, path))
745 (count, path))
746 i += 1
746 i += 1
747 # consume '//' in the path
747 # consume '//' in the path
748 while i < pathlen - 1 and path[i] == '/':
748 while i < pathlen - 1 and path[i] == '/':
749 i += 1
749 i += 1
750 count -= 1
750 count -= 1
751 return path[:i].lstrip(), path[i:].rstrip()
751 return path[:i].lstrip(), path[i:].rstrip()
752
752
753 nulla = afile_orig == "/dev/null"
753 nulla = afile_orig == "/dev/null"
754 nullb = bfile_orig == "/dev/null"
754 nullb = bfile_orig == "/dev/null"
755 abase, afile = pathstrip(afile_orig, strip)
755 abase, afile = pathstrip(afile_orig, strip)
756 gooda = not nulla and util.lexists(afile)
756 gooda = not nulla and util.lexists(afile)
757 bbase, bfile = pathstrip(bfile_orig, strip)
757 bbase, bfile = pathstrip(bfile_orig, strip)
758 if afile == bfile:
758 if afile == bfile:
759 goodb = gooda
759 goodb = gooda
760 else:
760 else:
761 goodb = not nullb and os.path.exists(bfile)
761 goodb = not nullb and os.path.exists(bfile)
762 createfunc = hunk.createfile
762 createfunc = hunk.createfile
763 missing = not goodb and not gooda and not createfunc()
763 missing = not goodb and not gooda and not createfunc()
764
764
765 # some diff programs apparently produce create patches where the
765 # some diff programs apparently produce create patches where the
766 # afile is not /dev/null, but rather the same name as the bfile
766 # afile is not /dev/null, but rather the same name as the bfile
767 if missing and afile == bfile:
767 if missing and afile == bfile:
768 # this isn't very pretty
768 # this isn't very pretty
769 hunk.create = True
769 hunk.create = True
770 if createfunc():
770 if createfunc():
771 missing = False
771 missing = False
772 else:
772 else:
773 hunk.create = False
773 hunk.create = False
774
774
775 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
775 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
776 # diff is between a file and its backup. In this case, the original
776 # diff is between a file and its backup. In this case, the original
777 # file should be patched (see original mpatch code).
777 # file should be patched (see original mpatch code).
778 isbackup = (abase == bbase and bfile.startswith(afile))
778 isbackup = (abase == bbase and bfile.startswith(afile))
779 fname = None
779 fname = None
780 if not missing:
780 if not missing:
781 if gooda and goodb:
781 if gooda and goodb:
782 fname = isbackup and afile or bfile
782 fname = isbackup and afile or bfile
783 elif gooda:
783 elif gooda:
784 fname = afile
784 fname = afile
785
785
786 if not fname:
786 if not fname:
787 if not nullb:
787 if not nullb:
788 fname = isbackup and afile or bfile
788 fname = isbackup and afile or bfile
789 elif not nulla:
789 elif not nulla:
790 fname = afile
790 fname = afile
791 else:
791 else:
792 raise PatchError(_("undefined source and destination files"))
792 raise PatchError(_("undefined source and destination files"))
793
793
794 return fname, missing
794 return fname, missing
795
795
796 def scangitpatch(lr, firstline):
796 def scangitpatch(lr, firstline):
797 """
797 """
798 Git patches can emit:
798 Git patches can emit:
799 - rename a to b
799 - rename a to b
800 - change b
800 - change b
801 - copy a to c
801 - copy a to c
802 - change c
802 - change c
803
803
804 We cannot apply this sequence as-is, the renamed 'a' could not be
804 We cannot apply this sequence as-is, the renamed 'a' could not be
805 found for it would have been renamed already. And we cannot copy
805 found for it would have been renamed already. And we cannot copy
806 from 'b' instead because 'b' would have been changed already. So
806 from 'b' instead because 'b' would have been changed already. So
807 we scan the git patch for copy and rename commands so we can
807 we scan the git patch for copy and rename commands so we can
808 perform the copies ahead of time.
808 perform the copies ahead of time.
809 """
809 """
810 pos = 0
810 pos = 0
811 try:
811 try:
812 pos = lr.fp.tell()
812 pos = lr.fp.tell()
813 fp = lr.fp
813 fp = lr.fp
814 except IOError:
814 except IOError:
815 fp = cStringIO.StringIO(lr.fp.read())
815 fp = cStringIO.StringIO(lr.fp.read())
816 gitlr = linereader(fp, lr.textmode)
816 gitlr = linereader(fp, lr.textmode)
817 gitlr.push(firstline)
817 gitlr.push(firstline)
818 (dopatch, gitpatches) = readgitpatch(gitlr)
818 (dopatch, gitpatches) = readgitpatch(gitlr)
819 fp.seek(pos)
819 fp.seek(pos)
820 return dopatch, gitpatches
820 return dopatch, gitpatches
821
821
822 def iterhunks(ui, fp, sourcefile=None, textmode=False):
822 def iterhunks(ui, fp, sourcefile=None, textmode=False):
823 """Read a patch and yield the following events:
823 """Read a patch and yield the following events:
824 - ("file", afile, bfile, firsthunk): select a new target file.
824 - ("file", afile, bfile, firsthunk): select a new target file.
825 - ("hunk", hunk): a new hunk is ready to be applied, follows a
825 - ("hunk", hunk): a new hunk is ready to be applied, follows a
826 "file" event.
826 "file" event.
827 - ("git", gitchanges): current diff is in git format, gitchanges
827 - ("git", gitchanges): current diff is in git format, gitchanges
828 maps filenames to gitpatch records. Unique event.
828 maps filenames to gitpatch records. Unique event.
829
829
830 If textmode is True, input line-endings are normalized to LF.
830 If textmode is True, input line-endings are normalized to LF.
831 """
831 """
832 changed = {}
832 changed = {}
833 current_hunk = None
833 current_hunk = None
834 afile = ""
834 afile = ""
835 bfile = ""
835 bfile = ""
836 state = None
836 state = None
837 hunknum = 0
837 hunknum = 0
838 emitfile = False
838 emitfile = False
839 git = False
839 git = False
840
840
841 # our states
841 # our states
842 BFILE = 1
842 BFILE = 1
843 context = None
843 context = None
844 lr = linereader(fp, textmode)
844 lr = linereader(fp, textmode)
845 dopatch = True
845 dopatch = True
846 # gitworkdone is True if a git operation (copy, rename, ...) was
846 # gitworkdone is True if a git operation (copy, rename, ...) was
847 # performed already for the current file. Useful when the file
847 # performed already for the current file. Useful when the file
848 # section may have no hunk.
848 # section may have no hunk.
849 gitworkdone = False
849 gitworkdone = False
850
850
851 while True:
851 while True:
852 newfile = False
852 newfile = False
853 x = lr.readline()
853 x = lr.readline()
854 if not x:
854 if not x:
855 break
855 break
856 if current_hunk:
856 if current_hunk:
857 if x.startswith('\ '):
857 if x.startswith('\ '):
858 current_hunk.fix_newline()
858 current_hunk.fix_newline()
859 yield 'hunk', current_hunk
859 yield 'hunk', current_hunk
860 current_hunk = None
860 current_hunk = None
861 gitworkdone = False
861 gitworkdone = False
862 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
862 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
863 ((context is not False) and x.startswith('***************')))):
863 ((context is not False) and x.startswith('***************')))):
864 try:
864 try:
865 if context is None and x.startswith('***************'):
865 if context is None and x.startswith('***************'):
866 context = True
866 context = True
867 gpatch = changed.get(bfile)
867 gpatch = changed.get(bfile)
868 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
868 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
869 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
869 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
870 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
870 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
871 except PatchError, err:
871 except PatchError, err:
872 ui.debug(err)
872 ui.debug(err)
873 current_hunk = None
873 current_hunk = None
874 continue
874 continue
875 hunknum += 1
875 hunknum += 1
876 if emitfile:
876 if emitfile:
877 emitfile = False
877 emitfile = False
878 yield 'file', (afile, bfile, current_hunk)
878 yield 'file', (afile, bfile, current_hunk)
879 elif state == BFILE and x.startswith('GIT binary patch'):
879 elif state == BFILE and x.startswith('GIT binary patch'):
880 current_hunk = binhunk(changed[bfile])
880 current_hunk = binhunk(changed[bfile])
881 hunknum += 1
881 hunknum += 1
882 if emitfile:
882 if emitfile:
883 emitfile = False
883 emitfile = False
884 yield 'file', ('a/' + afile, 'b/' + bfile, current_hunk)
884 yield 'file', ('a/' + afile, 'b/' + bfile, current_hunk)
885 current_hunk.extract(lr)
885 current_hunk.extract(lr)
886 elif x.startswith('diff --git'):
886 elif x.startswith('diff --git'):
887 # check for git diff, scanning the whole patch file if needed
887 # check for git diff, scanning the whole patch file if needed
888 m = gitre.match(x)
888 m = gitre.match(x)
889 if m:
889 if m:
890 afile, bfile = m.group(1, 2)
890 afile, bfile = m.group(1, 2)
891 if not git:
891 if not git:
892 git = True
892 git = True
893 dopatch, gitpatches = scangitpatch(lr, x)
893 dopatch, gitpatches = scangitpatch(lr, x)
894 yield 'git', gitpatches
894 yield 'git', gitpatches
895 for gp in gitpatches:
895 for gp in gitpatches:
896 changed[gp.path] = gp
896 changed[gp.path] = gp
897 # else error?
897 # else error?
898 # copy/rename + modify should modify target, not source
898 # copy/rename + modify should modify target, not source
899 gp = changed.get(bfile)
899 gp = changed.get(bfile)
900 if gp and gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD'):
900 if gp and gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD'):
901 afile = bfile
901 afile = bfile
902 gitworkdone = True
902 gitworkdone = True
903 newfile = True
903 newfile = True
904 elif x.startswith('---'):
904 elif x.startswith('---'):
905 # check for a unified diff
905 # check for a unified diff
906 l2 = lr.readline()
906 l2 = lr.readline()
907 if not l2.startswith('+++'):
907 if not l2.startswith('+++'):
908 lr.push(l2)
908 lr.push(l2)
909 continue
909 continue
910 newfile = True
910 newfile = True
911 context = False
911 context = False
912 afile = parsefilename(x)
912 afile = parsefilename(x)
913 bfile = parsefilename(l2)
913 bfile = parsefilename(l2)
914 elif x.startswith('***'):
914 elif x.startswith('***'):
915 # check for a context diff
915 # check for a context diff
916 l2 = lr.readline()
916 l2 = lr.readline()
917 if not l2.startswith('---'):
917 if not l2.startswith('---'):
918 lr.push(l2)
918 lr.push(l2)
919 continue
919 continue
920 l3 = lr.readline()
920 l3 = lr.readline()
921 lr.push(l3)
921 lr.push(l3)
922 if not l3.startswith("***************"):
922 if not l3.startswith("***************"):
923 lr.push(l2)
923 lr.push(l2)
924 continue
924 continue
925 newfile = True
925 newfile = True
926 context = True
926 context = True
927 afile = parsefilename(x)
927 afile = parsefilename(x)
928 bfile = parsefilename(l2)
928 bfile = parsefilename(l2)
929
929
930 if newfile:
930 if newfile:
931 emitfile = True
931 emitfile = True
932 state = BFILE
932 state = BFILE
933 hunknum = 0
933 hunknum = 0
934 if current_hunk:
934 if current_hunk:
935 if current_hunk.complete():
935 if current_hunk.complete():
936 yield 'hunk', current_hunk
936 yield 'hunk', current_hunk
937 else:
937 else:
938 raise PatchError(_("malformed patch %s %s") % (afile,
938 raise PatchError(_("malformed patch %s %s") % (afile,
939 current_hunk.desc))
939 current_hunk.desc))
940
940
941 if hunknum == 0 and dopatch and not gitworkdone:
941 if hunknum == 0 and dopatch and not gitworkdone:
942 raise NoHunks
942 raise NoHunks
943
943
944 def applydiff(ui, fp, changed, strip=1, sourcefile=None, eol=None):
944 def applydiff(ui, fp, changed, strip=1, sourcefile=None, eol=None):
945 """
945 """
946 Reads a patch from fp and tries to apply it.
946 Reads a patch from fp and tries to apply it.
947
947
948 The dict 'changed' is filled in with all of the filenames changed
948 The dict 'changed' is filled in with all of the filenames changed
949 by the patch. Returns 0 for a clean patch, -1 if any rejects were
949 by the patch. Returns 0 for a clean patch, -1 if any rejects were
950 found and 1 if there was any fuzz.
950 found and 1 if there was any fuzz.
951
951
952 If 'eol' is None, the patch content and patched file are read in
952 If 'eol' is None, the patch content and patched file are read in
953 binary mode. Otherwise, line endings are ignored when patching then
953 binary mode. Otherwise, line endings are ignored when patching then
954 normalized to 'eol' (usually '\n' or \r\n').
954 normalized to 'eol' (usually '\n' or \r\n').
955 """
955 """
956 rejects = 0
956 rejects = 0
957 err = 0
957 err = 0
958 current_file = None
958 current_file = None
959 gitpatches = None
959 gitpatches = None
960 opener = util.opener(os.getcwd())
960 opener = util.opener(os.getcwd())
961 textmode = eol is not None
961 textmode = eol is not None
962
962
963 def closefile():
963 def closefile():
964 if not current_file:
964 if not current_file:
965 return 0
965 return 0
966 current_file.close()
966 current_file.close()
967 return len(current_file.rej)
967 return len(current_file.rej)
968
968
969 for state, values in iterhunks(ui, fp, sourcefile, textmode):
969 for state, values in iterhunks(ui, fp, sourcefile, textmode):
970 if state == 'hunk':
970 if state == 'hunk':
971 if not current_file:
971 if not current_file:
972 continue
972 continue
973 current_hunk = values
973 current_hunk = values
974 ret = current_file.apply(current_hunk)
974 ret = current_file.apply(current_hunk)
975 if ret >= 0:
975 if ret >= 0:
976 changed.setdefault(current_file.fname, None)
976 changed.setdefault(current_file.fname, None)
977 if ret > 0:
977 if ret > 0:
978 err = 1
978 err = 1
979 elif state == 'file':
979 elif state == 'file':
980 rejects += closefile()
980 rejects += closefile()
981 afile, bfile, first_hunk = values
981 afile, bfile, first_hunk = values
982 try:
982 try:
983 if sourcefile:
983 if sourcefile:
984 current_file = patchfile(ui, sourcefile, opener, eol=eol)
984 current_file = patchfile(ui, sourcefile, opener, eol=eol)
985 else:
985 else:
986 current_file, missing = selectfile(afile, bfile, first_hunk,
986 current_file, missing = selectfile(afile, bfile, first_hunk,
987 strip)
987 strip)
988 current_file = patchfile(ui, current_file, opener, missing, eol)
988 current_file = patchfile(ui, current_file, opener, missing, eol)
989 except PatchError, err:
989 except PatchError, err:
990 ui.warn(str(err) + '\n')
990 ui.warn(str(err) + '\n')
991 current_file, current_hunk = None, None
991 current_file, current_hunk = None, None
992 rejects += 1
992 rejects += 1
993 continue
993 continue
994 elif state == 'git':
994 elif state == 'git':
995 gitpatches = values
995 gitpatches = values
996 cwd = os.getcwd()
996 cwd = os.getcwd()
997 for gp in gitpatches:
997 for gp in gitpatches:
998 if gp.op in ('COPY', 'RENAME'):
998 if gp.op in ('COPY', 'RENAME'):
999 copyfile(gp.oldpath, gp.path, cwd)
999 copyfile(gp.oldpath, gp.path, cwd)
1000 changed[gp.path] = gp
1000 changed[gp.path] = gp
1001 else:
1001 else:
1002 raise util.Abort(_('unsupported parser state: %s') % state)
1002 raise util.Abort(_('unsupported parser state: %s') % state)
1003
1003
1004 rejects += closefile()
1004 rejects += closefile()
1005
1005
1006 if rejects:
1006 if rejects:
1007 return -1
1007 return -1
1008 return err
1008 return err
1009
1009
1010 def diffopts(ui, opts={}, untrusted=False):
1010 def diffopts(ui, opts={}, untrusted=False):
1011 def get(key, name=None, getter=ui.configbool):
1011 def get(key, name=None, getter=ui.configbool):
1012 return (opts.get(key) or
1012 return (opts.get(key) or
1013 getter('diff', name or key, None, untrusted=untrusted))
1013 getter('diff', name or key, None, untrusted=untrusted))
1014 return mdiff.diffopts(
1014 return mdiff.diffopts(
1015 text=opts.get('text'),
1015 text=opts.get('text'),
1016 git=get('git'),
1016 git=get('git'),
1017 nodates=get('nodates'),
1017 nodates=get('nodates'),
1018 showfunc=get('show_function', 'showfunc'),
1018 showfunc=get('show_function', 'showfunc'),
1019 ignorews=get('ignore_all_space', 'ignorews'),
1019 ignorews=get('ignore_all_space', 'ignorews'),
1020 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1020 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1021 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1021 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1022 context=get('unified', getter=ui.config))
1022 context=get('unified', getter=ui.config))
1023
1023
1024 def updatedir(ui, repo, patches, similarity=0):
1024 def updatedir(ui, repo, patches, similarity=0):
1025 '''Update dirstate after patch application according to metadata'''
1025 '''Update dirstate after patch application according to metadata'''
1026 if not patches:
1026 if not patches:
1027 return
1027 return
1028 copies = []
1028 copies = []
1029 removes = set()
1029 removes = set()
1030 cfiles = patches.keys()
1030 cfiles = patches.keys()
1031 cwd = repo.getcwd()
1031 cwd = repo.getcwd()
1032 if cwd:
1032 if cwd:
1033 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
1033 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
1034 for f in patches:
1034 for f in patches:
1035 gp = patches[f]
1035 gp = patches[f]
1036 if not gp:
1036 if not gp:
1037 continue
1037 continue
1038 if gp.op == 'RENAME':
1038 if gp.op == 'RENAME':
1039 copies.append((gp.oldpath, gp.path))
1039 copies.append((gp.oldpath, gp.path))
1040 removes.add(gp.oldpath)
1040 removes.add(gp.oldpath)
1041 elif gp.op == 'COPY':
1041 elif gp.op == 'COPY':
1042 copies.append((gp.oldpath, gp.path))
1042 copies.append((gp.oldpath, gp.path))
1043 elif gp.op == 'DELETE':
1043 elif gp.op == 'DELETE':
1044 removes.add(gp.path)
1044 removes.add(gp.path)
1045 for src, dst in copies:
1045 for src, dst in copies:
1046 repo.copy(src, dst)
1046 repo.copy(src, dst)
1047 if (not similarity) and removes:
1047 if (not similarity) and removes:
1048 repo.remove(sorted(removes), True)
1048 repo.remove(sorted(removes), True)
1049 for f in patches:
1049 for f in patches:
1050 gp = patches[f]
1050 gp = patches[f]
1051 if gp and gp.mode:
1051 if gp and gp.mode:
1052 islink, isexec = gp.mode
1052 islink, isexec = gp.mode
1053 dst = repo.wjoin(gp.path)
1053 dst = repo.wjoin(gp.path)
1054 # patch won't create empty files
1054 # patch won't create empty files
1055 if gp.op == 'ADD' and not os.path.exists(dst):
1055 if gp.op == 'ADD' and not os.path.exists(dst):
1056 flags = (isexec and 'x' or '') + (islink and 'l' or '')
1056 flags = (isexec and 'x' or '') + (islink and 'l' or '')
1057 repo.wwrite(gp.path, '', flags)
1057 repo.wwrite(gp.path, '', flags)
1058 elif gp.op != 'DELETE':
1058 elif gp.op != 'DELETE':
1059 util.set_flags(dst, islink, isexec)
1059 util.set_flags(dst, islink, isexec)
1060 cmdutil.addremove(repo, cfiles, similarity=similarity)
1060 cmdutil.addremove(repo, cfiles, similarity=similarity)
1061 files = patches.keys()
1061 files = patches.keys()
1062 files.extend([r for r in removes if r not in files])
1062 files.extend([r for r in removes if r not in files])
1063 return sorted(files)
1063 return sorted(files)
1064
1064
1065 def externalpatch(patcher, args, patchname, ui, strip, cwd, files):
1065 def externalpatch(patcher, args, patchname, ui, strip, cwd, files):
1066 """use <patcher> to apply <patchname> to the working directory.
1066 """use <patcher> to apply <patchname> to the working directory.
1067 returns whether patch was applied with fuzz factor."""
1067 returns whether patch was applied with fuzz factor."""
1068
1068
1069 fuzz = False
1069 fuzz = False
1070 if cwd:
1070 if cwd:
1071 args.append('-d %s' % util.shellquote(cwd))
1071 args.append('-d %s' % util.shellquote(cwd))
1072 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1072 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1073 util.shellquote(patchname)))
1073 util.shellquote(patchname)))
1074
1074
1075 for line in fp:
1075 for line in fp:
1076 line = line.rstrip()
1076 line = line.rstrip()
1077 ui.note(line + '\n')
1077 ui.note(line + '\n')
1078 if line.startswith('patching file '):
1078 if line.startswith('patching file '):
1079 pf = util.parse_patch_output(line)
1079 pf = util.parse_patch_output(line)
1080 printed_file = False
1080 printed_file = False
1081 files.setdefault(pf, None)
1081 files.setdefault(pf, None)
1082 elif line.find('with fuzz') >= 0:
1082 elif line.find('with fuzz') >= 0:
1083 fuzz = True
1083 fuzz = True
1084 if not printed_file:
1084 if not printed_file:
1085 ui.warn(pf + '\n')
1085 ui.warn(pf + '\n')
1086 printed_file = True
1086 printed_file = True
1087 ui.warn(line + '\n')
1087 ui.warn(line + '\n')
1088 elif line.find('saving rejects to file') >= 0:
1088 elif line.find('saving rejects to file') >= 0:
1089 ui.warn(line + '\n')
1089 ui.warn(line + '\n')
1090 elif line.find('FAILED') >= 0:
1090 elif line.find('FAILED') >= 0:
1091 if not printed_file:
1091 if not printed_file:
1092 ui.warn(pf + '\n')
1092 ui.warn(pf + '\n')
1093 printed_file = True
1093 printed_file = True
1094 ui.warn(line + '\n')
1094 ui.warn(line + '\n')
1095 code = fp.close()
1095 code = fp.close()
1096 if code:
1096 if code:
1097 raise PatchError(_("patch command failed: %s") %
1097 raise PatchError(_("patch command failed: %s") %
1098 util.explain_exit(code)[0])
1098 util.explain_exit(code)[0])
1099 return fuzz
1099 return fuzz
1100
1100
1101 def internalpatch(patchobj, ui, strip, cwd, files={}, eolmode='strict'):
1101 def internalpatch(patchobj, ui, strip, cwd, files={}, eolmode='strict'):
1102 """use builtin patch to apply <patchobj> to the working directory.
1102 """use builtin patch to apply <patchobj> to the working directory.
1103 returns whether patch was applied with fuzz factor."""
1103 returns whether patch was applied with fuzz factor."""
1104
1104
1105 if eolmode is None:
1105 if eolmode is None:
1106 eolmode = ui.config('patch', 'eol', 'strict')
1106 eolmode = ui.config('patch', 'eol', 'strict')
1107 try:
1107 try:
1108 eol = {'strict': None, 'crlf': '\r\n', 'lf': '\n'}[eolmode.lower()]
1108 eol = {'strict': None, 'crlf': '\r\n', 'lf': '\n'}[eolmode.lower()]
1109 except KeyError:
1109 except KeyError:
1110 raise util.Abort(_('Unsupported line endings type: %s') % eolmode)
1110 raise util.Abort(_('Unsupported line endings type: %s') % eolmode)
1111
1111
1112 try:
1112 try:
1113 fp = open(patchobj, 'rb')
1113 fp = open(patchobj, 'rb')
1114 except TypeError:
1114 except TypeError:
1115 fp = patchobj
1115 fp = patchobj
1116 if cwd:
1116 if cwd:
1117 curdir = os.getcwd()
1117 curdir = os.getcwd()
1118 os.chdir(cwd)
1118 os.chdir(cwd)
1119 try:
1119 try:
1120 ret = applydiff(ui, fp, files, strip=strip, eol=eol)
1120 ret = applydiff(ui, fp, files, strip=strip, eol=eol)
1121 finally:
1121 finally:
1122 if cwd:
1122 if cwd:
1123 os.chdir(curdir)
1123 os.chdir(curdir)
1124 if ret < 0:
1124 if ret < 0:
1125 raise PatchError
1125 raise PatchError
1126 return ret > 0
1126 return ret > 0
1127
1127
1128 def patch(patchname, ui, strip=1, cwd=None, files={}, eolmode='strict'):
1128 def patch(patchname, ui, strip=1, cwd=None, files={}, eolmode='strict'):
1129 """Apply <patchname> to the working directory.
1129 """Apply <patchname> to the working directory.
1130
1130
1131 'eolmode' specifies how end of lines should be handled. It can be:
1131 'eolmode' specifies how end of lines should be handled. It can be:
1132 - 'strict': inputs are read in binary mode, EOLs are preserved
1132 - 'strict': inputs are read in binary mode, EOLs are preserved
1133 - 'crlf': EOLs are ignored when patching and reset to CRLF
1133 - 'crlf': EOLs are ignored when patching and reset to CRLF
1134 - 'lf': EOLs are ignored when patching and reset to LF
1134 - 'lf': EOLs are ignored when patching and reset to LF
1135 - None: get it from user settings, default to 'strict'
1135 - None: get it from user settings, default to 'strict'
1136 'eolmode' is ignored when using an external patcher program.
1136 'eolmode' is ignored when using an external patcher program.
1137
1137
1138 Returns whether patch was applied with fuzz factor.
1138 Returns whether patch was applied with fuzz factor.
1139 """
1139 """
1140 patcher = ui.config('ui', 'patch')
1140 patcher = ui.config('ui', 'patch')
1141 args = []
1141 args = []
1142 try:
1142 try:
1143 if patcher:
1143 if patcher:
1144 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1144 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1145 files)
1145 files)
1146 else:
1146 else:
1147 try:
1147 try:
1148 return internalpatch(patchname, ui, strip, cwd, files, eolmode)
1148 return internalpatch(patchname, ui, strip, cwd, files, eolmode)
1149 except NoHunks:
1149 except NoHunks:
1150 patcher = util.find_exe('gpatch') or util.find_exe('patch') or 'patch'
1150 patcher = util.find_exe('gpatch') or util.find_exe('patch') or 'patch'
1151 ui.debug('no valid hunks found; trying with %r instead\n' %
1151 ui.debug('no valid hunks found; trying with %r instead\n' %
1152 patcher)
1152 patcher)
1153 if util.needbinarypatch():
1153 if util.needbinarypatch():
1154 args.append('--binary')
1154 args.append('--binary')
1155 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1155 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1156 files)
1156 files)
1157 except PatchError, err:
1157 except PatchError, err:
1158 s = str(err)
1158 s = str(err)
1159 if s:
1159 if s:
1160 raise util.Abort(s)
1160 raise util.Abort(s)
1161 else:
1161 else:
1162 raise util.Abort(_('patch failed to apply'))
1162 raise util.Abort(_('patch failed to apply'))
1163
1163
1164 def b85diff(to, tn):
1164 def b85diff(to, tn):
1165 '''print base85-encoded binary diff'''
1165 '''print base85-encoded binary diff'''
1166 def gitindex(text):
1166 def gitindex(text):
1167 if not text:
1167 if not text:
1168 return '0' * 40
1168 return '0' * 40
1169 l = len(text)
1169 l = len(text)
1170 s = util.sha1('blob %d\0' % l)
1170 s = util.sha1('blob %d\0' % l)
1171 s.update(text)
1171 s.update(text)
1172 return s.hexdigest()
1172 return s.hexdigest()
1173
1173
1174 def fmtline(line):
1174 def fmtline(line):
1175 l = len(line)
1175 l = len(line)
1176 if l <= 26:
1176 if l <= 26:
1177 l = chr(ord('A') + l - 1)
1177 l = chr(ord('A') + l - 1)
1178 else:
1178 else:
1179 l = chr(l - 26 + ord('a') - 1)
1179 l = chr(l - 26 + ord('a') - 1)
1180 return '%c%s\n' % (l, base85.b85encode(line, True))
1180 return '%c%s\n' % (l, base85.b85encode(line, True))
1181
1181
1182 def chunk(text, csize=52):
1182 def chunk(text, csize=52):
1183 l = len(text)
1183 l = len(text)
1184 i = 0
1184 i = 0
1185 while i < l:
1185 while i < l:
1186 yield text[i:i+csize]
1186 yield text[i:i+csize]
1187 i += csize
1187 i += csize
1188
1188
1189 tohash = gitindex(to)
1189 tohash = gitindex(to)
1190 tnhash = gitindex(tn)
1190 tnhash = gitindex(tn)
1191 if tohash == tnhash:
1191 if tohash == tnhash:
1192 return ""
1192 return ""
1193
1193
1194 # TODO: deltas
1194 # TODO: deltas
1195 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1195 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1196 (tohash, tnhash, len(tn))]
1196 (tohash, tnhash, len(tn))]
1197 for l in chunk(zlib.compress(tn)):
1197 for l in chunk(zlib.compress(tn)):
1198 ret.append(fmtline(l))
1198 ret.append(fmtline(l))
1199 ret.append('\n')
1199 ret.append('\n')
1200 return ''.join(ret)
1200 return ''.join(ret)
1201
1201
1202 def _addmodehdr(header, omode, nmode):
1202 def _addmodehdr(header, omode, nmode):
1203 if omode != nmode:
1203 if omode != nmode:
1204 header.append('old mode %s\n' % omode)
1204 header.append('old mode %s\n' % omode)
1205 header.append('new mode %s\n' % nmode)
1205 header.append('new mode %s\n' % nmode)
1206
1206
1207 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None):
1207 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None):
1208 '''yields diff of changes to files between two nodes, or node and
1208 '''yields diff of changes to files between two nodes, or node and
1209 working directory.
1209 working directory.
1210
1210
1211 if node1 is None, use first dirstate parent instead.
1211 if node1 is None, use first dirstate parent instead.
1212 if node2 is None, compare node1 with working directory.'''
1212 if node2 is None, compare node1 with working directory.'''
1213
1213
1214 if opts is None:
1214 if opts is None:
1215 opts = mdiff.defaultopts
1215 opts = mdiff.defaultopts
1216
1216
1217 if not node1:
1217 if not node1:
1218 node1 = repo.dirstate.parents()[0]
1218 node1 = repo.dirstate.parents()[0]
1219
1219
1220 def lrugetfilectx():
1220 def lrugetfilectx():
1221 cache = {}
1221 cache = {}
1222 order = []
1222 order = []
1223 def getfilectx(f, ctx):
1223 def getfilectx(f, ctx):
1224 fctx = ctx.filectx(f, filelog=cache.get(f))
1224 fctx = ctx.filectx(f, filelog=cache.get(f))
1225 if f not in cache:
1225 if f not in cache:
1226 if len(cache) > 20:
1226 if len(cache) > 20:
1227 del cache[order.pop(0)]
1227 del cache[order.pop(0)]
1228 cache[f] = fctx._filelog
1228 cache[f] = fctx._filelog
1229 else:
1229 else:
1230 order.remove(f)
1230 order.remove(f)
1231 order.append(f)
1231 order.append(f)
1232 return fctx
1232 return fctx
1233 return getfilectx
1233 return getfilectx
1234 getfilectx = lrugetfilectx()
1234 getfilectx = lrugetfilectx()
1235
1235
1236 ctx1 = repo[node1]
1236 ctx1 = repo[node1]
1237 ctx2 = repo[node2]
1237 ctx2 = repo[node2]
1238
1238
1239 if not changes:
1239 if not changes:
1240 changes = repo.status(ctx1, ctx2, match=match)
1240 changes = repo.status(ctx1, ctx2, match=match)
1241 modified, added, removed = changes[:3]
1241 modified, added, removed = changes[:3]
1242
1242
1243 if not modified and not added and not removed:
1243 if not modified and not added and not removed:
1244 return
1244 return
1245
1245
1246 date1 = util.datestr(ctx1.date())
1246 date1 = util.datestr(ctx1.date())
1247 man1 = ctx1.manifest()
1247 man1 = ctx1.manifest()
1248
1248
1249 if repo.ui.quiet:
1249 if repo.ui.quiet:
1250 r = None
1250 r = None
1251 else:
1251 else:
1252 hexfunc = repo.ui.debugflag and hex or short
1252 hexfunc = repo.ui.debugflag and hex or short
1253 r = [hexfunc(node) for node in [node1, node2] if node]
1253 r = [hexfunc(node) for node in [node1, node2] if node]
1254
1254
1255 if opts.git:
1255 if opts.git:
1256 copy, diverge = copies.copies(repo, ctx1, ctx2, repo[nullid])
1256 copy, diverge = copies.copies(repo, ctx1, ctx2, repo[nullid])
1257 copy = copy.copy()
1257 copy = copy.copy()
1258 for k, v in copy.items():
1258 for k, v in copy.items():
1259 copy[v] = k
1259 copy[v] = k
1260
1260
1261 gone = set()
1261 gone = set()
1262 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1262 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1263
1263
1264 for f in sorted(modified + added + removed):
1264 for f in sorted(modified + added + removed):
1265 to = None
1265 to = None
1266 tn = None
1266 tn = None
1267 dodiff = True
1267 dodiff = True
1268 header = []
1268 header = []
1269 if f in man1:
1269 if f in man1:
1270 to = getfilectx(f, ctx1).data()
1270 to = getfilectx(f, ctx1).data()
1271 if f not in removed:
1271 if f not in removed:
1272 tn = getfilectx(f, ctx2).data()
1272 tn = getfilectx(f, ctx2).data()
1273 a, b = f, f
1273 a, b = f, f
1274 if opts.git:
1274 if opts.git:
1275 if f in added:
1275 if f in added:
1276 mode = gitmode[ctx2.flags(f)]
1276 mode = gitmode[ctx2.flags(f)]
1277 if f in copy:
1277 if f in copy:
1278 a = copy[f]
1278 a = copy[f]
1279 omode = gitmode[man1.flags(a)]
1279 omode = gitmode[man1.flags(a)]
1280 _addmodehdr(header, omode, mode)
1280 _addmodehdr(header, omode, mode)
1281 if a in removed and a not in gone:
1281 if a in removed and a not in gone:
1282 op = 'rename'
1282 op = 'rename'
1283 gone.add(a)
1283 gone.add(a)
1284 else:
1284 else:
1285 op = 'copy'
1285 op = 'copy'
1286 header.append('%s from %s\n' % (op, a))
1286 header.append('%s from %s\n' % (op, a))
1287 header.append('%s to %s\n' % (op, f))
1287 header.append('%s to %s\n' % (op, f))
1288 to = getfilectx(a, ctx1).data()
1288 to = getfilectx(a, ctx1).data()
1289 else:
1289 else:
1290 header.append('new file mode %s\n' % mode)
1290 header.append('new file mode %s\n' % mode)
1291 if util.binary(tn):
1291 if util.binary(tn):
1292 dodiff = 'binary'
1292 dodiff = 'binary'
1293 elif f in removed:
1293 elif f in removed:
1294 # have we already reported a copy above?
1294 # have we already reported a copy above?
1295 if f in copy and copy[f] in added and copy[copy[f]] == f:
1295 if f in copy and copy[f] in added and copy[copy[f]] == f:
1296 dodiff = False
1296 dodiff = False
1297 else:
1297 else:
1298 header.append('deleted file mode %s\n' %
1298 header.append('deleted file mode %s\n' %
1299 gitmode[man1.flags(f)])
1299 gitmode[man1.flags(f)])
1300 else:
1300 else:
1301 omode = gitmode[man1.flags(f)]
1301 omode = gitmode[man1.flags(f)]
1302 nmode = gitmode[ctx2.flags(f)]
1302 nmode = gitmode[ctx2.flags(f)]
1303 _addmodehdr(header, omode, nmode)
1303 _addmodehdr(header, omode, nmode)
1304 if util.binary(to) or util.binary(tn):
1304 if util.binary(to) or util.binary(tn):
1305 dodiff = 'binary'
1305 dodiff = 'binary'
1306 r = None
1306 r = None
1307 header.insert(0, mdiff.diffline(r, a, b, opts))
1307 header.insert(0, mdiff.diffline(r, a, b, opts))
1308 if dodiff:
1308 if dodiff:
1309 if dodiff == 'binary':
1309 if dodiff == 'binary':
1310 text = b85diff(to, tn)
1310 text = b85diff(to, tn)
1311 else:
1311 else:
1312 text = mdiff.unidiff(to, date1,
1312 text = mdiff.unidiff(to, date1,
1313 # ctx2 date may be dynamic
1313 # ctx2 date may be dynamic
1314 tn, util.datestr(ctx2.date()),
1314 tn, util.datestr(ctx2.date()),
1315 a, b, r, opts=opts)
1315 a, b, r, opts=opts)
1316 if header and (text or len(header) > 1):
1316 if header and (text or len(header) > 1):
1317 yield ''.join(header)
1317 yield ''.join(header)
1318 if text:
1318 if text:
1319 yield text
1319 yield text
1320
1320
1321 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1321 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1322 opts=None):
1322 opts=None):
1323 '''export changesets as hg patches.'''
1323 '''export changesets as hg patches.'''
1324
1324
1325 total = len(revs)
1325 total = len(revs)
1326 revwidth = max([len(str(rev)) for rev in revs])
1326 revwidth = max([len(str(rev)) for rev in revs])
1327
1327
1328 def single(rev, seqno, fp):
1328 def single(rev, seqno, fp):
1329 ctx = repo[rev]
1329 ctx = repo[rev]
1330 node = ctx.node()
1330 node = ctx.node()
1331 parents = [p.node() for p in ctx.parents() if p]
1331 parents = [p.node() for p in ctx.parents() if p]
1332 branch = ctx.branch()
1332 branch = ctx.branch()
1333 if switch_parent:
1333 if switch_parent:
1334 parents.reverse()
1334 parents.reverse()
1335 prev = (parents and parents[0]) or nullid
1335 prev = (parents and parents[0]) or nullid
1336
1336
1337 if not fp:
1337 if not fp:
1338 fp = cmdutil.make_file(repo, template, node, total=total,
1338 fp = cmdutil.make_file(repo, template, node, total=total,
1339 seqno=seqno, revwidth=revwidth,
1339 seqno=seqno, revwidth=revwidth,
1340 mode='ab')
1340 mode='ab')
1341 if fp != sys.stdout and hasattr(fp, 'name'):
1341 if fp != sys.stdout and hasattr(fp, 'name'):
1342 repo.ui.note("%s\n" % fp.name)
1342 repo.ui.note("%s\n" % fp.name)
1343
1343
1344 fp.write("# HG changeset patch\n")
1344 fp.write("# HG changeset patch\n")
1345 fp.write("# User %s\n" % ctx.user())
1345 fp.write("# User %s\n" % ctx.user())
1346 fp.write("# Date %d %d\n" % ctx.date())
1346 fp.write("# Date %d %d\n" % ctx.date())
1347 if branch and (branch != 'default'):
1347 if branch and (branch != 'default'):
1348 fp.write("# Branch %s\n" % branch)
1348 fp.write("# Branch %s\n" % branch)
1349 fp.write("# Node ID %s\n" % hex(node))
1349 fp.write("# Node ID %s\n" % hex(node))
1350 fp.write("# Parent %s\n" % hex(prev))
1350 fp.write("# Parent %s\n" % hex(prev))
1351 if len(parents) > 1:
1351 if len(parents) > 1:
1352 fp.write("# Parent %s\n" % hex(parents[1]))
1352 fp.write("# Parent %s\n" % hex(parents[1]))
1353 fp.write(ctx.description().rstrip())
1353 fp.write(ctx.description().rstrip())
1354 fp.write("\n\n")
1354 fp.write("\n\n")
1355
1355
1356 for chunk in diff(repo, prev, node, opts=opts):
1356 for chunk in diff(repo, prev, node, opts=opts):
1357 fp.write(chunk)
1357 fp.write(chunk)
1358
1358
1359 for seqno, rev in enumerate(revs):
1359 for seqno, rev in enumerate(revs):
1360 single(rev, seqno+1, fp)
1360 single(rev, seqno+1, fp)
1361
1361
1362 def diffstatdata(lines):
1362 def diffstatdata(lines):
1363 filename, adds, removes = None, 0, 0
1363 filename, adds, removes = None, 0, 0
1364 for line in lines:
1364 for line in lines:
1365 if line.startswith('diff'):
1365 if line.startswith('diff'):
1366 if filename:
1366 if filename:
1367 yield (filename, adds, removes)
1367 isbinary = adds == 0 and removes == 0
1368 yield (filename, adds, removes, isbinary)
1368 # set numbers to 0 anyway when starting new file
1369 # set numbers to 0 anyway when starting new file
1369 adds, removes = 0, 0
1370 adds, removes = 0, 0
1370 if line.startswith('diff --git'):
1371 if line.startswith('diff --git'):
1371 filename = gitre.search(line).group(1)
1372 filename = gitre.search(line).group(1)
1372 else:
1373 else:
1373 # format: "diff -r ... -r ... filename"
1374 # format: "diff -r ... -r ... filename"
1374 filename = line.split(None, 5)[-1]
1375 filename = line.split(None, 5)[-1]
1375 elif line.startswith('+') and not line.startswith('+++'):
1376 elif line.startswith('+') and not line.startswith('+++'):
1376 adds += 1
1377 adds += 1
1377 elif line.startswith('-') and not line.startswith('---'):
1378 elif line.startswith('-') and not line.startswith('---'):
1378 removes += 1
1379 removes += 1
1379 if filename:
1380 if filename:
1380 yield (filename, adds, removes)
1381 isbinary = adds == 0 and removes == 0
1382 yield (filename, adds, removes, isbinary)
1381
1383
1382 def diffstat(lines, width=80):
1384 def diffstat(lines, width=80, git=False):
1383 output = []
1385 output = []
1384 stats = list(diffstatdata(lines))
1386 stats = list(diffstatdata(lines))
1385
1387
1386 maxtotal, maxname = 0, 0
1388 maxtotal, maxname = 0, 0
1387 totaladds, totalremoves = 0, 0
1389 totaladds, totalremoves = 0, 0
1388 for filename, adds, removes in stats:
1390 hasbinary = False
1391 for filename, adds, removes, isbinary in stats:
1389 totaladds += adds
1392 totaladds += adds
1390 totalremoves += removes
1393 totalremoves += removes
1391 maxname = max(maxname, len(filename))
1394 maxname = max(maxname, len(filename))
1392 maxtotal = max(maxtotal, adds+removes)
1395 maxtotal = max(maxtotal, adds+removes)
1396 if isbinary:
1397 hasbinary = True
1393
1398
1394 countwidth = len(str(maxtotal))
1399 countwidth = len(str(maxtotal))
1400 if hasbinary and countwidth < 3:
1401 countwidth = 3
1395 graphwidth = width - countwidth - maxname - 6
1402 graphwidth = width - countwidth - maxname - 6
1396 if graphwidth < 10:
1403 if graphwidth < 10:
1397 graphwidth = 10
1404 graphwidth = 10
1398
1405
1399 def scale(i):
1406 def scale(i):
1400 if maxtotal <= graphwidth:
1407 if maxtotal <= graphwidth:
1401 return i
1408 return i
1402 # If diffstat runs out of room it doesn't print anything,
1409 # If diffstat runs out of room it doesn't print anything,
1403 # which isn't very useful, so always print at least one + or -
1410 # which isn't very useful, so always print at least one + or -
1404 # if there were at least some changes.
1411 # if there were at least some changes.
1405 return max(i * graphwidth // maxtotal, int(bool(i)))
1412 return max(i * graphwidth // maxtotal, int(bool(i)))
1406
1413
1407 for filename, adds, removes in stats:
1414 for filename, adds, removes, isbinary in stats:
1415 if git and isbinary:
1416 count = 'Bin'
1417 else:
1418 count = adds + removes
1408 pluses = '+' * scale(adds)
1419 pluses = '+' * scale(adds)
1409 minuses = '-' * scale(removes)
1420 minuses = '-' * scale(removes)
1410 output.append(' %-*s | %*s %s%s\n' % (maxname, filename, countwidth,
1421 output.append(' %-*s | %*s %s%s\n' % (maxname, filename, countwidth,
1411 adds+removes, pluses, minuses))
1422 count, pluses, minuses))
1412
1423
1413 if stats:
1424 if stats:
1414 output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
1425 output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
1415 % (len(stats), totaladds, totalremoves))
1426 % (len(stats), totaladds, totalremoves))
1416
1427
1417 return ''.join(output)
1428 return ''.join(output)
@@ -1,31 +1,34 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 hg init repo
3 hg init repo
4 cd repo
4 cd repo
5 i=0; while (( $i < 213 )); do echo a >> a; i=$(($i + 1)); done
5 i=0; while (( $i < 213 )); do echo a >> a; i=$(($i + 1)); done
6 hg add a
6 hg add a
7
7
8 echo '% wide diffstat'
8 echo '% wide diffstat'
9 hg diff --stat
9 hg diff --stat
10
10
11 echo '% diffstat width'
11 echo '% diffstat width'
12 COLUMNS=24 hg diff --config ui.interactive=true --stat
12 COLUMNS=24 hg diff --config ui.interactive=true --stat
13
13
14 hg ci -m adda
14 hg ci -m adda
15
15
16 cat >> a <<EOF
16 cat >> a <<EOF
17 a
17 a
18 a
18 a
19 a
19 a
20 EOF
20 EOF
21
21
22 echo '% narrow diffstat'
22 echo '% narrow diffstat'
23 hg diff --stat
23 hg diff --stat
24
24
25 hg ci -m appenda
25 hg ci -m appenda
26
26
27 printf '%b' '\x00' > b
27 printf '%b' '\x00' > b
28 hg add b
28 hg add b
29
29
30 echo '% binary diffstat'
30 echo '% binary diffstat'
31 hg diff --stat
31 hg diff --stat
32
33 echo '% binary git diffstat'
34 hg diff --stat --git
@@ -1,12 +1,15 b''
1 % wide diffstat
1 % wide diffstat
2 a | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 a | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 1 files changed, 213 insertions(+), 0 deletions(-)
3 1 files changed, 213 insertions(+), 0 deletions(-)
4 % diffstat width
4 % diffstat width
5 a | 213 ++++++++++++++
5 a | 213 ++++++++++++++
6 1 files changed, 213 insertions(+), 0 deletions(-)
6 1 files changed, 213 insertions(+), 0 deletions(-)
7 % narrow diffstat
7 % narrow diffstat
8 a | 3 +++
8 a | 3 +++
9 1 files changed, 3 insertions(+), 0 deletions(-)
9 1 files changed, 3 insertions(+), 0 deletions(-)
10 % binary diffstat
10 % binary diffstat
11 b | 0
11 b | 0
12 1 files changed, 0 insertions(+), 0 deletions(-)
12 1 files changed, 0 insertions(+), 0 deletions(-)
13 % binary git diffstat
14 b | Bin
15 1 files changed, 0 insertions(+), 0 deletions(-)
General Comments 0
You need to be logged in to leave comments. Login now