##// END OF EJS Templates
some modernization cleanups, forward compatibility
Dirkjan Ochtman -
r8366:0bf00450 default
parent child Browse files
Show More
@@ -1,2611 +1,2611 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 '''patch management and development
8 '''patch management and development
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use "hg help command" for more details):
17 Common tasks (use "hg help command" for more details):
18
18
19 prepare repository to work with patches qinit
19 prepare repository to work with patches qinit
20 create new patch qnew
20 create new patch qnew
21 import existing patch qimport
21 import existing patch qimport
22
22
23 print patch series qseries
23 print patch series qseries
24 print applied patches qapplied
24 print applied patches qapplied
25 print name of top applied patch qtop
25 print name of top applied patch qtop
26
26
27 add known patch to applied stack qpush
27 add known patch to applied stack qpush
28 remove patch from applied stack qpop
28 remove patch from applied stack qpop
29 refresh contents of top applied patch qrefresh
29 refresh contents of top applied patch qrefresh
30 '''
30 '''
31
31
32 from mercurial.i18n import _
32 from mercurial.i18n import _
33 from mercurial.node import bin, hex, short, nullid, nullrev
33 from mercurial.node import bin, hex, short, nullid, nullrev
34 from mercurial.lock import release
34 from mercurial.lock import release
35 from mercurial import commands, cmdutil, hg, patch, util
35 from mercurial import commands, cmdutil, hg, patch, util
36 from mercurial import repair, extensions, url, error
36 from mercurial import repair, extensions, url, error
37 import os, sys, re, errno
37 import os, sys, re, errno
38
38
39 commands.norepo += " qclone"
39 commands.norepo += " qclone"
40
40
41 # Patch names looks like unix-file names.
41 # Patch names looks like unix-file names.
42 # They must be joinable with queue directory and result in the patch path.
42 # They must be joinable with queue directory and result in the patch path.
43 normname = util.normpath
43 normname = util.normpath
44
44
45 class statusentry:
45 class statusentry:
46 def __init__(self, rev, name=None):
46 def __init__(self, rev, name=None):
47 if not name:
47 if not name:
48 fields = rev.split(':', 1)
48 fields = rev.split(':', 1)
49 if len(fields) == 2:
49 if len(fields) == 2:
50 self.rev, self.name = fields
50 self.rev, self.name = fields
51 else:
51 else:
52 self.rev, self.name = None, None
52 self.rev, self.name = None, None
53 else:
53 else:
54 self.rev, self.name = rev, name
54 self.rev, self.name = rev, name
55
55
56 def __str__(self):
56 def __str__(self):
57 return self.rev + ':' + self.name
57 return self.rev + ':' + self.name
58
58
59 class patchheader(object):
59 class patchheader(object):
60 def __init__(self, message, comments, user, date, haspatch):
60 def __init__(self, message, comments, user, date, haspatch):
61 self.message = message
61 self.message = message
62 self.comments = comments
62 self.comments = comments
63 self.user = user
63 self.user = user
64 self.date = date
64 self.date = date
65 self.haspatch = haspatch
65 self.haspatch = haspatch
66
66
67 def setuser(self, user):
67 def setuser(self, user):
68 if not self.setheader(['From: ', '# User '], user):
68 if not self.setheader(['From: ', '# User '], user):
69 try:
69 try:
70 patchheaderat = self.comments.index('# HG changeset patch')
70 patchheaderat = self.comments.index('# HG changeset patch')
71 self.comments.insert(patchheaderat + 1,'# User ' + user)
71 self.comments.insert(patchheaderat + 1,'# User ' + user)
72 except ValueError:
72 except ValueError:
73 self.comments = ['From: ' + user, ''] + self.comments
73 self.comments = ['From: ' + user, ''] + self.comments
74 self.user = user
74 self.user = user
75
75
76 def setdate(self, date):
76 def setdate(self, date):
77 if self.setheader(['# Date '], date):
77 if self.setheader(['# Date '], date):
78 self.date = date
78 self.date = date
79
79
80 def setmessage(self, message):
80 def setmessage(self, message):
81 if self.comments:
81 if self.comments:
82 self._delmsg()
82 self._delmsg()
83 self.message = [message]
83 self.message = [message]
84 self.comments += self.message
84 self.comments += self.message
85
85
86 def setheader(self, prefixes, new):
86 def setheader(self, prefixes, new):
87 '''Update all references to a field in the patch header.
87 '''Update all references to a field in the patch header.
88 If none found, add it email style.'''
88 If none found, add it email style.'''
89 res = False
89 res = False
90 for prefix in prefixes:
90 for prefix in prefixes:
91 for i in xrange(len(self.comments)):
91 for i in xrange(len(self.comments)):
92 if self.comments[i].startswith(prefix):
92 if self.comments[i].startswith(prefix):
93 self.comments[i] = prefix + new
93 self.comments[i] = prefix + new
94 res = True
94 res = True
95 break
95 break
96 return res
96 return res
97
97
98 def __str__(self):
98 def __str__(self):
99 if not self.comments:
99 if not self.comments:
100 return ''
100 return ''
101 return '\n'.join(self.comments) + '\n\n'
101 return '\n'.join(self.comments) + '\n\n'
102
102
103 def _delmsg(self):
103 def _delmsg(self):
104 '''Remove existing message, keeping the rest of the comments fields.
104 '''Remove existing message, keeping the rest of the comments fields.
105 If comments contains 'subject: ', message will prepend
105 If comments contains 'subject: ', message will prepend
106 the field and a blank line.'''
106 the field and a blank line.'''
107 if self.message:
107 if self.message:
108 subj = 'subject: ' + self.message[0].lower()
108 subj = 'subject: ' + self.message[0].lower()
109 for i in xrange(len(self.comments)):
109 for i in xrange(len(self.comments)):
110 if subj == self.comments[i].lower():
110 if subj == self.comments[i].lower():
111 del self.comments[i]
111 del self.comments[i]
112 self.message = self.message[2:]
112 self.message = self.message[2:]
113 break
113 break
114 ci = 0
114 ci = 0
115 for mi in xrange(len(self.message)):
115 for mi in xrange(len(self.message)):
116 while self.message[mi] != self.comments[ci]:
116 while self.message[mi] != self.comments[ci]:
117 ci += 1
117 ci += 1
118 del self.comments[ci]
118 del self.comments[ci]
119
119
120 class queue:
120 class queue:
121 def __init__(self, ui, path, patchdir=None):
121 def __init__(self, ui, path, patchdir=None):
122 self.basepath = path
122 self.basepath = path
123 self.path = patchdir or os.path.join(path, "patches")
123 self.path = patchdir or os.path.join(path, "patches")
124 self.opener = util.opener(self.path)
124 self.opener = util.opener(self.path)
125 self.ui = ui
125 self.ui = ui
126 self.applied = []
126 self.applied = []
127 self.full_series = []
127 self.full_series = []
128 self.applied_dirty = 0
128 self.applied_dirty = 0
129 self.series_dirty = 0
129 self.series_dirty = 0
130 self.series_path = "series"
130 self.series_path = "series"
131 self.status_path = "status"
131 self.status_path = "status"
132 self.guards_path = "guards"
132 self.guards_path = "guards"
133 self.active_guards = None
133 self.active_guards = None
134 self.guards_dirty = False
134 self.guards_dirty = False
135 self._diffopts = None
135 self._diffopts = None
136
136
137 if os.path.exists(self.join(self.series_path)):
137 if os.path.exists(self.join(self.series_path)):
138 self.full_series = self.opener(self.series_path).read().splitlines()
138 self.full_series = self.opener(self.series_path).read().splitlines()
139 self.parse_series()
139 self.parse_series()
140
140
141 if os.path.exists(self.join(self.status_path)):
141 if os.path.exists(self.join(self.status_path)):
142 lines = self.opener(self.status_path).read().splitlines()
142 lines = self.opener(self.status_path).read().splitlines()
143 self.applied = [statusentry(l) for l in lines]
143 self.applied = [statusentry(l) for l in lines]
144
144
145 def diffopts(self):
145 def diffopts(self):
146 if self._diffopts is None:
146 if self._diffopts is None:
147 self._diffopts = patch.diffopts(self.ui)
147 self._diffopts = patch.diffopts(self.ui)
148 return self._diffopts
148 return self._diffopts
149
149
150 def join(self, *p):
150 def join(self, *p):
151 return os.path.join(self.path, *p)
151 return os.path.join(self.path, *p)
152
152
153 def find_series(self, patch):
153 def find_series(self, patch):
154 pre = re.compile("(\s*)([^#]+)")
154 pre = re.compile("(\s*)([^#]+)")
155 index = 0
155 index = 0
156 for l in self.full_series:
156 for l in self.full_series:
157 m = pre.match(l)
157 m = pre.match(l)
158 if m:
158 if m:
159 s = m.group(2)
159 s = m.group(2)
160 s = s.rstrip()
160 s = s.rstrip()
161 if s == patch:
161 if s == patch:
162 return index
162 return index
163 index += 1
163 index += 1
164 return None
164 return None
165
165
166 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
166 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
167
167
168 def parse_series(self):
168 def parse_series(self):
169 self.series = []
169 self.series = []
170 self.series_guards = []
170 self.series_guards = []
171 for l in self.full_series:
171 for l in self.full_series:
172 h = l.find('#')
172 h = l.find('#')
173 if h == -1:
173 if h == -1:
174 patch = l
174 patch = l
175 comment = ''
175 comment = ''
176 elif h == 0:
176 elif h == 0:
177 continue
177 continue
178 else:
178 else:
179 patch = l[:h]
179 patch = l[:h]
180 comment = l[h:]
180 comment = l[h:]
181 patch = patch.strip()
181 patch = patch.strip()
182 if patch:
182 if patch:
183 if patch in self.series:
183 if patch in self.series:
184 raise util.Abort(_('%s appears more than once in %s') %
184 raise util.Abort(_('%s appears more than once in %s') %
185 (patch, self.join(self.series_path)))
185 (patch, self.join(self.series_path)))
186 self.series.append(patch)
186 self.series.append(patch)
187 self.series_guards.append(self.guard_re.findall(comment))
187 self.series_guards.append(self.guard_re.findall(comment))
188
188
189 def check_guard(self, guard):
189 def check_guard(self, guard):
190 if not guard:
190 if not guard:
191 return _('guard cannot be an empty string')
191 return _('guard cannot be an empty string')
192 bad_chars = '# \t\r\n\f'
192 bad_chars = '# \t\r\n\f'
193 first = guard[0]
193 first = guard[0]
194 if first in '-+':
194 if first in '-+':
195 return (_('guard %r starts with invalid character: %r') %
195 return (_('guard %r starts with invalid character: %r') %
196 (guard, first))
196 (guard, first))
197 for c in bad_chars:
197 for c in bad_chars:
198 if c in guard:
198 if c in guard:
199 return _('invalid character in guard %r: %r') % (guard, c)
199 return _('invalid character in guard %r: %r') % (guard, c)
200
200
201 def set_active(self, guards):
201 def set_active(self, guards):
202 for guard in guards:
202 for guard in guards:
203 bad = self.check_guard(guard)
203 bad = self.check_guard(guard)
204 if bad:
204 if bad:
205 raise util.Abort(bad)
205 raise util.Abort(bad)
206 guards = sorted(set(guards))
206 guards = sorted(set(guards))
207 self.ui.debug(_('active guards: %s\n') % ' '.join(guards))
207 self.ui.debug(_('active guards: %s\n') % ' '.join(guards))
208 self.active_guards = guards
208 self.active_guards = guards
209 self.guards_dirty = True
209 self.guards_dirty = True
210
210
211 def active(self):
211 def active(self):
212 if self.active_guards is None:
212 if self.active_guards is None:
213 self.active_guards = []
213 self.active_guards = []
214 try:
214 try:
215 guards = self.opener(self.guards_path).read().split()
215 guards = self.opener(self.guards_path).read().split()
216 except IOError, err:
216 except IOError, err:
217 if err.errno != errno.ENOENT: raise
217 if err.errno != errno.ENOENT: raise
218 guards = []
218 guards = []
219 for i, guard in enumerate(guards):
219 for i, guard in enumerate(guards):
220 bad = self.check_guard(guard)
220 bad = self.check_guard(guard)
221 if bad:
221 if bad:
222 self.ui.warn('%s:%d: %s\n' %
222 self.ui.warn('%s:%d: %s\n' %
223 (self.join(self.guards_path), i + 1, bad))
223 (self.join(self.guards_path), i + 1, bad))
224 else:
224 else:
225 self.active_guards.append(guard)
225 self.active_guards.append(guard)
226 return self.active_guards
226 return self.active_guards
227
227
228 def set_guards(self, idx, guards):
228 def set_guards(self, idx, guards):
229 for g in guards:
229 for g in guards:
230 if len(g) < 2:
230 if len(g) < 2:
231 raise util.Abort(_('guard %r too short') % g)
231 raise util.Abort(_('guard %r too short') % g)
232 if g[0] not in '-+':
232 if g[0] not in '-+':
233 raise util.Abort(_('guard %r starts with invalid char') % g)
233 raise util.Abort(_('guard %r starts with invalid char') % g)
234 bad = self.check_guard(g[1:])
234 bad = self.check_guard(g[1:])
235 if bad:
235 if bad:
236 raise util.Abort(bad)
236 raise util.Abort(bad)
237 drop = self.guard_re.sub('', self.full_series[idx])
237 drop = self.guard_re.sub('', self.full_series[idx])
238 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
238 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
239 self.parse_series()
239 self.parse_series()
240 self.series_dirty = True
240 self.series_dirty = True
241
241
242 def pushable(self, idx):
242 def pushable(self, idx):
243 if isinstance(idx, str):
243 if isinstance(idx, str):
244 idx = self.series.index(idx)
244 idx = self.series.index(idx)
245 patchguards = self.series_guards[idx]
245 patchguards = self.series_guards[idx]
246 if not patchguards:
246 if not patchguards:
247 return True, None
247 return True, None
248 guards = self.active()
248 guards = self.active()
249 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
249 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
250 if exactneg:
250 if exactneg:
251 return False, exactneg[0]
251 return False, exactneg[0]
252 pos = [g for g in patchguards if g[0] == '+']
252 pos = [g for g in patchguards if g[0] == '+']
253 exactpos = [g for g in pos if g[1:] in guards]
253 exactpos = [g for g in pos if g[1:] in guards]
254 if pos:
254 if pos:
255 if exactpos:
255 if exactpos:
256 return True, exactpos[0]
256 return True, exactpos[0]
257 return False, pos
257 return False, pos
258 return True, ''
258 return True, ''
259
259
260 def explain_pushable(self, idx, all_patches=False):
260 def explain_pushable(self, idx, all_patches=False):
261 write = all_patches and self.ui.write or self.ui.warn
261 write = all_patches and self.ui.write or self.ui.warn
262 if all_patches or self.ui.verbose:
262 if all_patches or self.ui.verbose:
263 if isinstance(idx, str):
263 if isinstance(idx, str):
264 idx = self.series.index(idx)
264 idx = self.series.index(idx)
265 pushable, why = self.pushable(idx)
265 pushable, why = self.pushable(idx)
266 if all_patches and pushable:
266 if all_patches and pushable:
267 if why is None:
267 if why is None:
268 write(_('allowing %s - no guards in effect\n') %
268 write(_('allowing %s - no guards in effect\n') %
269 self.series[idx])
269 self.series[idx])
270 else:
270 else:
271 if not why:
271 if not why:
272 write(_('allowing %s - no matching negative guards\n') %
272 write(_('allowing %s - no matching negative guards\n') %
273 self.series[idx])
273 self.series[idx])
274 else:
274 else:
275 write(_('allowing %s - guarded by %r\n') %
275 write(_('allowing %s - guarded by %r\n') %
276 (self.series[idx], why))
276 (self.series[idx], why))
277 if not pushable:
277 if not pushable:
278 if why:
278 if why:
279 write(_('skipping %s - guarded by %r\n') %
279 write(_('skipping %s - guarded by %r\n') %
280 (self.series[idx], why))
280 (self.series[idx], why))
281 else:
281 else:
282 write(_('skipping %s - no matching guards\n') %
282 write(_('skipping %s - no matching guards\n') %
283 self.series[idx])
283 self.series[idx])
284
284
285 def save_dirty(self):
285 def save_dirty(self):
286 def write_list(items, path):
286 def write_list(items, path):
287 fp = self.opener(path, 'w')
287 fp = self.opener(path, 'w')
288 for i in items:
288 for i in items:
289 fp.write("%s\n" % i)
289 fp.write("%s\n" % i)
290 fp.close()
290 fp.close()
291 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
291 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
292 if self.series_dirty: write_list(self.full_series, self.series_path)
292 if self.series_dirty: write_list(self.full_series, self.series_path)
293 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
293 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
294
294
295 def readheaders(self, patch):
295 def readheaders(self, patch):
296 def eatdiff(lines):
296 def eatdiff(lines):
297 while lines:
297 while lines:
298 l = lines[-1]
298 l = lines[-1]
299 if (l.startswith("diff -") or
299 if (l.startswith("diff -") or
300 l.startswith("Index:") or
300 l.startswith("Index:") or
301 l.startswith("===========")):
301 l.startswith("===========")):
302 del lines[-1]
302 del lines[-1]
303 else:
303 else:
304 break
304 break
305 def eatempty(lines):
305 def eatempty(lines):
306 while lines:
306 while lines:
307 l = lines[-1]
307 l = lines[-1]
308 if re.match('\s*$', l):
308 if re.match('\s*$', l):
309 del lines[-1]
309 del lines[-1]
310 else:
310 else:
311 break
311 break
312
312
313 pf = self.join(patch)
313 pf = self.join(patch)
314 message = []
314 message = []
315 comments = []
315 comments = []
316 user = None
316 user = None
317 date = None
317 date = None
318 format = None
318 format = None
319 subject = None
319 subject = None
320 diffstart = 0
320 diffstart = 0
321
321
322 for line in file(pf):
322 for line in file(pf):
323 line = line.rstrip()
323 line = line.rstrip()
324 if line.startswith('diff --git'):
324 if line.startswith('diff --git'):
325 diffstart = 2
325 diffstart = 2
326 break
326 break
327 if diffstart:
327 if diffstart:
328 if line.startswith('+++ '):
328 if line.startswith('+++ '):
329 diffstart = 2
329 diffstart = 2
330 break
330 break
331 if line.startswith("--- "):
331 if line.startswith("--- "):
332 diffstart = 1
332 diffstart = 1
333 continue
333 continue
334 elif format == "hgpatch":
334 elif format == "hgpatch":
335 # parse values when importing the result of an hg export
335 # parse values when importing the result of an hg export
336 if line.startswith("# User "):
336 if line.startswith("# User "):
337 user = line[7:]
337 user = line[7:]
338 elif line.startswith("# Date "):
338 elif line.startswith("# Date "):
339 date = line[7:]
339 date = line[7:]
340 elif not line.startswith("# ") and line:
340 elif not line.startswith("# ") and line:
341 message.append(line)
341 message.append(line)
342 format = None
342 format = None
343 elif line == '# HG changeset patch':
343 elif line == '# HG changeset patch':
344 format = "hgpatch"
344 format = "hgpatch"
345 elif (format != "tagdone" and (line.startswith("Subject: ") or
345 elif (format != "tagdone" and (line.startswith("Subject: ") or
346 line.startswith("subject: "))):
346 line.startswith("subject: "))):
347 subject = line[9:]
347 subject = line[9:]
348 format = "tag"
348 format = "tag"
349 elif (format != "tagdone" and (line.startswith("From: ") or
349 elif (format != "tagdone" and (line.startswith("From: ") or
350 line.startswith("from: "))):
350 line.startswith("from: "))):
351 user = line[6:]
351 user = line[6:]
352 format = "tag"
352 format = "tag"
353 elif format == "tag" and line == "":
353 elif format == "tag" and line == "":
354 # when looking for tags (subject: from: etc) they
354 # when looking for tags (subject: from: etc) they
355 # end once you find a blank line in the source
355 # end once you find a blank line in the source
356 format = "tagdone"
356 format = "tagdone"
357 elif message or line:
357 elif message or line:
358 message.append(line)
358 message.append(line)
359 comments.append(line)
359 comments.append(line)
360
360
361 eatdiff(message)
361 eatdiff(message)
362 eatdiff(comments)
362 eatdiff(comments)
363 eatempty(message)
363 eatempty(message)
364 eatempty(comments)
364 eatempty(comments)
365
365
366 # make sure message isn't empty
366 # make sure message isn't empty
367 if format and format.startswith("tag") and subject:
367 if format and format.startswith("tag") and subject:
368 message.insert(0, "")
368 message.insert(0, "")
369 message.insert(0, subject)
369 message.insert(0, subject)
370 return patchheader(message, comments, user, date, diffstart > 1)
370 return patchheader(message, comments, user, date, diffstart > 1)
371
371
372 def removeundo(self, repo):
372 def removeundo(self, repo):
373 undo = repo.sjoin('undo')
373 undo = repo.sjoin('undo')
374 if not os.path.exists(undo):
374 if not os.path.exists(undo):
375 return
375 return
376 try:
376 try:
377 os.unlink(undo)
377 os.unlink(undo)
378 except OSError, inst:
378 except OSError, inst:
379 self.ui.warn(_('error removing undo: %s\n') % str(inst))
379 self.ui.warn(_('error removing undo: %s\n') % str(inst))
380
380
381 def printdiff(self, repo, node1, node2=None, files=None,
381 def printdiff(self, repo, node1, node2=None, files=None,
382 fp=None, changes=None, opts={}):
382 fp=None, changes=None, opts={}):
383 m = cmdutil.match(repo, files, opts)
383 m = cmdutil.match(repo, files, opts)
384 chunks = patch.diff(repo, node1, node2, m, changes, self.diffopts())
384 chunks = patch.diff(repo, node1, node2, m, changes, self.diffopts())
385 write = fp is None and repo.ui.write or fp.write
385 write = fp is None and repo.ui.write or fp.write
386 for chunk in chunks:
386 for chunk in chunks:
387 write(chunk)
387 write(chunk)
388
388
389 def mergeone(self, repo, mergeq, head, patch, rev):
389 def mergeone(self, repo, mergeq, head, patch, rev):
390 # first try just applying the patch
390 # first try just applying the patch
391 (err, n) = self.apply(repo, [ patch ], update_status=False,
391 (err, n) = self.apply(repo, [ patch ], update_status=False,
392 strict=True, merge=rev)
392 strict=True, merge=rev)
393
393
394 if err == 0:
394 if err == 0:
395 return (err, n)
395 return (err, n)
396
396
397 if n is None:
397 if n is None:
398 raise util.Abort(_("apply failed for patch %s") % patch)
398 raise util.Abort(_("apply failed for patch %s") % patch)
399
399
400 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
400 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
401
401
402 # apply failed, strip away that rev and merge.
402 # apply failed, strip away that rev and merge.
403 hg.clean(repo, head)
403 hg.clean(repo, head)
404 self.strip(repo, n, update=False, backup='strip')
404 self.strip(repo, n, update=False, backup='strip')
405
405
406 ctx = repo[rev]
406 ctx = repo[rev]
407 ret = hg.merge(repo, rev)
407 ret = hg.merge(repo, rev)
408 if ret:
408 if ret:
409 raise util.Abort(_("update returned %d") % ret)
409 raise util.Abort(_("update returned %d") % ret)
410 n = repo.commit(None, ctx.description(), ctx.user(), force=1)
410 n = repo.commit(None, ctx.description(), ctx.user(), force=1)
411 if n == None:
411 if n == None:
412 raise util.Abort(_("repo commit failed"))
412 raise util.Abort(_("repo commit failed"))
413 try:
413 try:
414 ph = mergeq.readheaders(patch)
414 ph = mergeq.readheaders(patch)
415 except:
415 except:
416 raise util.Abort(_("unable to read %s") % patch)
416 raise util.Abort(_("unable to read %s") % patch)
417
417
418 patchf = self.opener(patch, "w")
418 patchf = self.opener(patch, "w")
419 comments = str(ph)
419 comments = str(ph)
420 if comments:
420 if comments:
421 patchf.write(comments)
421 patchf.write(comments)
422 self.printdiff(repo, head, n, fp=patchf)
422 self.printdiff(repo, head, n, fp=patchf)
423 patchf.close()
423 patchf.close()
424 self.removeundo(repo)
424 self.removeundo(repo)
425 return (0, n)
425 return (0, n)
426
426
427 def qparents(self, repo, rev=None):
427 def qparents(self, repo, rev=None):
428 if rev is None:
428 if rev is None:
429 (p1, p2) = repo.dirstate.parents()
429 (p1, p2) = repo.dirstate.parents()
430 if p2 == nullid:
430 if p2 == nullid:
431 return p1
431 return p1
432 if len(self.applied) == 0:
432 if len(self.applied) == 0:
433 return None
433 return None
434 return bin(self.applied[-1].rev)
434 return bin(self.applied[-1].rev)
435 pp = repo.changelog.parents(rev)
435 pp = repo.changelog.parents(rev)
436 if pp[1] != nullid:
436 if pp[1] != nullid:
437 arevs = [ x.rev for x in self.applied ]
437 arevs = [ x.rev for x in self.applied ]
438 p0 = hex(pp[0])
438 p0 = hex(pp[0])
439 p1 = hex(pp[1])
439 p1 = hex(pp[1])
440 if p0 in arevs:
440 if p0 in arevs:
441 return pp[0]
441 return pp[0]
442 if p1 in arevs:
442 if p1 in arevs:
443 return pp[1]
443 return pp[1]
444 return pp[0]
444 return pp[0]
445
445
446 def mergepatch(self, repo, mergeq, series):
446 def mergepatch(self, repo, mergeq, series):
447 if len(self.applied) == 0:
447 if len(self.applied) == 0:
448 # each of the patches merged in will have two parents. This
448 # each of the patches merged in will have two parents. This
449 # can confuse the qrefresh, qdiff, and strip code because it
449 # can confuse the qrefresh, qdiff, and strip code because it
450 # needs to know which parent is actually in the patch queue.
450 # needs to know which parent is actually in the patch queue.
451 # so, we insert a merge marker with only one parent. This way
451 # so, we insert a merge marker with only one parent. This way
452 # the first patch in the queue is never a merge patch
452 # the first patch in the queue is never a merge patch
453 #
453 #
454 pname = ".hg.patches.merge.marker"
454 pname = ".hg.patches.merge.marker"
455 n = repo.commit(None, '[mq]: merge marker', user=None, force=1)
455 n = repo.commit(None, '[mq]: merge marker', user=None, force=1)
456 self.removeundo(repo)
456 self.removeundo(repo)
457 self.applied.append(statusentry(hex(n), pname))
457 self.applied.append(statusentry(hex(n), pname))
458 self.applied_dirty = 1
458 self.applied_dirty = 1
459
459
460 head = self.qparents(repo)
460 head = self.qparents(repo)
461
461
462 for patch in series:
462 for patch in series:
463 patch = mergeq.lookup(patch, strict=True)
463 patch = mergeq.lookup(patch, strict=True)
464 if not patch:
464 if not patch:
465 self.ui.warn(_("patch %s does not exist\n") % patch)
465 self.ui.warn(_("patch %s does not exist\n") % patch)
466 return (1, None)
466 return (1, None)
467 pushable, reason = self.pushable(patch)
467 pushable, reason = self.pushable(patch)
468 if not pushable:
468 if not pushable:
469 self.explain_pushable(patch, all_patches=True)
469 self.explain_pushable(patch, all_patches=True)
470 continue
470 continue
471 info = mergeq.isapplied(patch)
471 info = mergeq.isapplied(patch)
472 if not info:
472 if not info:
473 self.ui.warn(_("patch %s is not applied\n") % patch)
473 self.ui.warn(_("patch %s is not applied\n") % patch)
474 return (1, None)
474 return (1, None)
475 rev = bin(info[1])
475 rev = bin(info[1])
476 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
476 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
477 if head:
477 if head:
478 self.applied.append(statusentry(hex(head), patch))
478 self.applied.append(statusentry(hex(head), patch))
479 self.applied_dirty = 1
479 self.applied_dirty = 1
480 if err:
480 if err:
481 return (err, head)
481 return (err, head)
482 self.save_dirty()
482 self.save_dirty()
483 return (0, head)
483 return (0, head)
484
484
485 def patch(self, repo, patchfile):
485 def patch(self, repo, patchfile):
486 '''Apply patchfile to the working directory.
486 '''Apply patchfile to the working directory.
487 patchfile: file name of patch'''
487 patchfile: file name of patch'''
488 files = {}
488 files = {}
489 try:
489 try:
490 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
490 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
491 files=files)
491 files=files)
492 except Exception, inst:
492 except Exception, inst:
493 self.ui.note(str(inst) + '\n')
493 self.ui.note(str(inst) + '\n')
494 if not self.ui.verbose:
494 if not self.ui.verbose:
495 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
495 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
496 return (False, files, False)
496 return (False, files, False)
497
497
498 return (True, files, fuzz)
498 return (True, files, fuzz)
499
499
500 def apply(self, repo, series, list=False, update_status=True,
500 def apply(self, repo, series, list=False, update_status=True,
501 strict=False, patchdir=None, merge=None, all_files={}):
501 strict=False, patchdir=None, merge=None, all_files={}):
502 wlock = lock = tr = None
502 wlock = lock = tr = None
503 try:
503 try:
504 wlock = repo.wlock()
504 wlock = repo.wlock()
505 lock = repo.lock()
505 lock = repo.lock()
506 tr = repo.transaction()
506 tr = repo.transaction()
507 try:
507 try:
508 ret = self._apply(repo, series, list, update_status,
508 ret = self._apply(repo, series, list, update_status,
509 strict, patchdir, merge, all_files=all_files)
509 strict, patchdir, merge, all_files=all_files)
510 tr.close()
510 tr.close()
511 self.save_dirty()
511 self.save_dirty()
512 return ret
512 return ret
513 except:
513 except:
514 try:
514 try:
515 tr.abort()
515 tr.abort()
516 finally:
516 finally:
517 repo.invalidate()
517 repo.invalidate()
518 repo.dirstate.invalidate()
518 repo.dirstate.invalidate()
519 raise
519 raise
520 finally:
520 finally:
521 del tr
521 del tr
522 release(lock, wlock)
522 release(lock, wlock)
523 self.removeundo(repo)
523 self.removeundo(repo)
524
524
525 def _apply(self, repo, series, list=False, update_status=True,
525 def _apply(self, repo, series, list=False, update_status=True,
526 strict=False, patchdir=None, merge=None, all_files={}):
526 strict=False, patchdir=None, merge=None, all_files={}):
527 # TODO unify with commands.py
527 # TODO unify with commands.py
528 if not patchdir:
528 if not patchdir:
529 patchdir = self.path
529 patchdir = self.path
530 err = 0
530 err = 0
531 n = None
531 n = None
532 for patchname in series:
532 for patchname in series:
533 pushable, reason = self.pushable(patchname)
533 pushable, reason = self.pushable(patchname)
534 if not pushable:
534 if not pushable:
535 self.explain_pushable(patchname, all_patches=True)
535 self.explain_pushable(patchname, all_patches=True)
536 continue
536 continue
537 self.ui.warn(_("applying %s\n") % patchname)
537 self.ui.warn(_("applying %s\n") % patchname)
538 pf = os.path.join(patchdir, patchname)
538 pf = os.path.join(patchdir, patchname)
539
539
540 try:
540 try:
541 ph = self.readheaders(patchname)
541 ph = self.readheaders(patchname)
542 except:
542 except:
543 self.ui.warn(_("Unable to read %s\n") % patchname)
543 self.ui.warn(_("Unable to read %s\n") % patchname)
544 err = 1
544 err = 1
545 break
545 break
546
546
547 message = ph.message
547 message = ph.message
548 if not message:
548 if not message:
549 message = _("imported patch %s\n") % patchname
549 message = _("imported patch %s\n") % patchname
550 else:
550 else:
551 if list:
551 if list:
552 message.append(_("\nimported patch %s") % patchname)
552 message.append(_("\nimported patch %s") % patchname)
553 message = '\n'.join(message)
553 message = '\n'.join(message)
554
554
555 if ph.haspatch:
555 if ph.haspatch:
556 (patcherr, files, fuzz) = self.patch(repo, pf)
556 (patcherr, files, fuzz) = self.patch(repo, pf)
557 all_files.update(files)
557 all_files.update(files)
558 patcherr = not patcherr
558 patcherr = not patcherr
559 else:
559 else:
560 self.ui.warn(_("patch %s is empty\n") % patchname)
560 self.ui.warn(_("patch %s is empty\n") % patchname)
561 patcherr, files, fuzz = 0, [], 0
561 patcherr, files, fuzz = 0, [], 0
562
562
563 if merge and files:
563 if merge and files:
564 # Mark as removed/merged and update dirstate parent info
564 # Mark as removed/merged and update dirstate parent info
565 removed = []
565 removed = []
566 merged = []
566 merged = []
567 for f in files:
567 for f in files:
568 if os.path.exists(repo.wjoin(f)):
568 if os.path.exists(repo.wjoin(f)):
569 merged.append(f)
569 merged.append(f)
570 else:
570 else:
571 removed.append(f)
571 removed.append(f)
572 for f in removed:
572 for f in removed:
573 repo.dirstate.remove(f)
573 repo.dirstate.remove(f)
574 for f in merged:
574 for f in merged:
575 repo.dirstate.merge(f)
575 repo.dirstate.merge(f)
576 p1, p2 = repo.dirstate.parents()
576 p1, p2 = repo.dirstate.parents()
577 repo.dirstate.setparents(p1, merge)
577 repo.dirstate.setparents(p1, merge)
578
578
579 files = patch.updatedir(self.ui, repo, files)
579 files = patch.updatedir(self.ui, repo, files)
580 match = cmdutil.matchfiles(repo, files or [])
580 match = cmdutil.matchfiles(repo, files or [])
581 n = repo.commit(files, message, ph.user, ph.date, match=match,
581 n = repo.commit(files, message, ph.user, ph.date, match=match,
582 force=True)
582 force=True)
583
583
584 if n == None:
584 if n == None:
585 raise util.Abort(_("repo commit failed"))
585 raise util.Abort(_("repo commit failed"))
586
586
587 if update_status:
587 if update_status:
588 self.applied.append(statusentry(hex(n), patchname))
588 self.applied.append(statusentry(hex(n), patchname))
589
589
590 if patcherr:
590 if patcherr:
591 self.ui.warn(_("patch failed, rejects left in working dir\n"))
591 self.ui.warn(_("patch failed, rejects left in working dir\n"))
592 err = 1
592 err = 1
593 break
593 break
594
594
595 if fuzz and strict:
595 if fuzz and strict:
596 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
596 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
597 err = 1
597 err = 1
598 break
598 break
599 return (err, n)
599 return (err, n)
600
600
601 def _clean_series(self, patches):
601 def _clean_series(self, patches):
602 for i in sorted([self.find_series(p) for p in patches], reverse=True):
602 for i in sorted([self.find_series(p) for p in patches], reverse=True):
603 del self.full_series[i]
603 del self.full_series[i]
604 self.parse_series()
604 self.parse_series()
605 self.series_dirty = 1
605 self.series_dirty = 1
606
606
607 def finish(self, repo, revs):
607 def finish(self, repo, revs):
608 firstrev = repo[self.applied[0].rev].rev()
608 firstrev = repo[self.applied[0].rev].rev()
609 appliedbase = 0
609 appliedbase = 0
610 patches = []
610 patches = []
611 for rev in sorted(revs):
611 for rev in sorted(revs):
612 if rev < firstrev:
612 if rev < firstrev:
613 raise util.Abort(_('revision %d is not managed') % rev)
613 raise util.Abort(_('revision %d is not managed') % rev)
614 base = bin(self.applied[appliedbase].rev)
614 base = bin(self.applied[appliedbase].rev)
615 node = repo.changelog.node(rev)
615 node = repo.changelog.node(rev)
616 if node != base:
616 if node != base:
617 raise util.Abort(_('cannot delete revision %d above '
617 raise util.Abort(_('cannot delete revision %d above '
618 'applied patches') % rev)
618 'applied patches') % rev)
619 patches.append(self.applied[appliedbase].name)
619 patches.append(self.applied[appliedbase].name)
620 appliedbase += 1
620 appliedbase += 1
621
621
622 r = self.qrepo()
622 r = self.qrepo()
623 if r:
623 if r:
624 r.remove(patches, True)
624 r.remove(patches, True)
625 else:
625 else:
626 for p in patches:
626 for p in patches:
627 os.unlink(self.join(p))
627 os.unlink(self.join(p))
628
628
629 del self.applied[:appliedbase]
629 del self.applied[:appliedbase]
630 self.applied_dirty = 1
630 self.applied_dirty = 1
631 self._clean_series(patches)
631 self._clean_series(patches)
632
632
633 def delete(self, repo, patches, opts):
633 def delete(self, repo, patches, opts):
634 if not patches and not opts.get('rev'):
634 if not patches and not opts.get('rev'):
635 raise util.Abort(_('qdelete requires at least one revision or '
635 raise util.Abort(_('qdelete requires at least one revision or '
636 'patch name'))
636 'patch name'))
637
637
638 realpatches = []
638 realpatches = []
639 for patch in patches:
639 for patch in patches:
640 patch = self.lookup(patch, strict=True)
640 patch = self.lookup(patch, strict=True)
641 info = self.isapplied(patch)
641 info = self.isapplied(patch)
642 if info:
642 if info:
643 raise util.Abort(_("cannot delete applied patch %s") % patch)
643 raise util.Abort(_("cannot delete applied patch %s") % patch)
644 if patch not in self.series:
644 if patch not in self.series:
645 raise util.Abort(_("patch %s not in series file") % patch)
645 raise util.Abort(_("patch %s not in series file") % patch)
646 realpatches.append(patch)
646 realpatches.append(patch)
647
647
648 appliedbase = 0
648 appliedbase = 0
649 if opts.get('rev'):
649 if opts.get('rev'):
650 if not self.applied:
650 if not self.applied:
651 raise util.Abort(_('no patches applied'))
651 raise util.Abort(_('no patches applied'))
652 revs = cmdutil.revrange(repo, opts['rev'])
652 revs = cmdutil.revrange(repo, opts['rev'])
653 if len(revs) > 1 and revs[0] > revs[1]:
653 if len(revs) > 1 and revs[0] > revs[1]:
654 revs.reverse()
654 revs.reverse()
655 for rev in revs:
655 for rev in revs:
656 if appliedbase >= len(self.applied):
656 if appliedbase >= len(self.applied):
657 raise util.Abort(_("revision %d is not managed") % rev)
657 raise util.Abort(_("revision %d is not managed") % rev)
658
658
659 base = bin(self.applied[appliedbase].rev)
659 base = bin(self.applied[appliedbase].rev)
660 node = repo.changelog.node(rev)
660 node = repo.changelog.node(rev)
661 if node != base:
661 if node != base:
662 raise util.Abort(_("cannot delete revision %d above "
662 raise util.Abort(_("cannot delete revision %d above "
663 "applied patches") % rev)
663 "applied patches") % rev)
664 realpatches.append(self.applied[appliedbase].name)
664 realpatches.append(self.applied[appliedbase].name)
665 appliedbase += 1
665 appliedbase += 1
666
666
667 if not opts.get('keep'):
667 if not opts.get('keep'):
668 r = self.qrepo()
668 r = self.qrepo()
669 if r:
669 if r:
670 r.remove(realpatches, True)
670 r.remove(realpatches, True)
671 else:
671 else:
672 for p in realpatches:
672 for p in realpatches:
673 os.unlink(self.join(p))
673 os.unlink(self.join(p))
674
674
675 if appliedbase:
675 if appliedbase:
676 del self.applied[:appliedbase]
676 del self.applied[:appliedbase]
677 self.applied_dirty = 1
677 self.applied_dirty = 1
678 self._clean_series(realpatches)
678 self._clean_series(realpatches)
679
679
680 def check_toppatch(self, repo):
680 def check_toppatch(self, repo):
681 if len(self.applied) > 0:
681 if len(self.applied) > 0:
682 top = bin(self.applied[-1].rev)
682 top = bin(self.applied[-1].rev)
683 pp = repo.dirstate.parents()
683 pp = repo.dirstate.parents()
684 if top not in pp:
684 if top not in pp:
685 raise util.Abort(_("working directory revision is not qtip"))
685 raise util.Abort(_("working directory revision is not qtip"))
686 return top
686 return top
687 return None
687 return None
688 def check_localchanges(self, repo, force=False, refresh=True):
688 def check_localchanges(self, repo, force=False, refresh=True):
689 m, a, r, d = repo.status()[:4]
689 m, a, r, d = repo.status()[:4]
690 if m or a or r or d:
690 if m or a or r or d:
691 if not force:
691 if not force:
692 if refresh:
692 if refresh:
693 raise util.Abort(_("local changes found, refresh first"))
693 raise util.Abort(_("local changes found, refresh first"))
694 else:
694 else:
695 raise util.Abort(_("local changes found"))
695 raise util.Abort(_("local changes found"))
696 return m, a, r, d
696 return m, a, r, d
697
697
698 _reserved = ('series', 'status', 'guards')
698 _reserved = ('series', 'status', 'guards')
699 def check_reserved_name(self, name):
699 def check_reserved_name(self, name):
700 if (name in self._reserved or name.startswith('.hg')
700 if (name in self._reserved or name.startswith('.hg')
701 or name.startswith('.mq')):
701 or name.startswith('.mq')):
702 raise util.Abort(_('"%s" cannot be used as the name of a patch')
702 raise util.Abort(_('"%s" cannot be used as the name of a patch')
703 % name)
703 % name)
704
704
705 def new(self, repo, patchfn, *pats, **opts):
705 def new(self, repo, patchfn, *pats, **opts):
706 """options:
706 """options:
707 msg: a string or a no-argument function returning a string
707 msg: a string or a no-argument function returning a string
708 """
708 """
709 msg = opts.get('msg')
709 msg = opts.get('msg')
710 force = opts.get('force')
710 force = opts.get('force')
711 user = opts.get('user')
711 user = opts.get('user')
712 date = opts.get('date')
712 date = opts.get('date')
713 if date:
713 if date:
714 date = util.parsedate(date)
714 date = util.parsedate(date)
715 self.check_reserved_name(patchfn)
715 self.check_reserved_name(patchfn)
716 if os.path.exists(self.join(patchfn)):
716 if os.path.exists(self.join(patchfn)):
717 raise util.Abort(_('patch "%s" already exists') % patchfn)
717 raise util.Abort(_('patch "%s" already exists') % patchfn)
718 if opts.get('include') or opts.get('exclude') or pats:
718 if opts.get('include') or opts.get('exclude') or pats:
719 match = cmdutil.match(repo, pats, opts)
719 match = cmdutil.match(repo, pats, opts)
720 # detect missing files in pats
720 # detect missing files in pats
721 def badfn(f, msg):
721 def badfn(f, msg):
722 raise util.Abort('%s: %s' % (f, msg))
722 raise util.Abort('%s: %s' % (f, msg))
723 match.bad = badfn
723 match.bad = badfn
724 m, a, r, d = repo.status(match=match)[:4]
724 m, a, r, d = repo.status(match=match)[:4]
725 else:
725 else:
726 m, a, r, d = self.check_localchanges(repo, force)
726 m, a, r, d = self.check_localchanges(repo, force)
727 match = cmdutil.matchfiles(repo, m + a + r)
727 match = cmdutil.matchfiles(repo, m + a + r)
728 commitfiles = m + a + r
728 commitfiles = m + a + r
729 self.check_toppatch(repo)
729 self.check_toppatch(repo)
730 insert = self.full_series_end()
730 insert = self.full_series_end()
731 wlock = repo.wlock()
731 wlock = repo.wlock()
732 try:
732 try:
733 # if patch file write fails, abort early
733 # if patch file write fails, abort early
734 p = self.opener(patchfn, "w")
734 p = self.opener(patchfn, "w")
735 try:
735 try:
736 if date:
736 if date:
737 p.write("# HG changeset patch\n")
737 p.write("# HG changeset patch\n")
738 if user:
738 if user:
739 p.write("# User " + user + "\n")
739 p.write("# User " + user + "\n")
740 p.write("# Date %d %d\n\n" % date)
740 p.write("# Date %d %d\n\n" % date)
741 elif user:
741 elif user:
742 p.write("From: " + user + "\n\n")
742 p.write("From: " + user + "\n\n")
743
743
744 if callable(msg):
744 if hasattr(msg, '__call__'):
745 msg = msg()
745 msg = msg()
746 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
746 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
747 n = repo.commit(commitfiles, commitmsg, user, date, match=match, force=True)
747 n = repo.commit(commitfiles, commitmsg, user, date, match=match, force=True)
748 if n == None:
748 if n == None:
749 raise util.Abort(_("repo commit failed"))
749 raise util.Abort(_("repo commit failed"))
750 try:
750 try:
751 self.full_series[insert:insert] = [patchfn]
751 self.full_series[insert:insert] = [patchfn]
752 self.applied.append(statusentry(hex(n), patchfn))
752 self.applied.append(statusentry(hex(n), patchfn))
753 self.parse_series()
753 self.parse_series()
754 self.series_dirty = 1
754 self.series_dirty = 1
755 self.applied_dirty = 1
755 self.applied_dirty = 1
756 if msg:
756 if msg:
757 msg = msg + "\n\n"
757 msg = msg + "\n\n"
758 p.write(msg)
758 p.write(msg)
759 if commitfiles:
759 if commitfiles:
760 diffopts = self.diffopts()
760 diffopts = self.diffopts()
761 if opts.get('git'): diffopts.git = True
761 if opts.get('git'): diffopts.git = True
762 parent = self.qparents(repo, n)
762 parent = self.qparents(repo, n)
763 chunks = patch.diff(repo, node1=parent, node2=n,
763 chunks = patch.diff(repo, node1=parent, node2=n,
764 match=match, opts=diffopts)
764 match=match, opts=diffopts)
765 for chunk in chunks:
765 for chunk in chunks:
766 p.write(chunk)
766 p.write(chunk)
767 p.close()
767 p.close()
768 wlock.release()
768 wlock.release()
769 wlock = None
769 wlock = None
770 r = self.qrepo()
770 r = self.qrepo()
771 if r: r.add([patchfn])
771 if r: r.add([patchfn])
772 except:
772 except:
773 repo.rollback()
773 repo.rollback()
774 raise
774 raise
775 except Exception:
775 except Exception:
776 patchpath = self.join(patchfn)
776 patchpath = self.join(patchfn)
777 try:
777 try:
778 os.unlink(patchpath)
778 os.unlink(patchpath)
779 except:
779 except:
780 self.ui.warn(_('error unlinking %s\n') % patchpath)
780 self.ui.warn(_('error unlinking %s\n') % patchpath)
781 raise
781 raise
782 self.removeundo(repo)
782 self.removeundo(repo)
783 finally:
783 finally:
784 release(wlock)
784 release(wlock)
785
785
786 def strip(self, repo, rev, update=True, backup="all", force=None):
786 def strip(self, repo, rev, update=True, backup="all", force=None):
787 wlock = lock = None
787 wlock = lock = None
788 try:
788 try:
789 wlock = repo.wlock()
789 wlock = repo.wlock()
790 lock = repo.lock()
790 lock = repo.lock()
791
791
792 if update:
792 if update:
793 self.check_localchanges(repo, force=force, refresh=False)
793 self.check_localchanges(repo, force=force, refresh=False)
794 urev = self.qparents(repo, rev)
794 urev = self.qparents(repo, rev)
795 hg.clean(repo, urev)
795 hg.clean(repo, urev)
796 repo.dirstate.write()
796 repo.dirstate.write()
797
797
798 self.removeundo(repo)
798 self.removeundo(repo)
799 repair.strip(self.ui, repo, rev, backup)
799 repair.strip(self.ui, repo, rev, backup)
800 # strip may have unbundled a set of backed up revisions after
800 # strip may have unbundled a set of backed up revisions after
801 # the actual strip
801 # the actual strip
802 self.removeundo(repo)
802 self.removeundo(repo)
803 finally:
803 finally:
804 release(lock, wlock)
804 release(lock, wlock)
805
805
806 def isapplied(self, patch):
806 def isapplied(self, patch):
807 """returns (index, rev, patch)"""
807 """returns (index, rev, patch)"""
808 for i in xrange(len(self.applied)):
808 for i in xrange(len(self.applied)):
809 a = self.applied[i]
809 a = self.applied[i]
810 if a.name == patch:
810 if a.name == patch:
811 return (i, a.rev, a.name)
811 return (i, a.rev, a.name)
812 return None
812 return None
813
813
814 # if the exact patch name does not exist, we try a few
814 # if the exact patch name does not exist, we try a few
815 # variations. If strict is passed, we try only #1
815 # variations. If strict is passed, we try only #1
816 #
816 #
817 # 1) a number to indicate an offset in the series file
817 # 1) a number to indicate an offset in the series file
818 # 2) a unique substring of the patch name was given
818 # 2) a unique substring of the patch name was given
819 # 3) patchname[-+]num to indicate an offset in the series file
819 # 3) patchname[-+]num to indicate an offset in the series file
820 def lookup(self, patch, strict=False):
820 def lookup(self, patch, strict=False):
821 patch = patch and str(patch)
821 patch = patch and str(patch)
822
822
823 def partial_name(s):
823 def partial_name(s):
824 if s in self.series:
824 if s in self.series:
825 return s
825 return s
826 matches = [x for x in self.series if s in x]
826 matches = [x for x in self.series if s in x]
827 if len(matches) > 1:
827 if len(matches) > 1:
828 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
828 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
829 for m in matches:
829 for m in matches:
830 self.ui.warn(' %s\n' % m)
830 self.ui.warn(' %s\n' % m)
831 return None
831 return None
832 if matches:
832 if matches:
833 return matches[0]
833 return matches[0]
834 if len(self.series) > 0 and len(self.applied) > 0:
834 if len(self.series) > 0 and len(self.applied) > 0:
835 if s == 'qtip':
835 if s == 'qtip':
836 return self.series[self.series_end(True)-1]
836 return self.series[self.series_end(True)-1]
837 if s == 'qbase':
837 if s == 'qbase':
838 return self.series[0]
838 return self.series[0]
839 return None
839 return None
840
840
841 if patch == None:
841 if patch == None:
842 return None
842 return None
843 if patch in self.series:
843 if patch in self.series:
844 return patch
844 return patch
845
845
846 if not os.path.isfile(self.join(patch)):
846 if not os.path.isfile(self.join(patch)):
847 try:
847 try:
848 sno = int(patch)
848 sno = int(patch)
849 except(ValueError, OverflowError):
849 except(ValueError, OverflowError):
850 pass
850 pass
851 else:
851 else:
852 if -len(self.series) <= sno < len(self.series):
852 if -len(self.series) <= sno < len(self.series):
853 return self.series[sno]
853 return self.series[sno]
854
854
855 if not strict:
855 if not strict:
856 res = partial_name(patch)
856 res = partial_name(patch)
857 if res:
857 if res:
858 return res
858 return res
859 minus = patch.rfind('-')
859 minus = patch.rfind('-')
860 if minus >= 0:
860 if minus >= 0:
861 res = partial_name(patch[:minus])
861 res = partial_name(patch[:minus])
862 if res:
862 if res:
863 i = self.series.index(res)
863 i = self.series.index(res)
864 try:
864 try:
865 off = int(patch[minus+1:] or 1)
865 off = int(patch[minus+1:] or 1)
866 except(ValueError, OverflowError):
866 except(ValueError, OverflowError):
867 pass
867 pass
868 else:
868 else:
869 if i - off >= 0:
869 if i - off >= 0:
870 return self.series[i - off]
870 return self.series[i - off]
871 plus = patch.rfind('+')
871 plus = patch.rfind('+')
872 if plus >= 0:
872 if plus >= 0:
873 res = partial_name(patch[:plus])
873 res = partial_name(patch[:plus])
874 if res:
874 if res:
875 i = self.series.index(res)
875 i = self.series.index(res)
876 try:
876 try:
877 off = int(patch[plus+1:] or 1)
877 off = int(patch[plus+1:] or 1)
878 except(ValueError, OverflowError):
878 except(ValueError, OverflowError):
879 pass
879 pass
880 else:
880 else:
881 if i + off < len(self.series):
881 if i + off < len(self.series):
882 return self.series[i + off]
882 return self.series[i + off]
883 raise util.Abort(_("patch %s not in series") % patch)
883 raise util.Abort(_("patch %s not in series") % patch)
884
884
885 def push(self, repo, patch=None, force=False, list=False,
885 def push(self, repo, patch=None, force=False, list=False,
886 mergeq=None, all=False):
886 mergeq=None, all=False):
887 wlock = repo.wlock()
887 wlock = repo.wlock()
888 if repo.dirstate.parents()[0] != repo.changelog.tip():
888 if repo.dirstate.parents()[0] != repo.changelog.tip():
889 self.ui.status(_("(working directory not at tip)\n"))
889 self.ui.status(_("(working directory not at tip)\n"))
890
890
891 if not self.series:
891 if not self.series:
892 self.ui.warn(_('no patches in series\n'))
892 self.ui.warn(_('no patches in series\n'))
893 return 0
893 return 0
894
894
895 try:
895 try:
896 patch = self.lookup(patch)
896 patch = self.lookup(patch)
897 # Suppose our series file is: A B C and the current 'top'
897 # Suppose our series file is: A B C and the current 'top'
898 # patch is B. qpush C should be performed (moving forward)
898 # patch is B. qpush C should be performed (moving forward)
899 # qpush B is a NOP (no change) qpush A is an error (can't
899 # qpush B is a NOP (no change) qpush A is an error (can't
900 # go backwards with qpush)
900 # go backwards with qpush)
901 if patch:
901 if patch:
902 info = self.isapplied(patch)
902 info = self.isapplied(patch)
903 if info:
903 if info:
904 if info[0] < len(self.applied) - 1:
904 if info[0] < len(self.applied) - 1:
905 raise util.Abort(
905 raise util.Abort(
906 _("cannot push to a previous patch: %s") % patch)
906 _("cannot push to a previous patch: %s") % patch)
907 self.ui.warn(
907 self.ui.warn(
908 _('qpush: %s is already at the top\n') % patch)
908 _('qpush: %s is already at the top\n') % patch)
909 return
909 return
910 pushable, reason = self.pushable(patch)
910 pushable, reason = self.pushable(patch)
911 if not pushable:
911 if not pushable:
912 if reason:
912 if reason:
913 reason = _('guarded by %r') % reason
913 reason = _('guarded by %r') % reason
914 else:
914 else:
915 reason = _('no matching guards')
915 reason = _('no matching guards')
916 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
916 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
917 return 1
917 return 1
918 elif all:
918 elif all:
919 patch = self.series[-1]
919 patch = self.series[-1]
920 if self.isapplied(patch):
920 if self.isapplied(patch):
921 self.ui.warn(_('all patches are currently applied\n'))
921 self.ui.warn(_('all patches are currently applied\n'))
922 return 0
922 return 0
923
923
924 # Following the above example, starting at 'top' of B:
924 # Following the above example, starting at 'top' of B:
925 # qpush should be performed (pushes C), but a subsequent
925 # qpush should be performed (pushes C), but a subsequent
926 # qpush without an argument is an error (nothing to
926 # qpush without an argument is an error (nothing to
927 # apply). This allows a loop of "...while hg qpush..." to
927 # apply). This allows a loop of "...while hg qpush..." to
928 # work as it detects an error when done
928 # work as it detects an error when done
929 start = self.series_end()
929 start = self.series_end()
930 if start == len(self.series):
930 if start == len(self.series):
931 self.ui.warn(_('patch series already fully applied\n'))
931 self.ui.warn(_('patch series already fully applied\n'))
932 return 1
932 return 1
933 if not force:
933 if not force:
934 self.check_localchanges(repo)
934 self.check_localchanges(repo)
935
935
936 self.applied_dirty = 1
936 self.applied_dirty = 1
937 if start > 0:
937 if start > 0:
938 self.check_toppatch(repo)
938 self.check_toppatch(repo)
939 if not patch:
939 if not patch:
940 patch = self.series[start]
940 patch = self.series[start]
941 end = start + 1
941 end = start + 1
942 else:
942 else:
943 end = self.series.index(patch, start) + 1
943 end = self.series.index(patch, start) + 1
944 s = self.series[start:end]
944 s = self.series[start:end]
945 all_files = {}
945 all_files = {}
946 try:
946 try:
947 if mergeq:
947 if mergeq:
948 ret = self.mergepatch(repo, mergeq, s)
948 ret = self.mergepatch(repo, mergeq, s)
949 else:
949 else:
950 ret = self.apply(repo, s, list, all_files=all_files)
950 ret = self.apply(repo, s, list, all_files=all_files)
951 except:
951 except:
952 self.ui.warn(_('cleaning up working directory...'))
952 self.ui.warn(_('cleaning up working directory...'))
953 node = repo.dirstate.parents()[0]
953 node = repo.dirstate.parents()[0]
954 hg.revert(repo, node, None)
954 hg.revert(repo, node, None)
955 unknown = repo.status(unknown=True)[4]
955 unknown = repo.status(unknown=True)[4]
956 # only remove unknown files that we know we touched or
956 # only remove unknown files that we know we touched or
957 # created while patching
957 # created while patching
958 for f in unknown:
958 for f in unknown:
959 if f in all_files:
959 if f in all_files:
960 util.unlink(repo.wjoin(f))
960 util.unlink(repo.wjoin(f))
961 self.ui.warn(_('done\n'))
961 self.ui.warn(_('done\n'))
962 raise
962 raise
963 top = self.applied[-1].name
963 top = self.applied[-1].name
964 if ret[0]:
964 if ret[0]:
965 self.ui.write(_("errors during apply, please fix and "
965 self.ui.write(_("errors during apply, please fix and "
966 "refresh %s\n") % top)
966 "refresh %s\n") % top)
967 else:
967 else:
968 self.ui.write(_("now at: %s\n") % top)
968 self.ui.write(_("now at: %s\n") % top)
969 return ret[0]
969 return ret[0]
970 finally:
970 finally:
971 wlock.release()
971 wlock.release()
972
972
973 def pop(self, repo, patch=None, force=False, update=True, all=False):
973 def pop(self, repo, patch=None, force=False, update=True, all=False):
974 def getfile(f, rev, flags):
974 def getfile(f, rev, flags):
975 t = repo.file(f).read(rev)
975 t = repo.file(f).read(rev)
976 repo.wwrite(f, t, flags)
976 repo.wwrite(f, t, flags)
977
977
978 wlock = repo.wlock()
978 wlock = repo.wlock()
979 try:
979 try:
980 if patch:
980 if patch:
981 # index, rev, patch
981 # index, rev, patch
982 info = self.isapplied(patch)
982 info = self.isapplied(patch)
983 if not info:
983 if not info:
984 patch = self.lookup(patch)
984 patch = self.lookup(patch)
985 info = self.isapplied(patch)
985 info = self.isapplied(patch)
986 if not info:
986 if not info:
987 raise util.Abort(_("patch %s is not applied") % patch)
987 raise util.Abort(_("patch %s is not applied") % patch)
988
988
989 if len(self.applied) == 0:
989 if len(self.applied) == 0:
990 # Allow qpop -a to work repeatedly,
990 # Allow qpop -a to work repeatedly,
991 # but not qpop without an argument
991 # but not qpop without an argument
992 self.ui.warn(_("no patches applied\n"))
992 self.ui.warn(_("no patches applied\n"))
993 return not all
993 return not all
994
994
995 if all:
995 if all:
996 start = 0
996 start = 0
997 elif patch:
997 elif patch:
998 start = info[0] + 1
998 start = info[0] + 1
999 else:
999 else:
1000 start = len(self.applied) - 1
1000 start = len(self.applied) - 1
1001
1001
1002 if start >= len(self.applied):
1002 if start >= len(self.applied):
1003 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1003 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1004 return
1004 return
1005
1005
1006 if not update:
1006 if not update:
1007 parents = repo.dirstate.parents()
1007 parents = repo.dirstate.parents()
1008 rr = [ bin(x.rev) for x in self.applied ]
1008 rr = [ bin(x.rev) for x in self.applied ]
1009 for p in parents:
1009 for p in parents:
1010 if p in rr:
1010 if p in rr:
1011 self.ui.warn(_("qpop: forcing dirstate update\n"))
1011 self.ui.warn(_("qpop: forcing dirstate update\n"))
1012 update = True
1012 update = True
1013 else:
1013 else:
1014 parents = [p.hex() for p in repo[None].parents()]
1014 parents = [p.hex() for p in repo[None].parents()]
1015 needupdate = False
1015 needupdate = False
1016 for entry in self.applied[start:]:
1016 for entry in self.applied[start:]:
1017 if entry.rev in parents:
1017 if entry.rev in parents:
1018 needupdate = True
1018 needupdate = True
1019 break
1019 break
1020 update = needupdate
1020 update = needupdate
1021
1021
1022 if not force and update:
1022 if not force and update:
1023 self.check_localchanges(repo)
1023 self.check_localchanges(repo)
1024
1024
1025 self.applied_dirty = 1
1025 self.applied_dirty = 1
1026 end = len(self.applied)
1026 end = len(self.applied)
1027 rev = bin(self.applied[start].rev)
1027 rev = bin(self.applied[start].rev)
1028 if update:
1028 if update:
1029 top = self.check_toppatch(repo)
1029 top = self.check_toppatch(repo)
1030
1030
1031 try:
1031 try:
1032 heads = repo.changelog.heads(rev)
1032 heads = repo.changelog.heads(rev)
1033 except error.LookupError:
1033 except error.LookupError:
1034 node = short(rev)
1034 node = short(rev)
1035 raise util.Abort(_('trying to pop unknown node %s') % node)
1035 raise util.Abort(_('trying to pop unknown node %s') % node)
1036
1036
1037 if heads != [bin(self.applied[-1].rev)]:
1037 if heads != [bin(self.applied[-1].rev)]:
1038 raise util.Abort(_("popping would remove a revision not "
1038 raise util.Abort(_("popping would remove a revision not "
1039 "managed by this patch queue"))
1039 "managed by this patch queue"))
1040
1040
1041 # we know there are no local changes, so we can make a simplified
1041 # we know there are no local changes, so we can make a simplified
1042 # form of hg.update.
1042 # form of hg.update.
1043 if update:
1043 if update:
1044 qp = self.qparents(repo, rev)
1044 qp = self.qparents(repo, rev)
1045 changes = repo.changelog.read(qp)
1045 changes = repo.changelog.read(qp)
1046 mmap = repo.manifest.read(changes[0])
1046 mmap = repo.manifest.read(changes[0])
1047 m, a, r, d = repo.status(qp, top)[:4]
1047 m, a, r, d = repo.status(qp, top)[:4]
1048 if d:
1048 if d:
1049 raise util.Abort(_("deletions found between repo revs"))
1049 raise util.Abort(_("deletions found between repo revs"))
1050 for f in m:
1050 for f in m:
1051 getfile(f, mmap[f], mmap.flags(f))
1051 getfile(f, mmap[f], mmap.flags(f))
1052 for f in r:
1052 for f in r:
1053 getfile(f, mmap[f], mmap.flags(f))
1053 getfile(f, mmap[f], mmap.flags(f))
1054 for f in m + r:
1054 for f in m + r:
1055 repo.dirstate.normal(f)
1055 repo.dirstate.normal(f)
1056 for f in a:
1056 for f in a:
1057 try:
1057 try:
1058 os.unlink(repo.wjoin(f))
1058 os.unlink(repo.wjoin(f))
1059 except OSError, e:
1059 except OSError, e:
1060 if e.errno != errno.ENOENT:
1060 if e.errno != errno.ENOENT:
1061 raise
1061 raise
1062 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1062 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1063 except: pass
1063 except: pass
1064 repo.dirstate.forget(f)
1064 repo.dirstate.forget(f)
1065 repo.dirstate.setparents(qp, nullid)
1065 repo.dirstate.setparents(qp, nullid)
1066 del self.applied[start:end]
1066 del self.applied[start:end]
1067 self.strip(repo, rev, update=False, backup='strip')
1067 self.strip(repo, rev, update=False, backup='strip')
1068 if len(self.applied):
1068 if len(self.applied):
1069 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1069 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1070 else:
1070 else:
1071 self.ui.write(_("patch queue now empty\n"))
1071 self.ui.write(_("patch queue now empty\n"))
1072 finally:
1072 finally:
1073 wlock.release()
1073 wlock.release()
1074
1074
1075 def diff(self, repo, pats, opts):
1075 def diff(self, repo, pats, opts):
1076 top = self.check_toppatch(repo)
1076 top = self.check_toppatch(repo)
1077 if not top:
1077 if not top:
1078 self.ui.write(_("no patches applied\n"))
1078 self.ui.write(_("no patches applied\n"))
1079 return
1079 return
1080 qp = self.qparents(repo, top)
1080 qp = self.qparents(repo, top)
1081 self._diffopts = patch.diffopts(self.ui, opts)
1081 self._diffopts = patch.diffopts(self.ui, opts)
1082 self.printdiff(repo, qp, files=pats, opts=opts)
1082 self.printdiff(repo, qp, files=pats, opts=opts)
1083
1083
1084 def refresh(self, repo, pats=None, **opts):
1084 def refresh(self, repo, pats=None, **opts):
1085 if len(self.applied) == 0:
1085 if len(self.applied) == 0:
1086 self.ui.write(_("no patches applied\n"))
1086 self.ui.write(_("no patches applied\n"))
1087 return 1
1087 return 1
1088 msg = opts.get('msg', '').rstrip()
1088 msg = opts.get('msg', '').rstrip()
1089 newuser = opts.get('user')
1089 newuser = opts.get('user')
1090 newdate = opts.get('date')
1090 newdate = opts.get('date')
1091 if newdate:
1091 if newdate:
1092 newdate = '%d %d' % util.parsedate(newdate)
1092 newdate = '%d %d' % util.parsedate(newdate)
1093 wlock = repo.wlock()
1093 wlock = repo.wlock()
1094 try:
1094 try:
1095 self.check_toppatch(repo)
1095 self.check_toppatch(repo)
1096 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1096 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1097 top = bin(top)
1097 top = bin(top)
1098 if repo.changelog.heads(top) != [top]:
1098 if repo.changelog.heads(top) != [top]:
1099 raise util.Abort(_("cannot refresh a revision with children"))
1099 raise util.Abort(_("cannot refresh a revision with children"))
1100 cparents = repo.changelog.parents(top)
1100 cparents = repo.changelog.parents(top)
1101 patchparent = self.qparents(repo, top)
1101 patchparent = self.qparents(repo, top)
1102 ph = self.readheaders(patchfn)
1102 ph = self.readheaders(patchfn)
1103
1103
1104 patchf = self.opener(patchfn, 'r')
1104 patchf = self.opener(patchfn, 'r')
1105
1105
1106 # if the patch was a git patch, refresh it as a git patch
1106 # if the patch was a git patch, refresh it as a git patch
1107 for line in patchf:
1107 for line in patchf:
1108 if line.startswith('diff --git'):
1108 if line.startswith('diff --git'):
1109 self.diffopts().git = True
1109 self.diffopts().git = True
1110 break
1110 break
1111
1111
1112 if msg:
1112 if msg:
1113 ph.setmessage(msg)
1113 ph.setmessage(msg)
1114 if newuser:
1114 if newuser:
1115 ph.setuser(newuser)
1115 ph.setuser(newuser)
1116 if newdate:
1116 if newdate:
1117 ph.setdate(newdate)
1117 ph.setdate(newdate)
1118
1118
1119 # only commit new patch when write is complete
1119 # only commit new patch when write is complete
1120 patchf = self.opener(patchfn, 'w', atomictemp=True)
1120 patchf = self.opener(patchfn, 'w', atomictemp=True)
1121
1121
1122 patchf.seek(0)
1122 patchf.seek(0)
1123 patchf.truncate()
1123 patchf.truncate()
1124
1124
1125 comments = str(ph)
1125 comments = str(ph)
1126 if comments:
1126 if comments:
1127 patchf.write(comments)
1127 patchf.write(comments)
1128
1128
1129 if opts.get('git'):
1129 if opts.get('git'):
1130 self.diffopts().git = True
1130 self.diffopts().git = True
1131 tip = repo.changelog.tip()
1131 tip = repo.changelog.tip()
1132 if top == tip:
1132 if top == tip:
1133 # if the top of our patch queue is also the tip, there is an
1133 # if the top of our patch queue is also the tip, there is an
1134 # optimization here. We update the dirstate in place and strip
1134 # optimization here. We update the dirstate in place and strip
1135 # off the tip commit. Then just commit the current directory
1135 # off the tip commit. Then just commit the current directory
1136 # tree. We can also send repo.commit the list of files
1136 # tree. We can also send repo.commit the list of files
1137 # changed to speed up the diff
1137 # changed to speed up the diff
1138 #
1138 #
1139 # in short mode, we only diff the files included in the
1139 # in short mode, we only diff the files included in the
1140 # patch already plus specified files
1140 # patch already plus specified files
1141 #
1141 #
1142 # this should really read:
1142 # this should really read:
1143 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1143 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1144 # but we do it backwards to take advantage of manifest/chlog
1144 # but we do it backwards to take advantage of manifest/chlog
1145 # caching against the next repo.status call
1145 # caching against the next repo.status call
1146 #
1146 #
1147 mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
1147 mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
1148 changes = repo.changelog.read(tip)
1148 changes = repo.changelog.read(tip)
1149 man = repo.manifest.read(changes[0])
1149 man = repo.manifest.read(changes[0])
1150 aaa = aa[:]
1150 aaa = aa[:]
1151 matchfn = cmdutil.match(repo, pats, opts)
1151 matchfn = cmdutil.match(repo, pats, opts)
1152 if opts.get('short'):
1152 if opts.get('short'):
1153 # if amending a patch, we start with existing
1153 # if amending a patch, we start with existing
1154 # files plus specified files - unfiltered
1154 # files plus specified files - unfiltered
1155 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1155 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1156 # filter with inc/exl options
1156 # filter with inc/exl options
1157 matchfn = cmdutil.match(repo, opts=opts)
1157 matchfn = cmdutil.match(repo, opts=opts)
1158 else:
1158 else:
1159 match = cmdutil.matchall(repo)
1159 match = cmdutil.matchall(repo)
1160 m, a, r, d = repo.status(match=match)[:4]
1160 m, a, r, d = repo.status(match=match)[:4]
1161
1161
1162 # we might end up with files that were added between
1162 # we might end up with files that were added between
1163 # tip and the dirstate parent, but then changed in the
1163 # tip and the dirstate parent, but then changed in the
1164 # local dirstate. in this case, we want them to only
1164 # local dirstate. in this case, we want them to only
1165 # show up in the added section
1165 # show up in the added section
1166 for x in m:
1166 for x in m:
1167 if x not in aa:
1167 if x not in aa:
1168 mm.append(x)
1168 mm.append(x)
1169 # we might end up with files added by the local dirstate that
1169 # we might end up with files added by the local dirstate that
1170 # were deleted by the patch. In this case, they should only
1170 # were deleted by the patch. In this case, they should only
1171 # show up in the changed section.
1171 # show up in the changed section.
1172 for x in a:
1172 for x in a:
1173 if x in dd:
1173 if x in dd:
1174 del dd[dd.index(x)]
1174 del dd[dd.index(x)]
1175 mm.append(x)
1175 mm.append(x)
1176 else:
1176 else:
1177 aa.append(x)
1177 aa.append(x)
1178 # make sure any files deleted in the local dirstate
1178 # make sure any files deleted in the local dirstate
1179 # are not in the add or change column of the patch
1179 # are not in the add or change column of the patch
1180 forget = []
1180 forget = []
1181 for x in d + r:
1181 for x in d + r:
1182 if x in aa:
1182 if x in aa:
1183 del aa[aa.index(x)]
1183 del aa[aa.index(x)]
1184 forget.append(x)
1184 forget.append(x)
1185 continue
1185 continue
1186 elif x in mm:
1186 elif x in mm:
1187 del mm[mm.index(x)]
1187 del mm[mm.index(x)]
1188 dd.append(x)
1188 dd.append(x)
1189
1189
1190 m = list(set(mm))
1190 m = list(set(mm))
1191 r = list(set(dd))
1191 r = list(set(dd))
1192 a = list(set(aa))
1192 a = list(set(aa))
1193 c = [filter(matchfn, l) for l in (m, a, r)]
1193 c = [filter(matchfn, l) for l in (m, a, r)]
1194 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1194 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1195 chunks = patch.diff(repo, patchparent, match=match,
1195 chunks = patch.diff(repo, patchparent, match=match,
1196 changes=c, opts=self.diffopts())
1196 changes=c, opts=self.diffopts())
1197 for chunk in chunks:
1197 for chunk in chunks:
1198 patchf.write(chunk)
1198 patchf.write(chunk)
1199
1199
1200 try:
1200 try:
1201 if self.diffopts().git:
1201 if self.diffopts().git:
1202 copies = {}
1202 copies = {}
1203 for dst in a:
1203 for dst in a:
1204 src = repo.dirstate.copied(dst)
1204 src = repo.dirstate.copied(dst)
1205 # during qfold, the source file for copies may
1205 # during qfold, the source file for copies may
1206 # be removed. Treat this as a simple add.
1206 # be removed. Treat this as a simple add.
1207 if src is not None and src in repo.dirstate:
1207 if src is not None and src in repo.dirstate:
1208 copies.setdefault(src, []).append(dst)
1208 copies.setdefault(src, []).append(dst)
1209 repo.dirstate.add(dst)
1209 repo.dirstate.add(dst)
1210 # remember the copies between patchparent and tip
1210 # remember the copies between patchparent and tip
1211 for dst in aaa:
1211 for dst in aaa:
1212 f = repo.file(dst)
1212 f = repo.file(dst)
1213 src = f.renamed(man[dst])
1213 src = f.renamed(man[dst])
1214 if src:
1214 if src:
1215 copies.setdefault(src[0], []).extend(copies.get(dst, []))
1215 copies.setdefault(src[0], []).extend(copies.get(dst, []))
1216 if dst in a:
1216 if dst in a:
1217 copies[src[0]].append(dst)
1217 copies[src[0]].append(dst)
1218 # we can't copy a file created by the patch itself
1218 # we can't copy a file created by the patch itself
1219 if dst in copies:
1219 if dst in copies:
1220 del copies[dst]
1220 del copies[dst]
1221 for src, dsts in copies.iteritems():
1221 for src, dsts in copies.iteritems():
1222 for dst in dsts:
1222 for dst in dsts:
1223 repo.dirstate.copy(src, dst)
1223 repo.dirstate.copy(src, dst)
1224 else:
1224 else:
1225 for dst in a:
1225 for dst in a:
1226 repo.dirstate.add(dst)
1226 repo.dirstate.add(dst)
1227 # Drop useless copy information
1227 # Drop useless copy information
1228 for f in list(repo.dirstate.copies()):
1228 for f in list(repo.dirstate.copies()):
1229 repo.dirstate.copy(None, f)
1229 repo.dirstate.copy(None, f)
1230 for f in r:
1230 for f in r:
1231 repo.dirstate.remove(f)
1231 repo.dirstate.remove(f)
1232 # if the patch excludes a modified file, mark that
1232 # if the patch excludes a modified file, mark that
1233 # file with mtime=0 so status can see it.
1233 # file with mtime=0 so status can see it.
1234 mm = []
1234 mm = []
1235 for i in xrange(len(m)-1, -1, -1):
1235 for i in xrange(len(m)-1, -1, -1):
1236 if not matchfn(m[i]):
1236 if not matchfn(m[i]):
1237 mm.append(m[i])
1237 mm.append(m[i])
1238 del m[i]
1238 del m[i]
1239 for f in m:
1239 for f in m:
1240 repo.dirstate.normal(f)
1240 repo.dirstate.normal(f)
1241 for f in mm:
1241 for f in mm:
1242 repo.dirstate.normallookup(f)
1242 repo.dirstate.normallookup(f)
1243 for f in forget:
1243 for f in forget:
1244 repo.dirstate.forget(f)
1244 repo.dirstate.forget(f)
1245
1245
1246 if not msg:
1246 if not msg:
1247 if not ph.message:
1247 if not ph.message:
1248 message = "[mq]: %s\n" % patchfn
1248 message = "[mq]: %s\n" % patchfn
1249 else:
1249 else:
1250 message = "\n".join(ph.message)
1250 message = "\n".join(ph.message)
1251 else:
1251 else:
1252 message = msg
1252 message = msg
1253
1253
1254 user = ph.user or changes[1]
1254 user = ph.user or changes[1]
1255
1255
1256 # assumes strip can roll itself back if interrupted
1256 # assumes strip can roll itself back if interrupted
1257 repo.dirstate.setparents(*cparents)
1257 repo.dirstate.setparents(*cparents)
1258 self.applied.pop()
1258 self.applied.pop()
1259 self.applied_dirty = 1
1259 self.applied_dirty = 1
1260 self.strip(repo, top, update=False,
1260 self.strip(repo, top, update=False,
1261 backup='strip')
1261 backup='strip')
1262 except:
1262 except:
1263 repo.dirstate.invalidate()
1263 repo.dirstate.invalidate()
1264 raise
1264 raise
1265
1265
1266 try:
1266 try:
1267 # might be nice to attempt to roll back strip after this
1267 # might be nice to attempt to roll back strip after this
1268 patchf.rename()
1268 patchf.rename()
1269 n = repo.commit(match.files(), message, user, ph.date,
1269 n = repo.commit(match.files(), message, user, ph.date,
1270 match=match, force=1)
1270 match=match, force=1)
1271 self.applied.append(statusentry(hex(n), patchfn))
1271 self.applied.append(statusentry(hex(n), patchfn))
1272 except:
1272 except:
1273 ctx = repo[cparents[0]]
1273 ctx = repo[cparents[0]]
1274 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1274 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1275 self.save_dirty()
1275 self.save_dirty()
1276 self.ui.warn(_('refresh interrupted while patch was popped! '
1276 self.ui.warn(_('refresh interrupted while patch was popped! '
1277 '(revert --all, qpush to recover)\n'))
1277 '(revert --all, qpush to recover)\n'))
1278 raise
1278 raise
1279 else:
1279 else:
1280 self.printdiff(repo, patchparent, fp=patchf)
1280 self.printdiff(repo, patchparent, fp=patchf)
1281 patchf.rename()
1281 patchf.rename()
1282 added = repo.status()[1]
1282 added = repo.status()[1]
1283 for a in added:
1283 for a in added:
1284 f = repo.wjoin(a)
1284 f = repo.wjoin(a)
1285 try:
1285 try:
1286 os.unlink(f)
1286 os.unlink(f)
1287 except OSError, e:
1287 except OSError, e:
1288 if e.errno != errno.ENOENT:
1288 if e.errno != errno.ENOENT:
1289 raise
1289 raise
1290 try: os.removedirs(os.path.dirname(f))
1290 try: os.removedirs(os.path.dirname(f))
1291 except: pass
1291 except: pass
1292 # forget the file copies in the dirstate
1292 # forget the file copies in the dirstate
1293 # push should readd the files later on
1293 # push should readd the files later on
1294 repo.dirstate.forget(a)
1294 repo.dirstate.forget(a)
1295 self.pop(repo, force=True)
1295 self.pop(repo, force=True)
1296 self.push(repo, force=True)
1296 self.push(repo, force=True)
1297 finally:
1297 finally:
1298 wlock.release()
1298 wlock.release()
1299 self.removeundo(repo)
1299 self.removeundo(repo)
1300
1300
1301 def init(self, repo, create=False):
1301 def init(self, repo, create=False):
1302 if not create and os.path.isdir(self.path):
1302 if not create and os.path.isdir(self.path):
1303 raise util.Abort(_("patch queue directory already exists"))
1303 raise util.Abort(_("patch queue directory already exists"))
1304 try:
1304 try:
1305 os.mkdir(self.path)
1305 os.mkdir(self.path)
1306 except OSError, inst:
1306 except OSError, inst:
1307 if inst.errno != errno.EEXIST or not create:
1307 if inst.errno != errno.EEXIST or not create:
1308 raise
1308 raise
1309 if create:
1309 if create:
1310 return self.qrepo(create=True)
1310 return self.qrepo(create=True)
1311
1311
1312 def unapplied(self, repo, patch=None):
1312 def unapplied(self, repo, patch=None):
1313 if patch and patch not in self.series:
1313 if patch and patch not in self.series:
1314 raise util.Abort(_("patch %s is not in series file") % patch)
1314 raise util.Abort(_("patch %s is not in series file") % patch)
1315 if not patch:
1315 if not patch:
1316 start = self.series_end()
1316 start = self.series_end()
1317 else:
1317 else:
1318 start = self.series.index(patch) + 1
1318 start = self.series.index(patch) + 1
1319 unapplied = []
1319 unapplied = []
1320 for i in xrange(start, len(self.series)):
1320 for i in xrange(start, len(self.series)):
1321 pushable, reason = self.pushable(i)
1321 pushable, reason = self.pushable(i)
1322 if pushable:
1322 if pushable:
1323 unapplied.append((i, self.series[i]))
1323 unapplied.append((i, self.series[i]))
1324 self.explain_pushable(i)
1324 self.explain_pushable(i)
1325 return unapplied
1325 return unapplied
1326
1326
1327 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1327 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1328 summary=False):
1328 summary=False):
1329 def displayname(patchname):
1329 def displayname(patchname):
1330 if summary:
1330 if summary:
1331 ph = self.readheaders(patchname)
1331 ph = self.readheaders(patchname)
1332 msg = ph.message
1332 msg = ph.message
1333 msg = msg and ': ' + msg[0] or ': '
1333 msg = msg and ': ' + msg[0] or ': '
1334 else:
1334 else:
1335 msg = ''
1335 msg = ''
1336 return '%s%s' % (patchname, msg)
1336 return '%s%s' % (patchname, msg)
1337
1337
1338 applied = set([p.name for p in self.applied])
1338 applied = set([p.name for p in self.applied])
1339 if length is None:
1339 if length is None:
1340 length = len(self.series) - start
1340 length = len(self.series) - start
1341 if not missing:
1341 if not missing:
1342 for i in xrange(start, start+length):
1342 for i in xrange(start, start+length):
1343 patch = self.series[i]
1343 patch = self.series[i]
1344 if patch in applied:
1344 if patch in applied:
1345 stat = 'A'
1345 stat = 'A'
1346 elif self.pushable(i)[0]:
1346 elif self.pushable(i)[0]:
1347 stat = 'U'
1347 stat = 'U'
1348 else:
1348 else:
1349 stat = 'G'
1349 stat = 'G'
1350 pfx = ''
1350 pfx = ''
1351 if self.ui.verbose:
1351 if self.ui.verbose:
1352 pfx = '%d %s ' % (i, stat)
1352 pfx = '%d %s ' % (i, stat)
1353 elif status and status != stat:
1353 elif status and status != stat:
1354 continue
1354 continue
1355 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1355 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1356 else:
1356 else:
1357 msng_list = []
1357 msng_list = []
1358 for root, dirs, files in os.walk(self.path):
1358 for root, dirs, files in os.walk(self.path):
1359 d = root[len(self.path) + 1:]
1359 d = root[len(self.path) + 1:]
1360 for f in files:
1360 for f in files:
1361 fl = os.path.join(d, f)
1361 fl = os.path.join(d, f)
1362 if (fl not in self.series and
1362 if (fl not in self.series and
1363 fl not in (self.status_path, self.series_path,
1363 fl not in (self.status_path, self.series_path,
1364 self.guards_path)
1364 self.guards_path)
1365 and not fl.startswith('.')):
1365 and not fl.startswith('.')):
1366 msng_list.append(fl)
1366 msng_list.append(fl)
1367 for x in sorted(msng_list):
1367 for x in sorted(msng_list):
1368 pfx = self.ui.verbose and ('D ') or ''
1368 pfx = self.ui.verbose and ('D ') or ''
1369 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1369 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1370
1370
1371 def issaveline(self, l):
1371 def issaveline(self, l):
1372 if l.name == '.hg.patches.save.line':
1372 if l.name == '.hg.patches.save.line':
1373 return True
1373 return True
1374
1374
1375 def qrepo(self, create=False):
1375 def qrepo(self, create=False):
1376 if create or os.path.isdir(self.join(".hg")):
1376 if create or os.path.isdir(self.join(".hg")):
1377 return hg.repository(self.ui, path=self.path, create=create)
1377 return hg.repository(self.ui, path=self.path, create=create)
1378
1378
1379 def restore(self, repo, rev, delete=None, qupdate=None):
1379 def restore(self, repo, rev, delete=None, qupdate=None):
1380 c = repo.changelog.read(rev)
1380 c = repo.changelog.read(rev)
1381 desc = c[4].strip()
1381 desc = c[4].strip()
1382 lines = desc.splitlines()
1382 lines = desc.splitlines()
1383 i = 0
1383 i = 0
1384 datastart = None
1384 datastart = None
1385 series = []
1385 series = []
1386 applied = []
1386 applied = []
1387 qpp = None
1387 qpp = None
1388 for i in xrange(0, len(lines)):
1388 for i in xrange(0, len(lines)):
1389 if lines[i] == 'Patch Data:':
1389 if lines[i] == 'Patch Data:':
1390 datastart = i + 1
1390 datastart = i + 1
1391 elif lines[i].startswith('Dirstate:'):
1391 elif lines[i].startswith('Dirstate:'):
1392 l = lines[i].rstrip()
1392 l = lines[i].rstrip()
1393 l = l[10:].split(' ')
1393 l = l[10:].split(' ')
1394 qpp = [ bin(x) for x in l ]
1394 qpp = [ bin(x) for x in l ]
1395 elif datastart != None:
1395 elif datastart != None:
1396 l = lines[i].rstrip()
1396 l = lines[i].rstrip()
1397 se = statusentry(l)
1397 se = statusentry(l)
1398 file_ = se.name
1398 file_ = se.name
1399 if se.rev:
1399 if se.rev:
1400 applied.append(se)
1400 applied.append(se)
1401 else:
1401 else:
1402 series.append(file_)
1402 series.append(file_)
1403 if datastart == None:
1403 if datastart == None:
1404 self.ui.warn(_("No saved patch data found\n"))
1404 self.ui.warn(_("No saved patch data found\n"))
1405 return 1
1405 return 1
1406 self.ui.warn(_("restoring status: %s\n") % lines[0])
1406 self.ui.warn(_("restoring status: %s\n") % lines[0])
1407 self.full_series = series
1407 self.full_series = series
1408 self.applied = applied
1408 self.applied = applied
1409 self.parse_series()
1409 self.parse_series()
1410 self.series_dirty = 1
1410 self.series_dirty = 1
1411 self.applied_dirty = 1
1411 self.applied_dirty = 1
1412 heads = repo.changelog.heads()
1412 heads = repo.changelog.heads()
1413 if delete:
1413 if delete:
1414 if rev not in heads:
1414 if rev not in heads:
1415 self.ui.warn(_("save entry has children, leaving it alone\n"))
1415 self.ui.warn(_("save entry has children, leaving it alone\n"))
1416 else:
1416 else:
1417 self.ui.warn(_("removing save entry %s\n") % short(rev))
1417 self.ui.warn(_("removing save entry %s\n") % short(rev))
1418 pp = repo.dirstate.parents()
1418 pp = repo.dirstate.parents()
1419 if rev in pp:
1419 if rev in pp:
1420 update = True
1420 update = True
1421 else:
1421 else:
1422 update = False
1422 update = False
1423 self.strip(repo, rev, update=update, backup='strip')
1423 self.strip(repo, rev, update=update, backup='strip')
1424 if qpp:
1424 if qpp:
1425 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1425 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1426 (short(qpp[0]), short(qpp[1])))
1426 (short(qpp[0]), short(qpp[1])))
1427 if qupdate:
1427 if qupdate:
1428 self.ui.status(_("queue directory updating\n"))
1428 self.ui.status(_("queue directory updating\n"))
1429 r = self.qrepo()
1429 r = self.qrepo()
1430 if not r:
1430 if not r:
1431 self.ui.warn(_("Unable to load queue repository\n"))
1431 self.ui.warn(_("Unable to load queue repository\n"))
1432 return 1
1432 return 1
1433 hg.clean(r, qpp[0])
1433 hg.clean(r, qpp[0])
1434
1434
1435 def save(self, repo, msg=None):
1435 def save(self, repo, msg=None):
1436 if len(self.applied) == 0:
1436 if len(self.applied) == 0:
1437 self.ui.warn(_("save: no patches applied, exiting\n"))
1437 self.ui.warn(_("save: no patches applied, exiting\n"))
1438 return 1
1438 return 1
1439 if self.issaveline(self.applied[-1]):
1439 if self.issaveline(self.applied[-1]):
1440 self.ui.warn(_("status is already saved\n"))
1440 self.ui.warn(_("status is already saved\n"))
1441 return 1
1441 return 1
1442
1442
1443 ar = [ ':' + x for x in self.full_series ]
1443 ar = [ ':' + x for x in self.full_series ]
1444 if not msg:
1444 if not msg:
1445 msg = _("hg patches saved state")
1445 msg = _("hg patches saved state")
1446 else:
1446 else:
1447 msg = "hg patches: " + msg.rstrip('\r\n')
1447 msg = "hg patches: " + msg.rstrip('\r\n')
1448 r = self.qrepo()
1448 r = self.qrepo()
1449 if r:
1449 if r:
1450 pp = r.dirstate.parents()
1450 pp = r.dirstate.parents()
1451 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1451 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1452 msg += "\n\nPatch Data:\n"
1452 msg += "\n\nPatch Data:\n"
1453 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1453 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1454 "\n".join(ar) + '\n' or "")
1454 "\n".join(ar) + '\n' or "")
1455 n = repo.commit(None, text, user=None, force=1)
1455 n = repo.commit(None, text, user=None, force=1)
1456 if not n:
1456 if not n:
1457 self.ui.warn(_("repo commit failed\n"))
1457 self.ui.warn(_("repo commit failed\n"))
1458 return 1
1458 return 1
1459 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1459 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1460 self.applied_dirty = 1
1460 self.applied_dirty = 1
1461 self.removeundo(repo)
1461 self.removeundo(repo)
1462
1462
1463 def full_series_end(self):
1463 def full_series_end(self):
1464 if len(self.applied) > 0:
1464 if len(self.applied) > 0:
1465 p = self.applied[-1].name
1465 p = self.applied[-1].name
1466 end = self.find_series(p)
1466 end = self.find_series(p)
1467 if end == None:
1467 if end == None:
1468 return len(self.full_series)
1468 return len(self.full_series)
1469 return end + 1
1469 return end + 1
1470 return 0
1470 return 0
1471
1471
1472 def series_end(self, all_patches=False):
1472 def series_end(self, all_patches=False):
1473 """If all_patches is False, return the index of the next pushable patch
1473 """If all_patches is False, return the index of the next pushable patch
1474 in the series, or the series length. If all_patches is True, return the
1474 in the series, or the series length. If all_patches is True, return the
1475 index of the first patch past the last applied one.
1475 index of the first patch past the last applied one.
1476 """
1476 """
1477 end = 0
1477 end = 0
1478 def next(start):
1478 def next(start):
1479 if all_patches:
1479 if all_patches:
1480 return start
1480 return start
1481 i = start
1481 i = start
1482 while i < len(self.series):
1482 while i < len(self.series):
1483 p, reason = self.pushable(i)
1483 p, reason = self.pushable(i)
1484 if p:
1484 if p:
1485 break
1485 break
1486 self.explain_pushable(i)
1486 self.explain_pushable(i)
1487 i += 1
1487 i += 1
1488 return i
1488 return i
1489 if len(self.applied) > 0:
1489 if len(self.applied) > 0:
1490 p = self.applied[-1].name
1490 p = self.applied[-1].name
1491 try:
1491 try:
1492 end = self.series.index(p)
1492 end = self.series.index(p)
1493 except ValueError:
1493 except ValueError:
1494 return 0
1494 return 0
1495 return next(end + 1)
1495 return next(end + 1)
1496 return next(end)
1496 return next(end)
1497
1497
1498 def appliedname(self, index):
1498 def appliedname(self, index):
1499 pname = self.applied[index].name
1499 pname = self.applied[index].name
1500 if not self.ui.verbose:
1500 if not self.ui.verbose:
1501 p = pname
1501 p = pname
1502 else:
1502 else:
1503 p = str(self.series.index(pname)) + " " + pname
1503 p = str(self.series.index(pname)) + " " + pname
1504 return p
1504 return p
1505
1505
1506 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1506 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1507 force=None, git=False):
1507 force=None, git=False):
1508 def checkseries(patchname):
1508 def checkseries(patchname):
1509 if patchname in self.series:
1509 if patchname in self.series:
1510 raise util.Abort(_('patch %s is already in the series file')
1510 raise util.Abort(_('patch %s is already in the series file')
1511 % patchname)
1511 % patchname)
1512 def checkfile(patchname):
1512 def checkfile(patchname):
1513 if not force and os.path.exists(self.join(patchname)):
1513 if not force and os.path.exists(self.join(patchname)):
1514 raise util.Abort(_('patch "%s" already exists')
1514 raise util.Abort(_('patch "%s" already exists')
1515 % patchname)
1515 % patchname)
1516
1516
1517 if rev:
1517 if rev:
1518 if files:
1518 if files:
1519 raise util.Abort(_('option "-r" not valid when importing '
1519 raise util.Abort(_('option "-r" not valid when importing '
1520 'files'))
1520 'files'))
1521 rev = cmdutil.revrange(repo, rev)
1521 rev = cmdutil.revrange(repo, rev)
1522 rev.sort(lambda x, y: cmp(y, x))
1522 rev.sort(lambda x, y: cmp(y, x))
1523 if (len(files) > 1 or len(rev) > 1) and patchname:
1523 if (len(files) > 1 or len(rev) > 1) and patchname:
1524 raise util.Abort(_('option "-n" not valid when importing multiple '
1524 raise util.Abort(_('option "-n" not valid when importing multiple '
1525 'patches'))
1525 'patches'))
1526 i = 0
1526 i = 0
1527 added = []
1527 added = []
1528 if rev:
1528 if rev:
1529 # If mq patches are applied, we can only import revisions
1529 # If mq patches are applied, we can only import revisions
1530 # that form a linear path to qbase.
1530 # that form a linear path to qbase.
1531 # Otherwise, they should form a linear path to a head.
1531 # Otherwise, they should form a linear path to a head.
1532 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1532 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1533 if len(heads) > 1:
1533 if len(heads) > 1:
1534 raise util.Abort(_('revision %d is the root of more than one '
1534 raise util.Abort(_('revision %d is the root of more than one '
1535 'branch') % rev[-1])
1535 'branch') % rev[-1])
1536 if self.applied:
1536 if self.applied:
1537 base = hex(repo.changelog.node(rev[0]))
1537 base = hex(repo.changelog.node(rev[0]))
1538 if base in [n.rev for n in self.applied]:
1538 if base in [n.rev for n in self.applied]:
1539 raise util.Abort(_('revision %d is already managed')
1539 raise util.Abort(_('revision %d is already managed')
1540 % rev[0])
1540 % rev[0])
1541 if heads != [bin(self.applied[-1].rev)]:
1541 if heads != [bin(self.applied[-1].rev)]:
1542 raise util.Abort(_('revision %d is not the parent of '
1542 raise util.Abort(_('revision %d is not the parent of '
1543 'the queue') % rev[0])
1543 'the queue') % rev[0])
1544 base = repo.changelog.rev(bin(self.applied[0].rev))
1544 base = repo.changelog.rev(bin(self.applied[0].rev))
1545 lastparent = repo.changelog.parentrevs(base)[0]
1545 lastparent = repo.changelog.parentrevs(base)[0]
1546 else:
1546 else:
1547 if heads != [repo.changelog.node(rev[0])]:
1547 if heads != [repo.changelog.node(rev[0])]:
1548 raise util.Abort(_('revision %d has unmanaged children')
1548 raise util.Abort(_('revision %d has unmanaged children')
1549 % rev[0])
1549 % rev[0])
1550 lastparent = None
1550 lastparent = None
1551
1551
1552 if git:
1552 if git:
1553 self.diffopts().git = True
1553 self.diffopts().git = True
1554
1554
1555 for r in rev:
1555 for r in rev:
1556 p1, p2 = repo.changelog.parentrevs(r)
1556 p1, p2 = repo.changelog.parentrevs(r)
1557 n = repo.changelog.node(r)
1557 n = repo.changelog.node(r)
1558 if p2 != nullrev:
1558 if p2 != nullrev:
1559 raise util.Abort(_('cannot import merge revision %d') % r)
1559 raise util.Abort(_('cannot import merge revision %d') % r)
1560 if lastparent and lastparent != r:
1560 if lastparent and lastparent != r:
1561 raise util.Abort(_('revision %d is not the parent of %d')
1561 raise util.Abort(_('revision %d is not the parent of %d')
1562 % (r, lastparent))
1562 % (r, lastparent))
1563 lastparent = p1
1563 lastparent = p1
1564
1564
1565 if not patchname:
1565 if not patchname:
1566 patchname = normname('%d.diff' % r)
1566 patchname = normname('%d.diff' % r)
1567 self.check_reserved_name(patchname)
1567 self.check_reserved_name(patchname)
1568 checkseries(patchname)
1568 checkseries(patchname)
1569 checkfile(patchname)
1569 checkfile(patchname)
1570 self.full_series.insert(0, patchname)
1570 self.full_series.insert(0, patchname)
1571
1571
1572 patchf = self.opener(patchname, "w")
1572 patchf = self.opener(patchname, "w")
1573 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1573 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1574 patchf.close()
1574 patchf.close()
1575
1575
1576 se = statusentry(hex(n), patchname)
1576 se = statusentry(hex(n), patchname)
1577 self.applied.insert(0, se)
1577 self.applied.insert(0, se)
1578
1578
1579 added.append(patchname)
1579 added.append(patchname)
1580 patchname = None
1580 patchname = None
1581 self.parse_series()
1581 self.parse_series()
1582 self.applied_dirty = 1
1582 self.applied_dirty = 1
1583
1583
1584 for filename in files:
1584 for filename in files:
1585 if existing:
1585 if existing:
1586 if filename == '-':
1586 if filename == '-':
1587 raise util.Abort(_('-e is incompatible with import from -'))
1587 raise util.Abort(_('-e is incompatible with import from -'))
1588 if not patchname:
1588 if not patchname:
1589 patchname = normname(filename)
1589 patchname = normname(filename)
1590 self.check_reserved_name(patchname)
1590 self.check_reserved_name(patchname)
1591 if not os.path.isfile(self.join(patchname)):
1591 if not os.path.isfile(self.join(patchname)):
1592 raise util.Abort(_("patch %s does not exist") % patchname)
1592 raise util.Abort(_("patch %s does not exist") % patchname)
1593 else:
1593 else:
1594 try:
1594 try:
1595 if filename == '-':
1595 if filename == '-':
1596 if not patchname:
1596 if not patchname:
1597 raise util.Abort(_('need --name to import a patch from -'))
1597 raise util.Abort(_('need --name to import a patch from -'))
1598 text = sys.stdin.read()
1598 text = sys.stdin.read()
1599 else:
1599 else:
1600 text = url.open(self.ui, filename).read()
1600 text = url.open(self.ui, filename).read()
1601 except (OSError, IOError):
1601 except (OSError, IOError):
1602 raise util.Abort(_("unable to read %s") % filename)
1602 raise util.Abort(_("unable to read %s") % filename)
1603 if not patchname:
1603 if not patchname:
1604 patchname = normname(os.path.basename(filename))
1604 patchname = normname(os.path.basename(filename))
1605 self.check_reserved_name(patchname)
1605 self.check_reserved_name(patchname)
1606 checkfile(patchname)
1606 checkfile(patchname)
1607 patchf = self.opener(patchname, "w")
1607 patchf = self.opener(patchname, "w")
1608 patchf.write(text)
1608 patchf.write(text)
1609 if not force:
1609 if not force:
1610 checkseries(patchname)
1610 checkseries(patchname)
1611 if patchname not in self.series:
1611 if patchname not in self.series:
1612 index = self.full_series_end() + i
1612 index = self.full_series_end() + i
1613 self.full_series[index:index] = [patchname]
1613 self.full_series[index:index] = [patchname]
1614 self.parse_series()
1614 self.parse_series()
1615 self.ui.warn(_("adding %s to series file\n") % patchname)
1615 self.ui.warn(_("adding %s to series file\n") % patchname)
1616 i += 1
1616 i += 1
1617 added.append(patchname)
1617 added.append(patchname)
1618 patchname = None
1618 patchname = None
1619 self.series_dirty = 1
1619 self.series_dirty = 1
1620 qrepo = self.qrepo()
1620 qrepo = self.qrepo()
1621 if qrepo:
1621 if qrepo:
1622 qrepo.add(added)
1622 qrepo.add(added)
1623
1623
1624 def delete(ui, repo, *patches, **opts):
1624 def delete(ui, repo, *patches, **opts):
1625 """remove patches from queue
1625 """remove patches from queue
1626
1626
1627 The patches must not be applied, unless they are arguments to the
1627 The patches must not be applied, unless they are arguments to the
1628 -r/--rev parameter. At least one patch or revision is required.
1628 -r/--rev parameter. At least one patch or revision is required.
1629
1629
1630 With --rev, mq will stop managing the named revisions (converting
1630 With --rev, mq will stop managing the named revisions (converting
1631 them to regular mercurial changesets). The qfinish command should
1631 them to regular mercurial changesets). The qfinish command should
1632 be used as an alternative for qdelete -r, as the latter option is
1632 be used as an alternative for qdelete -r, as the latter option is
1633 deprecated.
1633 deprecated.
1634
1634
1635 With -k/--keep, the patch files are preserved in the patch
1635 With -k/--keep, the patch files are preserved in the patch
1636 directory."""
1636 directory."""
1637 q = repo.mq
1637 q = repo.mq
1638 q.delete(repo, patches, opts)
1638 q.delete(repo, patches, opts)
1639 q.save_dirty()
1639 q.save_dirty()
1640 return 0
1640 return 0
1641
1641
1642 def applied(ui, repo, patch=None, **opts):
1642 def applied(ui, repo, patch=None, **opts):
1643 """print the patches already applied"""
1643 """print the patches already applied"""
1644 q = repo.mq
1644 q = repo.mq
1645 if patch:
1645 if patch:
1646 if patch not in q.series:
1646 if patch not in q.series:
1647 raise util.Abort(_("patch %s is not in series file") % patch)
1647 raise util.Abort(_("patch %s is not in series file") % patch)
1648 end = q.series.index(patch) + 1
1648 end = q.series.index(patch) + 1
1649 else:
1649 else:
1650 end = q.series_end(True)
1650 end = q.series_end(True)
1651 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1651 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1652
1652
1653 def unapplied(ui, repo, patch=None, **opts):
1653 def unapplied(ui, repo, patch=None, **opts):
1654 """print the patches not yet applied"""
1654 """print the patches not yet applied"""
1655 q = repo.mq
1655 q = repo.mq
1656 if patch:
1656 if patch:
1657 if patch not in q.series:
1657 if patch not in q.series:
1658 raise util.Abort(_("patch %s is not in series file") % patch)
1658 raise util.Abort(_("patch %s is not in series file") % patch)
1659 start = q.series.index(patch) + 1
1659 start = q.series.index(patch) + 1
1660 else:
1660 else:
1661 start = q.series_end(True)
1661 start = q.series_end(True)
1662 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1662 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1663
1663
1664 def qimport(ui, repo, *filename, **opts):
1664 def qimport(ui, repo, *filename, **opts):
1665 """import a patch
1665 """import a patch
1666
1666
1667 The patch is inserted into the series after the last applied
1667 The patch is inserted into the series after the last applied
1668 patch. If no patches have been applied, qimport prepends the patch
1668 patch. If no patches have been applied, qimport prepends the patch
1669 to the series.
1669 to the series.
1670
1670
1671 The patch will have the same name as its source file unless you
1671 The patch will have the same name as its source file unless you
1672 give it a new one with -n/--name.
1672 give it a new one with -n/--name.
1673
1673
1674 You can register an existing patch inside the patch directory with
1674 You can register an existing patch inside the patch directory with
1675 the -e/--existing flag.
1675 the -e/--existing flag.
1676
1676
1677 With -f/--force, an existing patch of the same name will be
1677 With -f/--force, an existing patch of the same name will be
1678 overwritten.
1678 overwritten.
1679
1679
1680 An existing changeset may be placed under mq control with -r/--rev
1680 An existing changeset may be placed under mq control with -r/--rev
1681 (e.g. qimport --rev tip -n patch will place tip under mq control).
1681 (e.g. qimport --rev tip -n patch will place tip under mq control).
1682 With -g/--git, patches imported with --rev will use the git diff
1682 With -g/--git, patches imported with --rev will use the git diff
1683 format. See the diffs help topic for information on why this is
1683 format. See the diffs help topic for information on why this is
1684 important for preserving rename/copy information and permission
1684 important for preserving rename/copy information and permission
1685 changes.
1685 changes.
1686
1686
1687 To import a patch from standard input, pass - as the patch file.
1687 To import a patch from standard input, pass - as the patch file.
1688 When importing from standard input, a patch name must be specified
1688 When importing from standard input, a patch name must be specified
1689 using the --name flag.
1689 using the --name flag.
1690 """
1690 """
1691 q = repo.mq
1691 q = repo.mq
1692 q.qimport(repo, filename, patchname=opts['name'],
1692 q.qimport(repo, filename, patchname=opts['name'],
1693 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1693 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1694 git=opts['git'])
1694 git=opts['git'])
1695 q.save_dirty()
1695 q.save_dirty()
1696
1696
1697 if opts.get('push') and not opts.get('rev'):
1697 if opts.get('push') and not opts.get('rev'):
1698 return q.push(repo, None)
1698 return q.push(repo, None)
1699 return 0
1699 return 0
1700
1700
1701 def init(ui, repo, **opts):
1701 def init(ui, repo, **opts):
1702 """init a new queue repository
1702 """init a new queue repository
1703
1703
1704 The queue repository is unversioned by default. If
1704 The queue repository is unversioned by default. If
1705 -c/--create-repo is specified, qinit will create a separate nested
1705 -c/--create-repo is specified, qinit will create a separate nested
1706 repository for patches (qinit -c may also be run later to convert
1706 repository for patches (qinit -c may also be run later to convert
1707 an unversioned patch repository into a versioned one). You can use
1707 an unversioned patch repository into a versioned one). You can use
1708 qcommit to commit changes to this queue repository."""
1708 qcommit to commit changes to this queue repository."""
1709 q = repo.mq
1709 q = repo.mq
1710 r = q.init(repo, create=opts['create_repo'])
1710 r = q.init(repo, create=opts['create_repo'])
1711 q.save_dirty()
1711 q.save_dirty()
1712 if r:
1712 if r:
1713 if not os.path.exists(r.wjoin('.hgignore')):
1713 if not os.path.exists(r.wjoin('.hgignore')):
1714 fp = r.wopener('.hgignore', 'w')
1714 fp = r.wopener('.hgignore', 'w')
1715 fp.write('^\\.hg\n')
1715 fp.write('^\\.hg\n')
1716 fp.write('^\\.mq\n')
1716 fp.write('^\\.mq\n')
1717 fp.write('syntax: glob\n')
1717 fp.write('syntax: glob\n')
1718 fp.write('status\n')
1718 fp.write('status\n')
1719 fp.write('guards\n')
1719 fp.write('guards\n')
1720 fp.close()
1720 fp.close()
1721 if not os.path.exists(r.wjoin('series')):
1721 if not os.path.exists(r.wjoin('series')):
1722 r.wopener('series', 'w').close()
1722 r.wopener('series', 'w').close()
1723 r.add(['.hgignore', 'series'])
1723 r.add(['.hgignore', 'series'])
1724 commands.add(ui, r)
1724 commands.add(ui, r)
1725 return 0
1725 return 0
1726
1726
1727 def clone(ui, source, dest=None, **opts):
1727 def clone(ui, source, dest=None, **opts):
1728 '''clone main and patch repository at same time
1728 '''clone main and patch repository at same time
1729
1729
1730 If source is local, destination will have no patches applied. If
1730 If source is local, destination will have no patches applied. If
1731 source is remote, this command can not check if patches are
1731 source is remote, this command can not check if patches are
1732 applied in source, so cannot guarantee that patches are not
1732 applied in source, so cannot guarantee that patches are not
1733 applied in destination. If you clone remote repository, be sure
1733 applied in destination. If you clone remote repository, be sure
1734 before that it has no patches applied.
1734 before that it has no patches applied.
1735
1735
1736 Source patch repository is looked for in <src>/.hg/patches by
1736 Source patch repository is looked for in <src>/.hg/patches by
1737 default. Use -p <url> to change.
1737 default. Use -p <url> to change.
1738
1738
1739 The patch directory must be a nested mercurial repository, as
1739 The patch directory must be a nested mercurial repository, as
1740 would be created by qinit -c.
1740 would be created by qinit -c.
1741 '''
1741 '''
1742 def patchdir(repo):
1742 def patchdir(repo):
1743 url = repo.url()
1743 url = repo.url()
1744 if url.endswith('/'):
1744 if url.endswith('/'):
1745 url = url[:-1]
1745 url = url[:-1]
1746 return url + '/.hg/patches'
1746 return url + '/.hg/patches'
1747 if dest is None:
1747 if dest is None:
1748 dest = hg.defaultdest(source)
1748 dest = hg.defaultdest(source)
1749 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1749 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1750 if opts['patches']:
1750 if opts['patches']:
1751 patchespath = ui.expandpath(opts['patches'])
1751 patchespath = ui.expandpath(opts['patches'])
1752 else:
1752 else:
1753 patchespath = patchdir(sr)
1753 patchespath = patchdir(sr)
1754 try:
1754 try:
1755 hg.repository(ui, patchespath)
1755 hg.repository(ui, patchespath)
1756 except error.RepoError:
1756 except error.RepoError:
1757 raise util.Abort(_('versioned patch repository not found'
1757 raise util.Abort(_('versioned patch repository not found'
1758 ' (see qinit -c)'))
1758 ' (see qinit -c)'))
1759 qbase, destrev = None, None
1759 qbase, destrev = None, None
1760 if sr.local():
1760 if sr.local():
1761 if sr.mq.applied:
1761 if sr.mq.applied:
1762 qbase = bin(sr.mq.applied[0].rev)
1762 qbase = bin(sr.mq.applied[0].rev)
1763 if not hg.islocal(dest):
1763 if not hg.islocal(dest):
1764 heads = set(sr.heads())
1764 heads = set(sr.heads())
1765 destrev = list(heads.difference(sr.heads(qbase)))
1765 destrev = list(heads.difference(sr.heads(qbase)))
1766 destrev.append(sr.changelog.parents(qbase)[0])
1766 destrev.append(sr.changelog.parents(qbase)[0])
1767 elif sr.capable('lookup'):
1767 elif sr.capable('lookup'):
1768 try:
1768 try:
1769 qbase = sr.lookup('qbase')
1769 qbase = sr.lookup('qbase')
1770 except error.RepoError:
1770 except error.RepoError:
1771 pass
1771 pass
1772 ui.note(_('cloning main repository\n'))
1772 ui.note(_('cloning main repository\n'))
1773 sr, dr = hg.clone(ui, sr.url(), dest,
1773 sr, dr = hg.clone(ui, sr.url(), dest,
1774 pull=opts['pull'],
1774 pull=opts['pull'],
1775 rev=destrev,
1775 rev=destrev,
1776 update=False,
1776 update=False,
1777 stream=opts['uncompressed'])
1777 stream=opts['uncompressed'])
1778 ui.note(_('cloning patch repository\n'))
1778 ui.note(_('cloning patch repository\n'))
1779 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1779 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1780 pull=opts['pull'], update=not opts['noupdate'],
1780 pull=opts['pull'], update=not opts['noupdate'],
1781 stream=opts['uncompressed'])
1781 stream=opts['uncompressed'])
1782 if dr.local():
1782 if dr.local():
1783 if qbase:
1783 if qbase:
1784 ui.note(_('stripping applied patches from destination '
1784 ui.note(_('stripping applied patches from destination '
1785 'repository\n'))
1785 'repository\n'))
1786 dr.mq.strip(dr, qbase, update=False, backup=None)
1786 dr.mq.strip(dr, qbase, update=False, backup=None)
1787 if not opts['noupdate']:
1787 if not opts['noupdate']:
1788 ui.note(_('updating destination repository\n'))
1788 ui.note(_('updating destination repository\n'))
1789 hg.update(dr, dr.changelog.tip())
1789 hg.update(dr, dr.changelog.tip())
1790
1790
1791 def commit(ui, repo, *pats, **opts):
1791 def commit(ui, repo, *pats, **opts):
1792 """commit changes in the queue repository"""
1792 """commit changes in the queue repository"""
1793 q = repo.mq
1793 q = repo.mq
1794 r = q.qrepo()
1794 r = q.qrepo()
1795 if not r: raise util.Abort('no queue repository')
1795 if not r: raise util.Abort('no queue repository')
1796 commands.commit(r.ui, r, *pats, **opts)
1796 commands.commit(r.ui, r, *pats, **opts)
1797
1797
1798 def series(ui, repo, **opts):
1798 def series(ui, repo, **opts):
1799 """print the entire series file"""
1799 """print the entire series file"""
1800 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1800 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1801 return 0
1801 return 0
1802
1802
1803 def top(ui, repo, **opts):
1803 def top(ui, repo, **opts):
1804 """print the name of the current patch"""
1804 """print the name of the current patch"""
1805 q = repo.mq
1805 q = repo.mq
1806 t = q.applied and q.series_end(True) or 0
1806 t = q.applied and q.series_end(True) or 0
1807 if t:
1807 if t:
1808 return q.qseries(repo, start=t-1, length=1, status='A',
1808 return q.qseries(repo, start=t-1, length=1, status='A',
1809 summary=opts.get('summary'))
1809 summary=opts.get('summary'))
1810 else:
1810 else:
1811 ui.write(_("no patches applied\n"))
1811 ui.write(_("no patches applied\n"))
1812 return 1
1812 return 1
1813
1813
1814 def next(ui, repo, **opts):
1814 def next(ui, repo, **opts):
1815 """print the name of the next patch"""
1815 """print the name of the next patch"""
1816 q = repo.mq
1816 q = repo.mq
1817 end = q.series_end()
1817 end = q.series_end()
1818 if end == len(q.series):
1818 if end == len(q.series):
1819 ui.write(_("all patches applied\n"))
1819 ui.write(_("all patches applied\n"))
1820 return 1
1820 return 1
1821 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1821 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1822
1822
1823 def prev(ui, repo, **opts):
1823 def prev(ui, repo, **opts):
1824 """print the name of the previous patch"""
1824 """print the name of the previous patch"""
1825 q = repo.mq
1825 q = repo.mq
1826 l = len(q.applied)
1826 l = len(q.applied)
1827 if l == 1:
1827 if l == 1:
1828 ui.write(_("only one patch applied\n"))
1828 ui.write(_("only one patch applied\n"))
1829 return 1
1829 return 1
1830 if not l:
1830 if not l:
1831 ui.write(_("no patches applied\n"))
1831 ui.write(_("no patches applied\n"))
1832 return 1
1832 return 1
1833 return q.qseries(repo, start=l-2, length=1, status='A',
1833 return q.qseries(repo, start=l-2, length=1, status='A',
1834 summary=opts.get('summary'))
1834 summary=opts.get('summary'))
1835
1835
1836 def setupheaderopts(ui, opts):
1836 def setupheaderopts(ui, opts):
1837 def do(opt,val):
1837 def do(opt,val):
1838 if not opts[opt] and opts['current' + opt]:
1838 if not opts[opt] and opts['current' + opt]:
1839 opts[opt] = val
1839 opts[opt] = val
1840 do('user', ui.username())
1840 do('user', ui.username())
1841 do('date', "%d %d" % util.makedate())
1841 do('date', "%d %d" % util.makedate())
1842
1842
1843 def new(ui, repo, patch, *args, **opts):
1843 def new(ui, repo, patch, *args, **opts):
1844 """create a new patch
1844 """create a new patch
1845
1845
1846 qnew creates a new patch on top of the currently-applied patch (if
1846 qnew creates a new patch on top of the currently-applied patch (if
1847 any). It will refuse to run if there are any outstanding changes
1847 any). It will refuse to run if there are any outstanding changes
1848 unless -f/--force is specified, in which case the patch will be
1848 unless -f/--force is specified, in which case the patch will be
1849 initialized with them. You may also use -I/--include,
1849 initialized with them. You may also use -I/--include,
1850 -X/--exclude, and/or a list of files after the patch name to add
1850 -X/--exclude, and/or a list of files after the patch name to add
1851 only changes to matching files to the new patch, leaving the rest
1851 only changes to matching files to the new patch, leaving the rest
1852 as uncommitted modifications.
1852 as uncommitted modifications.
1853
1853
1854 -u/--user and -d/--date can be used to set the (given) user and
1854 -u/--user and -d/--date can be used to set the (given) user and
1855 date, respectively. -U/--currentuser and -D/--currentdate set user
1855 date, respectively. -U/--currentuser and -D/--currentdate set user
1856 to current user and date to current date.
1856 to current user and date to current date.
1857
1857
1858 -e/--edit, -m/--message or -l/--logfile set the patch header as
1858 -e/--edit, -m/--message or -l/--logfile set the patch header as
1859 well as the commit message. If none is specified, the header is
1859 well as the commit message. If none is specified, the header is
1860 empty and the commit message is '[mq]: PATCH'.
1860 empty and the commit message is '[mq]: PATCH'.
1861
1861
1862 Use the -g/--git option to keep the patch in the git extended diff
1862 Use the -g/--git option to keep the patch in the git extended diff
1863 format. Read the diffs help topic for more information on why this
1863 format. Read the diffs help topic for more information on why this
1864 is important for preserving permission changes and copy/rename
1864 is important for preserving permission changes and copy/rename
1865 information.
1865 information.
1866 """
1866 """
1867 msg = cmdutil.logmessage(opts)
1867 msg = cmdutil.logmessage(opts)
1868 def getmsg(): return ui.edit(msg, ui.username())
1868 def getmsg(): return ui.edit(msg, ui.username())
1869 q = repo.mq
1869 q = repo.mq
1870 opts['msg'] = msg
1870 opts['msg'] = msg
1871 if opts.get('edit'):
1871 if opts.get('edit'):
1872 opts['msg'] = getmsg
1872 opts['msg'] = getmsg
1873 else:
1873 else:
1874 opts['msg'] = msg
1874 opts['msg'] = msg
1875 setupheaderopts(ui, opts)
1875 setupheaderopts(ui, opts)
1876 q.new(repo, patch, *args, **opts)
1876 q.new(repo, patch, *args, **opts)
1877 q.save_dirty()
1877 q.save_dirty()
1878 return 0
1878 return 0
1879
1879
1880 def refresh(ui, repo, *pats, **opts):
1880 def refresh(ui, repo, *pats, **opts):
1881 """update the current patch
1881 """update the current patch
1882
1882
1883 If any file patterns are provided, the refreshed patch will
1883 If any file patterns are provided, the refreshed patch will
1884 contain only the modifications that match those patterns; the
1884 contain only the modifications that match those patterns; the
1885 remaining modifications will remain in the working directory.
1885 remaining modifications will remain in the working directory.
1886
1886
1887 If -s/--short is specified, files currently included in the patch
1887 If -s/--short is specified, files currently included in the patch
1888 will be refreshed just like matched files and remain in the patch.
1888 will be refreshed just like matched files and remain in the patch.
1889
1889
1890 hg add/remove/copy/rename work as usual, though you might want to
1890 hg add/remove/copy/rename work as usual, though you might want to
1891 use git-style patches (-g/--git or [diff] git=1) to track copies
1891 use git-style patches (-g/--git or [diff] git=1) to track copies
1892 and renames. See the diffs help topic for more information on the
1892 and renames. See the diffs help topic for more information on the
1893 git diff format.
1893 git diff format.
1894 """
1894 """
1895 q = repo.mq
1895 q = repo.mq
1896 message = cmdutil.logmessage(opts)
1896 message = cmdutil.logmessage(opts)
1897 if opts['edit']:
1897 if opts['edit']:
1898 if not q.applied:
1898 if not q.applied:
1899 ui.write(_("no patches applied\n"))
1899 ui.write(_("no patches applied\n"))
1900 return 1
1900 return 1
1901 if message:
1901 if message:
1902 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1902 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1903 patch = q.applied[-1].name
1903 patch = q.applied[-1].name
1904 ph = q.readheaders(patch)
1904 ph = q.readheaders(patch)
1905 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
1905 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
1906 setupheaderopts(ui, opts)
1906 setupheaderopts(ui, opts)
1907 ret = q.refresh(repo, pats, msg=message, **opts)
1907 ret = q.refresh(repo, pats, msg=message, **opts)
1908 q.save_dirty()
1908 q.save_dirty()
1909 return ret
1909 return ret
1910
1910
1911 def diff(ui, repo, *pats, **opts):
1911 def diff(ui, repo, *pats, **opts):
1912 """diff of the current patch and subsequent modifications
1912 """diff of the current patch and subsequent modifications
1913
1913
1914 Shows a diff which includes the current patch as well as any
1914 Shows a diff which includes the current patch as well as any
1915 changes which have been made in the working directory since the
1915 changes which have been made in the working directory since the
1916 last refresh (thus showing what the current patch would become
1916 last refresh (thus showing what the current patch would become
1917 after a qrefresh).
1917 after a qrefresh).
1918
1918
1919 Use 'hg diff' if you only want to see the changes made since the
1919 Use 'hg diff' if you only want to see the changes made since the
1920 last qrefresh, or 'hg export qtip' if you want to see changes made
1920 last qrefresh, or 'hg export qtip' if you want to see changes made
1921 by the current patch without including changes made since the
1921 by the current patch without including changes made since the
1922 qrefresh.
1922 qrefresh.
1923 """
1923 """
1924 repo.mq.diff(repo, pats, opts)
1924 repo.mq.diff(repo, pats, opts)
1925 return 0
1925 return 0
1926
1926
1927 def fold(ui, repo, *files, **opts):
1927 def fold(ui, repo, *files, **opts):
1928 """fold the named patches into the current patch
1928 """fold the named patches into the current patch
1929
1929
1930 Patches must not yet be applied. Each patch will be successively
1930 Patches must not yet be applied. Each patch will be successively
1931 applied to the current patch in the order given. If all the
1931 applied to the current patch in the order given. If all the
1932 patches apply successfully, the current patch will be refreshed
1932 patches apply successfully, the current patch will be refreshed
1933 with the new cumulative patch, and the folded patches will be
1933 with the new cumulative patch, and the folded patches will be
1934 deleted. With -k/--keep, the folded patch files will not be
1934 deleted. With -k/--keep, the folded patch files will not be
1935 removed afterwards.
1935 removed afterwards.
1936
1936
1937 The header for each folded patch will be concatenated with the
1937 The header for each folded patch will be concatenated with the
1938 current patch header, separated by a line of '* * *'."""
1938 current patch header, separated by a line of '* * *'."""
1939
1939
1940 q = repo.mq
1940 q = repo.mq
1941
1941
1942 if not files:
1942 if not files:
1943 raise util.Abort(_('qfold requires at least one patch name'))
1943 raise util.Abort(_('qfold requires at least one patch name'))
1944 if not q.check_toppatch(repo):
1944 if not q.check_toppatch(repo):
1945 raise util.Abort(_('No patches applied'))
1945 raise util.Abort(_('No patches applied'))
1946
1946
1947 message = cmdutil.logmessage(opts)
1947 message = cmdutil.logmessage(opts)
1948 if opts['edit']:
1948 if opts['edit']:
1949 if message:
1949 if message:
1950 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1950 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1951
1951
1952 parent = q.lookup('qtip')
1952 parent = q.lookup('qtip')
1953 patches = []
1953 patches = []
1954 messages = []
1954 messages = []
1955 for f in files:
1955 for f in files:
1956 p = q.lookup(f)
1956 p = q.lookup(f)
1957 if p in patches or p == parent:
1957 if p in patches or p == parent:
1958 ui.warn(_('Skipping already folded patch %s') % p)
1958 ui.warn(_('Skipping already folded patch %s') % p)
1959 if q.isapplied(p):
1959 if q.isapplied(p):
1960 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1960 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1961 patches.append(p)
1961 patches.append(p)
1962
1962
1963 for p in patches:
1963 for p in patches:
1964 if not message:
1964 if not message:
1965 ph = q.readheaders(p)
1965 ph = q.readheaders(p)
1966 if ph.message:
1966 if ph.message:
1967 messages.append(ph.message)
1967 messages.append(ph.message)
1968 pf = q.join(p)
1968 pf = q.join(p)
1969 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1969 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1970 if not patchsuccess:
1970 if not patchsuccess:
1971 raise util.Abort(_('Error folding patch %s') % p)
1971 raise util.Abort(_('Error folding patch %s') % p)
1972 patch.updatedir(ui, repo, files)
1972 patch.updatedir(ui, repo, files)
1973
1973
1974 if not message:
1974 if not message:
1975 ph = q.readheaders(parent)
1975 ph = q.readheaders(parent)
1976 message, user = ph.message, ph.user
1976 message, user = ph.message, ph.user
1977 for msg in messages:
1977 for msg in messages:
1978 message.append('* * *')
1978 message.append('* * *')
1979 message.extend(msg)
1979 message.extend(msg)
1980 message = '\n'.join(message)
1980 message = '\n'.join(message)
1981
1981
1982 if opts['edit']:
1982 if opts['edit']:
1983 message = ui.edit(message, user or ui.username())
1983 message = ui.edit(message, user or ui.username())
1984
1984
1985 q.refresh(repo, msg=message)
1985 q.refresh(repo, msg=message)
1986 q.delete(repo, patches, opts)
1986 q.delete(repo, patches, opts)
1987 q.save_dirty()
1987 q.save_dirty()
1988
1988
1989 def goto(ui, repo, patch, **opts):
1989 def goto(ui, repo, patch, **opts):
1990 '''push or pop patches until named patch is at top of stack'''
1990 '''push or pop patches until named patch is at top of stack'''
1991 q = repo.mq
1991 q = repo.mq
1992 patch = q.lookup(patch)
1992 patch = q.lookup(patch)
1993 if q.isapplied(patch):
1993 if q.isapplied(patch):
1994 ret = q.pop(repo, patch, force=opts['force'])
1994 ret = q.pop(repo, patch, force=opts['force'])
1995 else:
1995 else:
1996 ret = q.push(repo, patch, force=opts['force'])
1996 ret = q.push(repo, patch, force=opts['force'])
1997 q.save_dirty()
1997 q.save_dirty()
1998 return ret
1998 return ret
1999
1999
2000 def guard(ui, repo, *args, **opts):
2000 def guard(ui, repo, *args, **opts):
2001 '''set or print guards for a patch
2001 '''set or print guards for a patch
2002
2002
2003 Guards control whether a patch can be pushed. A patch with no
2003 Guards control whether a patch can be pushed. A patch with no
2004 guards is always pushed. A patch with a positive guard ("+foo") is
2004 guards is always pushed. A patch with a positive guard ("+foo") is
2005 pushed only if the qselect command has activated it. A patch with
2005 pushed only if the qselect command has activated it. A patch with
2006 a negative guard ("-foo") is never pushed if the qselect command
2006 a negative guard ("-foo") is never pushed if the qselect command
2007 has activated it.
2007 has activated it.
2008
2008
2009 With no arguments, print the currently active guards.
2009 With no arguments, print the currently active guards.
2010 With arguments, set guards for the named patch.
2010 With arguments, set guards for the named patch.
2011 NOTE: Specifying negative guards now requires '--'.
2011 NOTE: Specifying negative guards now requires '--'.
2012
2012
2013 To set guards on another patch:
2013 To set guards on another patch:
2014 hg qguard -- other.patch +2.6.17 -stable
2014 hg qguard -- other.patch +2.6.17 -stable
2015 '''
2015 '''
2016 def status(idx):
2016 def status(idx):
2017 guards = q.series_guards[idx] or ['unguarded']
2017 guards = q.series_guards[idx] or ['unguarded']
2018 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2018 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2019 q = repo.mq
2019 q = repo.mq
2020 patch = None
2020 patch = None
2021 args = list(args)
2021 args = list(args)
2022 if opts['list']:
2022 if opts['list']:
2023 if args or opts['none']:
2023 if args or opts['none']:
2024 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2024 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2025 for i in xrange(len(q.series)):
2025 for i in xrange(len(q.series)):
2026 status(i)
2026 status(i)
2027 return
2027 return
2028 if not args or args[0][0:1] in '-+':
2028 if not args or args[0][0:1] in '-+':
2029 if not q.applied:
2029 if not q.applied:
2030 raise util.Abort(_('no patches applied'))
2030 raise util.Abort(_('no patches applied'))
2031 patch = q.applied[-1].name
2031 patch = q.applied[-1].name
2032 if patch is None and args[0][0:1] not in '-+':
2032 if patch is None and args[0][0:1] not in '-+':
2033 patch = args.pop(0)
2033 patch = args.pop(0)
2034 if patch is None:
2034 if patch is None:
2035 raise util.Abort(_('no patch to work with'))
2035 raise util.Abort(_('no patch to work with'))
2036 if args or opts['none']:
2036 if args or opts['none']:
2037 idx = q.find_series(patch)
2037 idx = q.find_series(patch)
2038 if idx is None:
2038 if idx is None:
2039 raise util.Abort(_('no patch named %s') % patch)
2039 raise util.Abort(_('no patch named %s') % patch)
2040 q.set_guards(idx, args)
2040 q.set_guards(idx, args)
2041 q.save_dirty()
2041 q.save_dirty()
2042 else:
2042 else:
2043 status(q.series.index(q.lookup(patch)))
2043 status(q.series.index(q.lookup(patch)))
2044
2044
2045 def header(ui, repo, patch=None):
2045 def header(ui, repo, patch=None):
2046 """print the header of the topmost or specified patch"""
2046 """print the header of the topmost or specified patch"""
2047 q = repo.mq
2047 q = repo.mq
2048
2048
2049 if patch:
2049 if patch:
2050 patch = q.lookup(patch)
2050 patch = q.lookup(patch)
2051 else:
2051 else:
2052 if not q.applied:
2052 if not q.applied:
2053 ui.write('no patches applied\n')
2053 ui.write('no patches applied\n')
2054 return 1
2054 return 1
2055 patch = q.lookup('qtip')
2055 patch = q.lookup('qtip')
2056 ph = repo.mq.readheaders(patch)
2056 ph = repo.mq.readheaders(patch)
2057
2057
2058 ui.write('\n'.join(ph.message) + '\n')
2058 ui.write('\n'.join(ph.message) + '\n')
2059
2059
2060 def lastsavename(path):
2060 def lastsavename(path):
2061 (directory, base) = os.path.split(path)
2061 (directory, base) = os.path.split(path)
2062 names = os.listdir(directory)
2062 names = os.listdir(directory)
2063 namere = re.compile("%s.([0-9]+)" % base)
2063 namere = re.compile("%s.([0-9]+)" % base)
2064 maxindex = None
2064 maxindex = None
2065 maxname = None
2065 maxname = None
2066 for f in names:
2066 for f in names:
2067 m = namere.match(f)
2067 m = namere.match(f)
2068 if m:
2068 if m:
2069 index = int(m.group(1))
2069 index = int(m.group(1))
2070 if maxindex == None or index > maxindex:
2070 if maxindex == None or index > maxindex:
2071 maxindex = index
2071 maxindex = index
2072 maxname = f
2072 maxname = f
2073 if maxname:
2073 if maxname:
2074 return (os.path.join(directory, maxname), maxindex)
2074 return (os.path.join(directory, maxname), maxindex)
2075 return (None, None)
2075 return (None, None)
2076
2076
2077 def savename(path):
2077 def savename(path):
2078 (last, index) = lastsavename(path)
2078 (last, index) = lastsavename(path)
2079 if last is None:
2079 if last is None:
2080 index = 0
2080 index = 0
2081 newpath = path + ".%d" % (index + 1)
2081 newpath = path + ".%d" % (index + 1)
2082 return newpath
2082 return newpath
2083
2083
2084 def push(ui, repo, patch=None, **opts):
2084 def push(ui, repo, patch=None, **opts):
2085 """push the next patch onto the stack
2085 """push the next patch onto the stack
2086
2086
2087 When -f/--force is applied, all local changes in patched files
2087 When -f/--force is applied, all local changes in patched files
2088 will be lost.
2088 will be lost.
2089 """
2089 """
2090 q = repo.mq
2090 q = repo.mq
2091 mergeq = None
2091 mergeq = None
2092
2092
2093 if opts['merge']:
2093 if opts['merge']:
2094 if opts['name']:
2094 if opts['name']:
2095 newpath = repo.join(opts['name'])
2095 newpath = repo.join(opts['name'])
2096 else:
2096 else:
2097 newpath, i = lastsavename(q.path)
2097 newpath, i = lastsavename(q.path)
2098 if not newpath:
2098 if not newpath:
2099 ui.warn(_("no saved queues found, please use -n\n"))
2099 ui.warn(_("no saved queues found, please use -n\n"))
2100 return 1
2100 return 1
2101 mergeq = queue(ui, repo.join(""), newpath)
2101 mergeq = queue(ui, repo.join(""), newpath)
2102 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2102 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2103 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2103 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2104 mergeq=mergeq, all=opts.get('all'))
2104 mergeq=mergeq, all=opts.get('all'))
2105 return ret
2105 return ret
2106
2106
2107 def pop(ui, repo, patch=None, **opts):
2107 def pop(ui, repo, patch=None, **opts):
2108 """pop the current patch off the stack
2108 """pop the current patch off the stack
2109
2109
2110 By default, pops off the top of the patch stack. If given a patch
2110 By default, pops off the top of the patch stack. If given a patch
2111 name, keeps popping off patches until the named patch is at the
2111 name, keeps popping off patches until the named patch is at the
2112 top of the stack.
2112 top of the stack.
2113 """
2113 """
2114 localupdate = True
2114 localupdate = True
2115 if opts['name']:
2115 if opts['name']:
2116 q = queue(ui, repo.join(""), repo.join(opts['name']))
2116 q = queue(ui, repo.join(""), repo.join(opts['name']))
2117 ui.warn(_('using patch queue: %s\n') % q.path)
2117 ui.warn(_('using patch queue: %s\n') % q.path)
2118 localupdate = False
2118 localupdate = False
2119 else:
2119 else:
2120 q = repo.mq
2120 q = repo.mq
2121 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2121 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2122 all=opts['all'])
2122 all=opts['all'])
2123 q.save_dirty()
2123 q.save_dirty()
2124 return ret
2124 return ret
2125
2125
2126 def rename(ui, repo, patch, name=None, **opts):
2126 def rename(ui, repo, patch, name=None, **opts):
2127 """rename a patch
2127 """rename a patch
2128
2128
2129 With one argument, renames the current patch to PATCH1.
2129 With one argument, renames the current patch to PATCH1.
2130 With two arguments, renames PATCH1 to PATCH2."""
2130 With two arguments, renames PATCH1 to PATCH2."""
2131
2131
2132 q = repo.mq
2132 q = repo.mq
2133
2133
2134 if not name:
2134 if not name:
2135 name = patch
2135 name = patch
2136 patch = None
2136 patch = None
2137
2137
2138 if patch:
2138 if patch:
2139 patch = q.lookup(patch)
2139 patch = q.lookup(patch)
2140 else:
2140 else:
2141 if not q.applied:
2141 if not q.applied:
2142 ui.write(_('no patches applied\n'))
2142 ui.write(_('no patches applied\n'))
2143 return
2143 return
2144 patch = q.lookup('qtip')
2144 patch = q.lookup('qtip')
2145 absdest = q.join(name)
2145 absdest = q.join(name)
2146 if os.path.isdir(absdest):
2146 if os.path.isdir(absdest):
2147 name = normname(os.path.join(name, os.path.basename(patch)))
2147 name = normname(os.path.join(name, os.path.basename(patch)))
2148 absdest = q.join(name)
2148 absdest = q.join(name)
2149 if os.path.exists(absdest):
2149 if os.path.exists(absdest):
2150 raise util.Abort(_('%s already exists') % absdest)
2150 raise util.Abort(_('%s already exists') % absdest)
2151
2151
2152 if name in q.series:
2152 if name in q.series:
2153 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2153 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2154
2154
2155 if ui.verbose:
2155 if ui.verbose:
2156 ui.write('renaming %s to %s\n' % (patch, name))
2156 ui.write('renaming %s to %s\n' % (patch, name))
2157 i = q.find_series(patch)
2157 i = q.find_series(patch)
2158 guards = q.guard_re.findall(q.full_series[i])
2158 guards = q.guard_re.findall(q.full_series[i])
2159 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2159 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2160 q.parse_series()
2160 q.parse_series()
2161 q.series_dirty = 1
2161 q.series_dirty = 1
2162
2162
2163 info = q.isapplied(patch)
2163 info = q.isapplied(patch)
2164 if info:
2164 if info:
2165 q.applied[info[0]] = statusentry(info[1], name)
2165 q.applied[info[0]] = statusentry(info[1], name)
2166 q.applied_dirty = 1
2166 q.applied_dirty = 1
2167
2167
2168 util.rename(q.join(patch), absdest)
2168 util.rename(q.join(patch), absdest)
2169 r = q.qrepo()
2169 r = q.qrepo()
2170 if r:
2170 if r:
2171 wlock = r.wlock()
2171 wlock = r.wlock()
2172 try:
2172 try:
2173 if r.dirstate[patch] == 'a':
2173 if r.dirstate[patch] == 'a':
2174 r.dirstate.forget(patch)
2174 r.dirstate.forget(patch)
2175 r.dirstate.add(name)
2175 r.dirstate.add(name)
2176 else:
2176 else:
2177 if r.dirstate[name] == 'r':
2177 if r.dirstate[name] == 'r':
2178 r.undelete([name])
2178 r.undelete([name])
2179 r.copy(patch, name)
2179 r.copy(patch, name)
2180 r.remove([patch], False)
2180 r.remove([patch], False)
2181 finally:
2181 finally:
2182 wlock.release()
2182 wlock.release()
2183
2183
2184 q.save_dirty()
2184 q.save_dirty()
2185
2185
2186 def restore(ui, repo, rev, **opts):
2186 def restore(ui, repo, rev, **opts):
2187 """restore the queue state saved by a revision"""
2187 """restore the queue state saved by a revision"""
2188 rev = repo.lookup(rev)
2188 rev = repo.lookup(rev)
2189 q = repo.mq
2189 q = repo.mq
2190 q.restore(repo, rev, delete=opts['delete'],
2190 q.restore(repo, rev, delete=opts['delete'],
2191 qupdate=opts['update'])
2191 qupdate=opts['update'])
2192 q.save_dirty()
2192 q.save_dirty()
2193 return 0
2193 return 0
2194
2194
2195 def save(ui, repo, **opts):
2195 def save(ui, repo, **opts):
2196 """save current queue state"""
2196 """save current queue state"""
2197 q = repo.mq
2197 q = repo.mq
2198 message = cmdutil.logmessage(opts)
2198 message = cmdutil.logmessage(opts)
2199 ret = q.save(repo, msg=message)
2199 ret = q.save(repo, msg=message)
2200 if ret:
2200 if ret:
2201 return ret
2201 return ret
2202 q.save_dirty()
2202 q.save_dirty()
2203 if opts['copy']:
2203 if opts['copy']:
2204 path = q.path
2204 path = q.path
2205 if opts['name']:
2205 if opts['name']:
2206 newpath = os.path.join(q.basepath, opts['name'])
2206 newpath = os.path.join(q.basepath, opts['name'])
2207 if os.path.exists(newpath):
2207 if os.path.exists(newpath):
2208 if not os.path.isdir(newpath):
2208 if not os.path.isdir(newpath):
2209 raise util.Abort(_('destination %s exists and is not '
2209 raise util.Abort(_('destination %s exists and is not '
2210 'a directory') % newpath)
2210 'a directory') % newpath)
2211 if not opts['force']:
2211 if not opts['force']:
2212 raise util.Abort(_('destination %s exists, '
2212 raise util.Abort(_('destination %s exists, '
2213 'use -f to force') % newpath)
2213 'use -f to force') % newpath)
2214 else:
2214 else:
2215 newpath = savename(path)
2215 newpath = savename(path)
2216 ui.warn(_("copy %s to %s\n") % (path, newpath))
2216 ui.warn(_("copy %s to %s\n") % (path, newpath))
2217 util.copyfiles(path, newpath)
2217 util.copyfiles(path, newpath)
2218 if opts['empty']:
2218 if opts['empty']:
2219 try:
2219 try:
2220 os.unlink(q.join(q.status_path))
2220 os.unlink(q.join(q.status_path))
2221 except:
2221 except:
2222 pass
2222 pass
2223 return 0
2223 return 0
2224
2224
2225 def strip(ui, repo, rev, **opts):
2225 def strip(ui, repo, rev, **opts):
2226 """strip a revision and all its descendants from the repository
2226 """strip a revision and all its descendants from the repository
2227
2227
2228 If one of the working directory's parent revisions is stripped, the
2228 If one of the working directory's parent revisions is stripped, the
2229 working directory will be updated to the parent of the stripped
2229 working directory will be updated to the parent of the stripped
2230 revision.
2230 revision.
2231 """
2231 """
2232 backup = 'all'
2232 backup = 'all'
2233 if opts['backup']:
2233 if opts['backup']:
2234 backup = 'strip'
2234 backup = 'strip'
2235 elif opts['nobackup']:
2235 elif opts['nobackup']:
2236 backup = 'none'
2236 backup = 'none'
2237
2237
2238 rev = repo.lookup(rev)
2238 rev = repo.lookup(rev)
2239 p = repo.dirstate.parents()
2239 p = repo.dirstate.parents()
2240 cl = repo.changelog
2240 cl = repo.changelog
2241 update = True
2241 update = True
2242 if p[0] == nullid:
2242 if p[0] == nullid:
2243 update = False
2243 update = False
2244 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2244 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2245 update = False
2245 update = False
2246 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2246 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2247 update = False
2247 update = False
2248
2248
2249 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2249 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2250 return 0
2250 return 0
2251
2251
2252 def select(ui, repo, *args, **opts):
2252 def select(ui, repo, *args, **opts):
2253 '''set or print guarded patches to push
2253 '''set or print guarded patches to push
2254
2254
2255 Use the qguard command to set or print guards on patch, then use
2255 Use the qguard command to set or print guards on patch, then use
2256 qselect to tell mq which guards to use. A patch will be pushed if
2256 qselect to tell mq which guards to use. A patch will be pushed if
2257 it has no guards or any positive guards match the currently
2257 it has no guards or any positive guards match the currently
2258 selected guard, but will not be pushed if any negative guards
2258 selected guard, but will not be pushed if any negative guards
2259 match the current guard. For example:
2259 match the current guard. For example:
2260
2260
2261 qguard foo.patch -stable (negative guard)
2261 qguard foo.patch -stable (negative guard)
2262 qguard bar.patch +stable (positive guard)
2262 qguard bar.patch +stable (positive guard)
2263 qselect stable
2263 qselect stable
2264
2264
2265 This activates the "stable" guard. mq will skip foo.patch (because
2265 This activates the "stable" guard. mq will skip foo.patch (because
2266 it has a negative match) but push bar.patch (because it has a
2266 it has a negative match) but push bar.patch (because it has a
2267 positive match).
2267 positive match).
2268
2268
2269 With no arguments, prints the currently active guards.
2269 With no arguments, prints the currently active guards.
2270 With one argument, sets the active guard.
2270 With one argument, sets the active guard.
2271
2271
2272 Use -n/--none to deactivate guards (no other arguments needed).
2272 Use -n/--none to deactivate guards (no other arguments needed).
2273 When no guards are active, patches with positive guards are
2273 When no guards are active, patches with positive guards are
2274 skipped and patches with negative guards are pushed.
2274 skipped and patches with negative guards are pushed.
2275
2275
2276 qselect can change the guards on applied patches. It does not pop
2276 qselect can change the guards on applied patches. It does not pop
2277 guarded patches by default. Use --pop to pop back to the last
2277 guarded patches by default. Use --pop to pop back to the last
2278 applied patch that is not guarded. Use --reapply (which implies
2278 applied patch that is not guarded. Use --reapply (which implies
2279 --pop) to push back to the current patch afterwards, but skip
2279 --pop) to push back to the current patch afterwards, but skip
2280 guarded patches.
2280 guarded patches.
2281
2281
2282 Use -s/--series to print a list of all guards in the series file
2282 Use -s/--series to print a list of all guards in the series file
2283 (no other arguments needed). Use -v for more information.'''
2283 (no other arguments needed). Use -v for more information.'''
2284
2284
2285 q = repo.mq
2285 q = repo.mq
2286 guards = q.active()
2286 guards = q.active()
2287 if args or opts['none']:
2287 if args or opts['none']:
2288 old_unapplied = q.unapplied(repo)
2288 old_unapplied = q.unapplied(repo)
2289 old_guarded = [i for i in xrange(len(q.applied)) if
2289 old_guarded = [i for i in xrange(len(q.applied)) if
2290 not q.pushable(i)[0]]
2290 not q.pushable(i)[0]]
2291 q.set_active(args)
2291 q.set_active(args)
2292 q.save_dirty()
2292 q.save_dirty()
2293 if not args:
2293 if not args:
2294 ui.status(_('guards deactivated\n'))
2294 ui.status(_('guards deactivated\n'))
2295 if not opts['pop'] and not opts['reapply']:
2295 if not opts['pop'] and not opts['reapply']:
2296 unapplied = q.unapplied(repo)
2296 unapplied = q.unapplied(repo)
2297 guarded = [i for i in xrange(len(q.applied))
2297 guarded = [i for i in xrange(len(q.applied))
2298 if not q.pushable(i)[0]]
2298 if not q.pushable(i)[0]]
2299 if len(unapplied) != len(old_unapplied):
2299 if len(unapplied) != len(old_unapplied):
2300 ui.status(_('number of unguarded, unapplied patches has '
2300 ui.status(_('number of unguarded, unapplied patches has '
2301 'changed from %d to %d\n') %
2301 'changed from %d to %d\n') %
2302 (len(old_unapplied), len(unapplied)))
2302 (len(old_unapplied), len(unapplied)))
2303 if len(guarded) != len(old_guarded):
2303 if len(guarded) != len(old_guarded):
2304 ui.status(_('number of guarded, applied patches has changed '
2304 ui.status(_('number of guarded, applied patches has changed '
2305 'from %d to %d\n') %
2305 'from %d to %d\n') %
2306 (len(old_guarded), len(guarded)))
2306 (len(old_guarded), len(guarded)))
2307 elif opts['series']:
2307 elif opts['series']:
2308 guards = {}
2308 guards = {}
2309 noguards = 0
2309 noguards = 0
2310 for gs in q.series_guards:
2310 for gs in q.series_guards:
2311 if not gs:
2311 if not gs:
2312 noguards += 1
2312 noguards += 1
2313 for g in gs:
2313 for g in gs:
2314 guards.setdefault(g, 0)
2314 guards.setdefault(g, 0)
2315 guards[g] += 1
2315 guards[g] += 1
2316 if ui.verbose:
2316 if ui.verbose:
2317 guards['NONE'] = noguards
2317 guards['NONE'] = noguards
2318 guards = guards.items()
2318 guards = guards.items()
2319 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2319 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2320 if guards:
2320 if guards:
2321 ui.note(_('guards in series file:\n'))
2321 ui.note(_('guards in series file:\n'))
2322 for guard, count in guards:
2322 for guard, count in guards:
2323 ui.note('%2d ' % count)
2323 ui.note('%2d ' % count)
2324 ui.write(guard, '\n')
2324 ui.write(guard, '\n')
2325 else:
2325 else:
2326 ui.note(_('no guards in series file\n'))
2326 ui.note(_('no guards in series file\n'))
2327 else:
2327 else:
2328 if guards:
2328 if guards:
2329 ui.note(_('active guards:\n'))
2329 ui.note(_('active guards:\n'))
2330 for g in guards:
2330 for g in guards:
2331 ui.write(g, '\n')
2331 ui.write(g, '\n')
2332 else:
2332 else:
2333 ui.write(_('no active guards\n'))
2333 ui.write(_('no active guards\n'))
2334 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2334 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2335 popped = False
2335 popped = False
2336 if opts['pop'] or opts['reapply']:
2336 if opts['pop'] or opts['reapply']:
2337 for i in xrange(len(q.applied)):
2337 for i in xrange(len(q.applied)):
2338 pushable, reason = q.pushable(i)
2338 pushable, reason = q.pushable(i)
2339 if not pushable:
2339 if not pushable:
2340 ui.status(_('popping guarded patches\n'))
2340 ui.status(_('popping guarded patches\n'))
2341 popped = True
2341 popped = True
2342 if i == 0:
2342 if i == 0:
2343 q.pop(repo, all=True)
2343 q.pop(repo, all=True)
2344 else:
2344 else:
2345 q.pop(repo, i-1)
2345 q.pop(repo, i-1)
2346 break
2346 break
2347 if popped:
2347 if popped:
2348 try:
2348 try:
2349 if reapply:
2349 if reapply:
2350 ui.status(_('reapplying unguarded patches\n'))
2350 ui.status(_('reapplying unguarded patches\n'))
2351 q.push(repo, reapply)
2351 q.push(repo, reapply)
2352 finally:
2352 finally:
2353 q.save_dirty()
2353 q.save_dirty()
2354
2354
2355 def finish(ui, repo, *revrange, **opts):
2355 def finish(ui, repo, *revrange, **opts):
2356 """move applied patches into repository history
2356 """move applied patches into repository history
2357
2357
2358 Finishes the specified revisions (corresponding to applied
2358 Finishes the specified revisions (corresponding to applied
2359 patches) by moving them out of mq control into regular repository
2359 patches) by moving them out of mq control into regular repository
2360 history.
2360 history.
2361
2361
2362 Accepts a revision range or the -a/--applied option. If --applied
2362 Accepts a revision range or the -a/--applied option. If --applied
2363 is specified, all applied mq revisions are removed from mq
2363 is specified, all applied mq revisions are removed from mq
2364 control. Otherwise, the given revisions must be at the base of the
2364 control. Otherwise, the given revisions must be at the base of the
2365 stack of applied patches.
2365 stack of applied patches.
2366
2366
2367 This can be especially useful if your changes have been applied to
2367 This can be especially useful if your changes have been applied to
2368 an upstream repository, or if you are about to push your changes
2368 an upstream repository, or if you are about to push your changes
2369 to upstream.
2369 to upstream.
2370 """
2370 """
2371 if not opts['applied'] and not revrange:
2371 if not opts['applied'] and not revrange:
2372 raise util.Abort(_('no revisions specified'))
2372 raise util.Abort(_('no revisions specified'))
2373 elif opts['applied']:
2373 elif opts['applied']:
2374 revrange = ('qbase:qtip',) + revrange
2374 revrange = ('qbase:qtip',) + revrange
2375
2375
2376 q = repo.mq
2376 q = repo.mq
2377 if not q.applied:
2377 if not q.applied:
2378 ui.status(_('no patches applied\n'))
2378 ui.status(_('no patches applied\n'))
2379 return 0
2379 return 0
2380
2380
2381 revs = cmdutil.revrange(repo, revrange)
2381 revs = cmdutil.revrange(repo, revrange)
2382 q.finish(repo, revs)
2382 q.finish(repo, revs)
2383 q.save_dirty()
2383 q.save_dirty()
2384 return 0
2384 return 0
2385
2385
2386 def reposetup(ui, repo):
2386 def reposetup(ui, repo):
2387 class mqrepo(repo.__class__):
2387 class mqrepo(repo.__class__):
2388 def abort_if_wdir_patched(self, errmsg, force=False):
2388 def abort_if_wdir_patched(self, errmsg, force=False):
2389 if self.mq.applied and not force:
2389 if self.mq.applied and not force:
2390 parent = hex(self.dirstate.parents()[0])
2390 parent = hex(self.dirstate.parents()[0])
2391 if parent in [s.rev for s in self.mq.applied]:
2391 if parent in [s.rev for s in self.mq.applied]:
2392 raise util.Abort(errmsg)
2392 raise util.Abort(errmsg)
2393
2393
2394 def commit(self, *args, **opts):
2394 def commit(self, *args, **opts):
2395 if len(args) >= 6:
2395 if len(args) >= 6:
2396 force = args[5]
2396 force = args[5]
2397 else:
2397 else:
2398 force = opts.get('force')
2398 force = opts.get('force')
2399 self.abort_if_wdir_patched(
2399 self.abort_if_wdir_patched(
2400 _('cannot commit over an applied mq patch'),
2400 _('cannot commit over an applied mq patch'),
2401 force)
2401 force)
2402
2402
2403 return super(mqrepo, self).commit(*args, **opts)
2403 return super(mqrepo, self).commit(*args, **opts)
2404
2404
2405 def push(self, remote, force=False, revs=None):
2405 def push(self, remote, force=False, revs=None):
2406 if self.mq.applied and not force and not revs:
2406 if self.mq.applied and not force and not revs:
2407 raise util.Abort(_('source has mq patches applied'))
2407 raise util.Abort(_('source has mq patches applied'))
2408 return super(mqrepo, self).push(remote, force, revs)
2408 return super(mqrepo, self).push(remote, force, revs)
2409
2409
2410 def tags(self):
2410 def tags(self):
2411 if self.tagscache:
2411 if self.tagscache:
2412 return self.tagscache
2412 return self.tagscache
2413
2413
2414 tagscache = super(mqrepo, self).tags()
2414 tagscache = super(mqrepo, self).tags()
2415
2415
2416 q = self.mq
2416 q = self.mq
2417 if not q.applied:
2417 if not q.applied:
2418 return tagscache
2418 return tagscache
2419
2419
2420 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2420 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2421
2421
2422 if mqtags[-1][0] not in self.changelog.nodemap:
2422 if mqtags[-1][0] not in self.changelog.nodemap:
2423 self.ui.warn(_('mq status file refers to unknown node %s\n')
2423 self.ui.warn(_('mq status file refers to unknown node %s\n')
2424 % short(mqtags[-1][0]))
2424 % short(mqtags[-1][0]))
2425 return tagscache
2425 return tagscache
2426
2426
2427 mqtags.append((mqtags[-1][0], 'qtip'))
2427 mqtags.append((mqtags[-1][0], 'qtip'))
2428 mqtags.append((mqtags[0][0], 'qbase'))
2428 mqtags.append((mqtags[0][0], 'qbase'))
2429 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2429 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2430 for patch in mqtags:
2430 for patch in mqtags:
2431 if patch[1] in tagscache:
2431 if patch[1] in tagscache:
2432 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2432 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2433 % patch[1])
2433 % patch[1])
2434 else:
2434 else:
2435 tagscache[patch[1]] = patch[0]
2435 tagscache[patch[1]] = patch[0]
2436
2436
2437 return tagscache
2437 return tagscache
2438
2438
2439 def _branchtags(self, partial, lrev):
2439 def _branchtags(self, partial, lrev):
2440 q = self.mq
2440 q = self.mq
2441 if not q.applied:
2441 if not q.applied:
2442 return super(mqrepo, self)._branchtags(partial, lrev)
2442 return super(mqrepo, self)._branchtags(partial, lrev)
2443
2443
2444 cl = self.changelog
2444 cl = self.changelog
2445 qbasenode = bin(q.applied[0].rev)
2445 qbasenode = bin(q.applied[0].rev)
2446 if qbasenode not in cl.nodemap:
2446 if qbasenode not in cl.nodemap:
2447 self.ui.warn(_('mq status file refers to unknown node %s\n')
2447 self.ui.warn(_('mq status file refers to unknown node %s\n')
2448 % short(qbasenode))
2448 % short(qbasenode))
2449 return super(mqrepo, self)._branchtags(partial, lrev)
2449 return super(mqrepo, self)._branchtags(partial, lrev)
2450
2450
2451 qbase = cl.rev(qbasenode)
2451 qbase = cl.rev(qbasenode)
2452 start = lrev + 1
2452 start = lrev + 1
2453 if start < qbase:
2453 if start < qbase:
2454 # update the cache (excluding the patches) and save it
2454 # update the cache (excluding the patches) and save it
2455 self._updatebranchcache(partial, lrev+1, qbase)
2455 self._updatebranchcache(partial, lrev+1, qbase)
2456 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2456 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2457 start = qbase
2457 start = qbase
2458 # if start = qbase, the cache is as updated as it should be.
2458 # if start = qbase, the cache is as updated as it should be.
2459 # if start > qbase, the cache includes (part of) the patches.
2459 # if start > qbase, the cache includes (part of) the patches.
2460 # we might as well use it, but we won't save it.
2460 # we might as well use it, but we won't save it.
2461
2461
2462 # update the cache up to the tip
2462 # update the cache up to the tip
2463 self._updatebranchcache(partial, start, len(cl))
2463 self._updatebranchcache(partial, start, len(cl))
2464
2464
2465 return partial
2465 return partial
2466
2466
2467 if repo.local():
2467 if repo.local():
2468 repo.__class__ = mqrepo
2468 repo.__class__ = mqrepo
2469 repo.mq = queue(ui, repo.join(""))
2469 repo.mq = queue(ui, repo.join(""))
2470
2470
2471 def mqimport(orig, ui, repo, *args, **kwargs):
2471 def mqimport(orig, ui, repo, *args, **kwargs):
2472 if hasattr(repo, 'abort_if_wdir_patched'):
2472 if hasattr(repo, 'abort_if_wdir_patched'):
2473 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2473 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2474 kwargs.get('force'))
2474 kwargs.get('force'))
2475 return orig(ui, repo, *args, **kwargs)
2475 return orig(ui, repo, *args, **kwargs)
2476
2476
2477 def uisetup(ui):
2477 def uisetup(ui):
2478 extensions.wrapcommand(commands.table, 'import', mqimport)
2478 extensions.wrapcommand(commands.table, 'import', mqimport)
2479
2479
2480 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2480 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2481
2481
2482 cmdtable = {
2482 cmdtable = {
2483 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2483 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2484 "qclone":
2484 "qclone":
2485 (clone,
2485 (clone,
2486 [('', 'pull', None, _('use pull protocol to copy metadata')),
2486 [('', 'pull', None, _('use pull protocol to copy metadata')),
2487 ('U', 'noupdate', None, _('do not update the new working directories')),
2487 ('U', 'noupdate', None, _('do not update the new working directories')),
2488 ('', 'uncompressed', None,
2488 ('', 'uncompressed', None,
2489 _('use uncompressed transfer (fast over LAN)')),
2489 _('use uncompressed transfer (fast over LAN)')),
2490 ('p', 'patches', '', _('location of source patch repository')),
2490 ('p', 'patches', '', _('location of source patch repository')),
2491 ] + commands.remoteopts,
2491 ] + commands.remoteopts,
2492 _('hg qclone [OPTION]... SOURCE [DEST]')),
2492 _('hg qclone [OPTION]... SOURCE [DEST]')),
2493 "qcommit|qci":
2493 "qcommit|qci":
2494 (commit,
2494 (commit,
2495 commands.table["^commit|ci"][1],
2495 commands.table["^commit|ci"][1],
2496 _('hg qcommit [OPTION]... [FILE]...')),
2496 _('hg qcommit [OPTION]... [FILE]...')),
2497 "^qdiff":
2497 "^qdiff":
2498 (diff,
2498 (diff,
2499 commands.diffopts + commands.diffopts2 + commands.walkopts,
2499 commands.diffopts + commands.diffopts2 + commands.walkopts,
2500 _('hg qdiff [OPTION]... [FILE]...')),
2500 _('hg qdiff [OPTION]... [FILE]...')),
2501 "qdelete|qremove|qrm":
2501 "qdelete|qremove|qrm":
2502 (delete,
2502 (delete,
2503 [('k', 'keep', None, _('keep patch file')),
2503 [('k', 'keep', None, _('keep patch file')),
2504 ('r', 'rev', [], _('stop managing a revision'))],
2504 ('r', 'rev', [], _('stop managing a revision'))],
2505 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2505 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2506 'qfold':
2506 'qfold':
2507 (fold,
2507 (fold,
2508 [('e', 'edit', None, _('edit patch header')),
2508 [('e', 'edit', None, _('edit patch header')),
2509 ('k', 'keep', None, _('keep folded patch files')),
2509 ('k', 'keep', None, _('keep folded patch files')),
2510 ] + commands.commitopts,
2510 ] + commands.commitopts,
2511 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2511 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2512 'qgoto':
2512 'qgoto':
2513 (goto,
2513 (goto,
2514 [('f', 'force', None, _('overwrite any local changes'))],
2514 [('f', 'force', None, _('overwrite any local changes'))],
2515 _('hg qgoto [OPTION]... PATCH')),
2515 _('hg qgoto [OPTION]... PATCH')),
2516 'qguard':
2516 'qguard':
2517 (guard,
2517 (guard,
2518 [('l', 'list', None, _('list all patches and guards')),
2518 [('l', 'list', None, _('list all patches and guards')),
2519 ('n', 'none', None, _('drop all guards'))],
2519 ('n', 'none', None, _('drop all guards'))],
2520 _('hg qguard [-l] [-n] -- [PATCH] [+GUARD]... [-GUARD]...')),
2520 _('hg qguard [-l] [-n] -- [PATCH] [+GUARD]... [-GUARD]...')),
2521 'qheader': (header, [], _('hg qheader [PATCH]')),
2521 'qheader': (header, [], _('hg qheader [PATCH]')),
2522 "^qimport":
2522 "^qimport":
2523 (qimport,
2523 (qimport,
2524 [('e', 'existing', None, _('import file in patch directory')),
2524 [('e', 'existing', None, _('import file in patch directory')),
2525 ('n', 'name', '', _('patch file name')),
2525 ('n', 'name', '', _('patch file name')),
2526 ('f', 'force', None, _('overwrite existing files')),
2526 ('f', 'force', None, _('overwrite existing files')),
2527 ('r', 'rev', [], _('place existing revisions under mq control')),
2527 ('r', 'rev', [], _('place existing revisions under mq control')),
2528 ('g', 'git', None, _('use git extended diff format')),
2528 ('g', 'git', None, _('use git extended diff format')),
2529 ('P', 'push', None, _('qpush after importing'))],
2529 ('P', 'push', None, _('qpush after importing'))],
2530 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2530 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2531 "^qinit":
2531 "^qinit":
2532 (init,
2532 (init,
2533 [('c', 'create-repo', None, _('create queue repository'))],
2533 [('c', 'create-repo', None, _('create queue repository'))],
2534 _('hg qinit [-c]')),
2534 _('hg qinit [-c]')),
2535 "qnew":
2535 "qnew":
2536 (new,
2536 (new,
2537 [('e', 'edit', None, _('edit commit message')),
2537 [('e', 'edit', None, _('edit commit message')),
2538 ('f', 'force', None, _('import uncommitted changes into patch')),
2538 ('f', 'force', None, _('import uncommitted changes into patch')),
2539 ('g', 'git', None, _('use git extended diff format')),
2539 ('g', 'git', None, _('use git extended diff format')),
2540 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2540 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2541 ('u', 'user', '', _('add "From: <given user>" to patch')),
2541 ('u', 'user', '', _('add "From: <given user>" to patch')),
2542 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2542 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2543 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2543 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2544 ] + commands.walkopts + commands.commitopts,
2544 ] + commands.walkopts + commands.commitopts,
2545 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2545 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2546 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2546 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2547 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2547 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2548 "^qpop":
2548 "^qpop":
2549 (pop,
2549 (pop,
2550 [('a', 'all', None, _('pop all patches')),
2550 [('a', 'all', None, _('pop all patches')),
2551 ('n', 'name', '', _('queue name to pop')),
2551 ('n', 'name', '', _('queue name to pop')),
2552 ('f', 'force', None, _('forget any local changes'))],
2552 ('f', 'force', None, _('forget any local changes'))],
2553 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2553 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2554 "^qpush":
2554 "^qpush":
2555 (push,
2555 (push,
2556 [('f', 'force', None, _('apply if the patch has rejects')),
2556 [('f', 'force', None, _('apply if the patch has rejects')),
2557 ('l', 'list', None, _('list patch name in commit text')),
2557 ('l', 'list', None, _('list patch name in commit text')),
2558 ('a', 'all', None, _('apply all patches')),
2558 ('a', 'all', None, _('apply all patches')),
2559 ('m', 'merge', None, _('merge from another queue')),
2559 ('m', 'merge', None, _('merge from another queue')),
2560 ('n', 'name', '', _('merge queue name'))],
2560 ('n', 'name', '', _('merge queue name'))],
2561 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2561 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2562 "^qrefresh":
2562 "^qrefresh":
2563 (refresh,
2563 (refresh,
2564 [('e', 'edit', None, _('edit commit message')),
2564 [('e', 'edit', None, _('edit commit message')),
2565 ('g', 'git', None, _('use git extended diff format')),
2565 ('g', 'git', None, _('use git extended diff format')),
2566 ('s', 'short', None, _('refresh only files already in the patch and specified files')),
2566 ('s', 'short', None, _('refresh only files already in the patch and specified files')),
2567 ('U', 'currentuser', None, _('add/update "From: <current user>" in patch')),
2567 ('U', 'currentuser', None, _('add/update "From: <current user>" in patch')),
2568 ('u', 'user', '', _('add/update "From: <given user>" in patch')),
2568 ('u', 'user', '', _('add/update "From: <given user>" in patch')),
2569 ('D', 'currentdate', None, _('update "Date: <current date>" in patch (if present)')),
2569 ('D', 'currentdate', None, _('update "Date: <current date>" in patch (if present)')),
2570 ('d', 'date', '', _('update "Date: <given date>" in patch (if present)'))
2570 ('d', 'date', '', _('update "Date: <given date>" in patch (if present)'))
2571 ] + commands.walkopts + commands.commitopts,
2571 ] + commands.walkopts + commands.commitopts,
2572 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2572 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2573 'qrename|qmv':
2573 'qrename|qmv':
2574 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2574 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2575 "qrestore":
2575 "qrestore":
2576 (restore,
2576 (restore,
2577 [('d', 'delete', None, _('delete save entry')),
2577 [('d', 'delete', None, _('delete save entry')),
2578 ('u', 'update', None, _('update queue working directory'))],
2578 ('u', 'update', None, _('update queue working directory'))],
2579 _('hg qrestore [-d] [-u] REV')),
2579 _('hg qrestore [-d] [-u] REV')),
2580 "qsave":
2580 "qsave":
2581 (save,
2581 (save,
2582 [('c', 'copy', None, _('copy patch directory')),
2582 [('c', 'copy', None, _('copy patch directory')),
2583 ('n', 'name', '', _('copy directory name')),
2583 ('n', 'name', '', _('copy directory name')),
2584 ('e', 'empty', None, _('clear queue status file')),
2584 ('e', 'empty', None, _('clear queue status file')),
2585 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2585 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2586 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2586 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2587 "qselect":
2587 "qselect":
2588 (select,
2588 (select,
2589 [('n', 'none', None, _('disable all guards')),
2589 [('n', 'none', None, _('disable all guards')),
2590 ('s', 'series', None, _('list all guards in series file')),
2590 ('s', 'series', None, _('list all guards in series file')),
2591 ('', 'pop', None, _('pop to before first guarded applied patch')),
2591 ('', 'pop', None, _('pop to before first guarded applied patch')),
2592 ('', 'reapply', None, _('pop, then reapply patches'))],
2592 ('', 'reapply', None, _('pop, then reapply patches'))],
2593 _('hg qselect [OPTION]... [GUARD]...')),
2593 _('hg qselect [OPTION]... [GUARD]...')),
2594 "qseries":
2594 "qseries":
2595 (series,
2595 (series,
2596 [('m', 'missing', None, _('print patches not in series')),
2596 [('m', 'missing', None, _('print patches not in series')),
2597 ] + seriesopts,
2597 ] + seriesopts,
2598 _('hg qseries [-ms]')),
2598 _('hg qseries [-ms]')),
2599 "^strip":
2599 "^strip":
2600 (strip,
2600 (strip,
2601 [('f', 'force', None, _('force removal with local changes')),
2601 [('f', 'force', None, _('force removal with local changes')),
2602 ('b', 'backup', None, _('bundle unrelated changesets')),
2602 ('b', 'backup', None, _('bundle unrelated changesets')),
2603 ('n', 'nobackup', None, _('no backups'))],
2603 ('n', 'nobackup', None, _('no backups'))],
2604 _('hg strip [-f] [-b] [-n] REV')),
2604 _('hg strip [-f] [-b] [-n] REV')),
2605 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2605 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2606 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2606 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2607 "qfinish":
2607 "qfinish":
2608 (finish,
2608 (finish,
2609 [('a', 'applied', None, _('finish all applied changesets'))],
2609 [('a', 'applied', None, _('finish all applied changesets'))],
2610 _('hg qfinish [-a] [REV...]')),
2610 _('hg qfinish [-a] [REV...]')),
2611 }
2611 }
@@ -1,469 +1,469 b''
1 # This library is free software; you can redistribute it and/or
1 # This library is free software; you can redistribute it and/or
2 # modify it under the terms of the GNU Lesser General Public
2 # modify it under the terms of the GNU Lesser General Public
3 # License as published by the Free Software Foundation; either
3 # License as published by the Free Software Foundation; either
4 # version 2.1 of the License, or (at your option) any later version.
4 # version 2.1 of the License, or (at your option) any later version.
5 #
5 #
6 # This library is distributed in the hope that it will be useful,
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
9 # Lesser General Public License for more details.
10 #
10 #
11 # You should have received a copy of the GNU Lesser General Public
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the
12 # License along with this library; if not, write to the
13 # Free Software Foundation, Inc.,
13 # Free Software Foundation, Inc.,
14 # 59 Temple Place, Suite 330,
14 # 59 Temple Place, Suite 330,
15 # Boston, MA 02111-1307 USA
15 # Boston, MA 02111-1307 USA
16
16
17 # This file is part of urlgrabber, a high-level cross-protocol url-grabber
17 # This file is part of urlgrabber, a high-level cross-protocol url-grabber
18 # Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko
18 # Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko
19
19
20 # $Id: byterange.py,v 1.9 2005/02/14 21:55:07 mstenner Exp $
20 # $Id: byterange.py,v 1.9 2005/02/14 21:55:07 mstenner Exp $
21
21
22 import os
22 import os
23 import stat
23 import stat
24 import urllib
24 import urllib
25 import urllib2
25 import urllib2
26 import rfc822
26 import email.utils
27
27
28 try:
28 try:
29 from cStringIO import StringIO
29 from cStringIO import StringIO
30 except ImportError, msg:
30 except ImportError, msg:
31 from StringIO import StringIO
31 from StringIO import StringIO
32
32
33 class RangeError(IOError):
33 class RangeError(IOError):
34 """Error raised when an unsatisfiable range is requested."""
34 """Error raised when an unsatisfiable range is requested."""
35 pass
35 pass
36
36
37 class HTTPRangeHandler(urllib2.BaseHandler):
37 class HTTPRangeHandler(urllib2.BaseHandler):
38 """Handler that enables HTTP Range headers.
38 """Handler that enables HTTP Range headers.
39
39
40 This was extremely simple. The Range header is a HTTP feature to
40 This was extremely simple. The Range header is a HTTP feature to
41 begin with so all this class does is tell urllib2 that the
41 begin with so all this class does is tell urllib2 that the
42 "206 Partial Content" reponse from the HTTP server is what we
42 "206 Partial Content" reponse from the HTTP server is what we
43 expected.
43 expected.
44
44
45 Example:
45 Example:
46 import urllib2
46 import urllib2
47 import byterange
47 import byterange
48
48
49 range_handler = range.HTTPRangeHandler()
49 range_handler = range.HTTPRangeHandler()
50 opener = urllib2.build_opener(range_handler)
50 opener = urllib2.build_opener(range_handler)
51
51
52 # install it
52 # install it
53 urllib2.install_opener(opener)
53 urllib2.install_opener(opener)
54
54
55 # create Request and set Range header
55 # create Request and set Range header
56 req = urllib2.Request('http://www.python.org/')
56 req = urllib2.Request('http://www.python.org/')
57 req.header['Range'] = 'bytes=30-50'
57 req.header['Range'] = 'bytes=30-50'
58 f = urllib2.urlopen(req)
58 f = urllib2.urlopen(req)
59 """
59 """
60
60
61 def http_error_206(self, req, fp, code, msg, hdrs):
61 def http_error_206(self, req, fp, code, msg, hdrs):
62 # 206 Partial Content Response
62 # 206 Partial Content Response
63 r = urllib.addinfourl(fp, hdrs, req.get_full_url())
63 r = urllib.addinfourl(fp, hdrs, req.get_full_url())
64 r.code = code
64 r.code = code
65 r.msg = msg
65 r.msg = msg
66 return r
66 return r
67
67
68 def http_error_416(self, req, fp, code, msg, hdrs):
68 def http_error_416(self, req, fp, code, msg, hdrs):
69 # HTTP's Range Not Satisfiable error
69 # HTTP's Range Not Satisfiable error
70 raise RangeError('Requested Range Not Satisfiable')
70 raise RangeError('Requested Range Not Satisfiable')
71
71
72 class RangeableFileObject:
72 class RangeableFileObject:
73 """File object wrapper to enable raw range handling.
73 """File object wrapper to enable raw range handling.
74 This was implemented primarilary for handling range
74 This was implemented primarilary for handling range
75 specifications for file:// urls. This object effectively makes
75 specifications for file:// urls. This object effectively makes
76 a file object look like it consists only of a range of bytes in
76 a file object look like it consists only of a range of bytes in
77 the stream.
77 the stream.
78
78
79 Examples:
79 Examples:
80 # expose 10 bytes, starting at byte position 20, from
80 # expose 10 bytes, starting at byte position 20, from
81 # /etc/aliases.
81 # /etc/aliases.
82 >>> fo = RangeableFileObject(file('/etc/passwd', 'r'), (20,30))
82 >>> fo = RangeableFileObject(file('/etc/passwd', 'r'), (20,30))
83 # seek seeks within the range (to position 23 in this case)
83 # seek seeks within the range (to position 23 in this case)
84 >>> fo.seek(3)
84 >>> fo.seek(3)
85 # tell tells where your at _within the range_ (position 3 in
85 # tell tells where your at _within the range_ (position 3 in
86 # this case)
86 # this case)
87 >>> fo.tell()
87 >>> fo.tell()
88 # read EOFs if an attempt is made to read past the last
88 # read EOFs if an attempt is made to read past the last
89 # byte in the range. the following will return only 7 bytes.
89 # byte in the range. the following will return only 7 bytes.
90 >>> fo.read(30)
90 >>> fo.read(30)
91 """
91 """
92
92
93 def __init__(self, fo, rangetup):
93 def __init__(self, fo, rangetup):
94 """Create a RangeableFileObject.
94 """Create a RangeableFileObject.
95 fo -- a file like object. only the read() method need be
95 fo -- a file like object. only the read() method need be
96 supported but supporting an optimized seek() is
96 supported but supporting an optimized seek() is
97 preferable.
97 preferable.
98 rangetup -- a (firstbyte,lastbyte) tuple specifying the range
98 rangetup -- a (firstbyte,lastbyte) tuple specifying the range
99 to work over.
99 to work over.
100 The file object provided is assumed to be at byte offset 0.
100 The file object provided is assumed to be at byte offset 0.
101 """
101 """
102 self.fo = fo
102 self.fo = fo
103 (self.firstbyte, self.lastbyte) = range_tuple_normalize(rangetup)
103 (self.firstbyte, self.lastbyte) = range_tuple_normalize(rangetup)
104 self.realpos = 0
104 self.realpos = 0
105 self._do_seek(self.firstbyte)
105 self._do_seek(self.firstbyte)
106
106
107 def __getattr__(self, name):
107 def __getattr__(self, name):
108 """This effectively allows us to wrap at the instance level.
108 """This effectively allows us to wrap at the instance level.
109 Any attribute not found in _this_ object will be searched for
109 Any attribute not found in _this_ object will be searched for
110 in self.fo. This includes methods."""
110 in self.fo. This includes methods."""
111 if hasattr(self.fo, name):
111 if hasattr(self.fo, name):
112 return getattr(self.fo, name)
112 return getattr(self.fo, name)
113 raise AttributeError(name)
113 raise AttributeError(name)
114
114
115 def tell(self):
115 def tell(self):
116 """Return the position within the range.
116 """Return the position within the range.
117 This is different from fo.seek in that position 0 is the
117 This is different from fo.seek in that position 0 is the
118 first byte position of the range tuple. For example, if
118 first byte position of the range tuple. For example, if
119 this object was created with a range tuple of (500,899),
119 this object was created with a range tuple of (500,899),
120 tell() will return 0 when at byte position 500 of the file.
120 tell() will return 0 when at byte position 500 of the file.
121 """
121 """
122 return (self.realpos - self.firstbyte)
122 return (self.realpos - self.firstbyte)
123
123
124 def seek(self, offset, whence=0):
124 def seek(self, offset, whence=0):
125 """Seek within the byte range.
125 """Seek within the byte range.
126 Positioning is identical to that described under tell().
126 Positioning is identical to that described under tell().
127 """
127 """
128 assert whence in (0, 1, 2)
128 assert whence in (0, 1, 2)
129 if whence == 0: # absolute seek
129 if whence == 0: # absolute seek
130 realoffset = self.firstbyte + offset
130 realoffset = self.firstbyte + offset
131 elif whence == 1: # relative seek
131 elif whence == 1: # relative seek
132 realoffset = self.realpos + offset
132 realoffset = self.realpos + offset
133 elif whence == 2: # absolute from end of file
133 elif whence == 2: # absolute from end of file
134 # XXX: are we raising the right Error here?
134 # XXX: are we raising the right Error here?
135 raise IOError('seek from end of file not supported.')
135 raise IOError('seek from end of file not supported.')
136
136
137 # do not allow seek past lastbyte in range
137 # do not allow seek past lastbyte in range
138 if self.lastbyte and (realoffset >= self.lastbyte):
138 if self.lastbyte and (realoffset >= self.lastbyte):
139 realoffset = self.lastbyte
139 realoffset = self.lastbyte
140
140
141 self._do_seek(realoffset - self.realpos)
141 self._do_seek(realoffset - self.realpos)
142
142
143 def read(self, size=-1):
143 def read(self, size=-1):
144 """Read within the range.
144 """Read within the range.
145 This method will limit the size read based on the range.
145 This method will limit the size read based on the range.
146 """
146 """
147 size = self._calc_read_size(size)
147 size = self._calc_read_size(size)
148 rslt = self.fo.read(size)
148 rslt = self.fo.read(size)
149 self.realpos += len(rslt)
149 self.realpos += len(rslt)
150 return rslt
150 return rslt
151
151
152 def readline(self, size=-1):
152 def readline(self, size=-1):
153 """Read lines within the range.
153 """Read lines within the range.
154 This method will limit the size read based on the range.
154 This method will limit the size read based on the range.
155 """
155 """
156 size = self._calc_read_size(size)
156 size = self._calc_read_size(size)
157 rslt = self.fo.readline(size)
157 rslt = self.fo.readline(size)
158 self.realpos += len(rslt)
158 self.realpos += len(rslt)
159 return rslt
159 return rslt
160
160
161 def _calc_read_size(self, size):
161 def _calc_read_size(self, size):
162 """Handles calculating the amount of data to read based on
162 """Handles calculating the amount of data to read based on
163 the range.
163 the range.
164 """
164 """
165 if self.lastbyte:
165 if self.lastbyte:
166 if size > -1:
166 if size > -1:
167 if ((self.realpos + size) >= self.lastbyte):
167 if ((self.realpos + size) >= self.lastbyte):
168 size = (self.lastbyte - self.realpos)
168 size = (self.lastbyte - self.realpos)
169 else:
169 else:
170 size = (self.lastbyte - self.realpos)
170 size = (self.lastbyte - self.realpos)
171 return size
171 return size
172
172
173 def _do_seek(self, offset):
173 def _do_seek(self, offset):
174 """Seek based on whether wrapped object supports seek().
174 """Seek based on whether wrapped object supports seek().
175 offset is relative to the current position (self.realpos).
175 offset is relative to the current position (self.realpos).
176 """
176 """
177 assert offset >= 0
177 assert offset >= 0
178 if not hasattr(self.fo, 'seek'):
178 if not hasattr(self.fo, 'seek'):
179 self._poor_mans_seek(offset)
179 self._poor_mans_seek(offset)
180 else:
180 else:
181 self.fo.seek(self.realpos + offset)
181 self.fo.seek(self.realpos + offset)
182 self.realpos += offset
182 self.realpos += offset
183
183
184 def _poor_mans_seek(self, offset):
184 def _poor_mans_seek(self, offset):
185 """Seek by calling the wrapped file objects read() method.
185 """Seek by calling the wrapped file objects read() method.
186 This is used for file like objects that do not have native
186 This is used for file like objects that do not have native
187 seek support. The wrapped objects read() method is called
187 seek support. The wrapped objects read() method is called
188 to manually seek to the desired position.
188 to manually seek to the desired position.
189 offset -- read this number of bytes from the wrapped
189 offset -- read this number of bytes from the wrapped
190 file object.
190 file object.
191 raise RangeError if we encounter EOF before reaching the
191 raise RangeError if we encounter EOF before reaching the
192 specified offset.
192 specified offset.
193 """
193 """
194 pos = 0
194 pos = 0
195 bufsize = 1024
195 bufsize = 1024
196 while pos < offset:
196 while pos < offset:
197 if (pos + bufsize) > offset:
197 if (pos + bufsize) > offset:
198 bufsize = offset - pos
198 bufsize = offset - pos
199 buf = self.fo.read(bufsize)
199 buf = self.fo.read(bufsize)
200 if len(buf) != bufsize:
200 if len(buf) != bufsize:
201 raise RangeError('Requested Range Not Satisfiable')
201 raise RangeError('Requested Range Not Satisfiable')
202 pos += bufsize
202 pos += bufsize
203
203
204 class FileRangeHandler(urllib2.FileHandler):
204 class FileRangeHandler(urllib2.FileHandler):
205 """FileHandler subclass that adds Range support.
205 """FileHandler subclass that adds Range support.
206 This class handles Range headers exactly like an HTTP
206 This class handles Range headers exactly like an HTTP
207 server would.
207 server would.
208 """
208 """
209 def open_local_file(self, req):
209 def open_local_file(self, req):
210 import mimetypes
210 import mimetypes
211 import mimetools
211 import mimetools
212 host = req.get_host()
212 host = req.get_host()
213 file = req.get_selector()
213 file = req.get_selector()
214 localfile = urllib.url2pathname(file)
214 localfile = urllib.url2pathname(file)
215 stats = os.stat(localfile)
215 stats = os.stat(localfile)
216 size = stats[stat.ST_SIZE]
216 size = stats[stat.ST_SIZE]
217 modified = rfc822.formatdate(stats[stat.ST_MTIME])
217 modified = email.utils.formatdate(stats[stat.ST_MTIME])
218 mtype = mimetypes.guess_type(file)[0]
218 mtype = mimetypes.guess_type(file)[0]
219 if host:
219 if host:
220 host, port = urllib.splitport(host)
220 host, port = urllib.splitport(host)
221 if port or socket.gethostbyname(host) not in self.get_names():
221 if port or socket.gethostbyname(host) not in self.get_names():
222 raise urllib2.URLError('file not on local host')
222 raise urllib2.URLError('file not on local host')
223 fo = open(localfile,'rb')
223 fo = open(localfile,'rb')
224 brange = req.headers.get('Range', None)
224 brange = req.headers.get('Range', None)
225 brange = range_header_to_tuple(brange)
225 brange = range_header_to_tuple(brange)
226 assert brange != ()
226 assert brange != ()
227 if brange:
227 if brange:
228 (fb, lb) = brange
228 (fb, lb) = brange
229 if lb == '':
229 if lb == '':
230 lb = size
230 lb = size
231 if fb < 0 or fb > size or lb > size:
231 if fb < 0 or fb > size or lb > size:
232 raise RangeError('Requested Range Not Satisfiable')
232 raise RangeError('Requested Range Not Satisfiable')
233 size = (lb - fb)
233 size = (lb - fb)
234 fo = RangeableFileObject(fo, (fb, lb))
234 fo = RangeableFileObject(fo, (fb, lb))
235 headers = mimetools.Message(StringIO(
235 headers = mimetools.Message(StringIO(
236 'Content-Type: %s\nContent-Length: %d\nLast-Modified: %s\n' %
236 'Content-Type: %s\nContent-Length: %d\nLast-Modified: %s\n' %
237 (mtype or 'text/plain', size, modified)))
237 (mtype or 'text/plain', size, modified)))
238 return urllib.addinfourl(fo, headers, 'file:'+file)
238 return urllib.addinfourl(fo, headers, 'file:'+file)
239
239
240
240
241 # FTP Range Support
241 # FTP Range Support
242 # Unfortunately, a large amount of base FTP code had to be copied
242 # Unfortunately, a large amount of base FTP code had to be copied
243 # from urllib and urllib2 in order to insert the FTP REST command.
243 # from urllib and urllib2 in order to insert the FTP REST command.
244 # Code modifications for range support have been commented as
244 # Code modifications for range support have been commented as
245 # follows:
245 # follows:
246 # -- range support modifications start/end here
246 # -- range support modifications start/end here
247
247
248 from urllib import splitport, splituser, splitpasswd, splitattr, \
248 from urllib import splitport, splituser, splitpasswd, splitattr, \
249 unquote, addclosehook, addinfourl
249 unquote, addclosehook, addinfourl
250 import ftplib
250 import ftplib
251 import socket
251 import socket
252 import sys
252 import sys
253 import mimetypes
253 import mimetypes
254 import mimetools
254 import mimetools
255
255
256 class FTPRangeHandler(urllib2.FTPHandler):
256 class FTPRangeHandler(urllib2.FTPHandler):
257 def ftp_open(self, req):
257 def ftp_open(self, req):
258 host = req.get_host()
258 host = req.get_host()
259 if not host:
259 if not host:
260 raise IOError('ftp error', 'no host given')
260 raise IOError('ftp error', 'no host given')
261 host, port = splitport(host)
261 host, port = splitport(host)
262 if port is None:
262 if port is None:
263 port = ftplib.FTP_PORT
263 port = ftplib.FTP_PORT
264
264
265 # username/password handling
265 # username/password handling
266 user, host = splituser(host)
266 user, host = splituser(host)
267 if user:
267 if user:
268 user, passwd = splitpasswd(user)
268 user, passwd = splitpasswd(user)
269 else:
269 else:
270 passwd = None
270 passwd = None
271 host = unquote(host)
271 host = unquote(host)
272 user = unquote(user or '')
272 user = unquote(user or '')
273 passwd = unquote(passwd or '')
273 passwd = unquote(passwd or '')
274
274
275 try:
275 try:
276 host = socket.gethostbyname(host)
276 host = socket.gethostbyname(host)
277 except socket.error, msg:
277 except socket.error, msg:
278 raise urllib2.URLError(msg)
278 raise urllib2.URLError(msg)
279 path, attrs = splitattr(req.get_selector())
279 path, attrs = splitattr(req.get_selector())
280 dirs = path.split('/')
280 dirs = path.split('/')
281 dirs = map(unquote, dirs)
281 dirs = map(unquote, dirs)
282 dirs, file = dirs[:-1], dirs[-1]
282 dirs, file = dirs[:-1], dirs[-1]
283 if dirs and not dirs[0]:
283 if dirs and not dirs[0]:
284 dirs = dirs[1:]
284 dirs = dirs[1:]
285 try:
285 try:
286 fw = self.connect_ftp(user, passwd, host, port, dirs)
286 fw = self.connect_ftp(user, passwd, host, port, dirs)
287 type = file and 'I' or 'D'
287 type = file and 'I' or 'D'
288 for attr in attrs:
288 for attr in attrs:
289 attr, value = splitattr(attr)
289 attr, value = splitattr(attr)
290 if attr.lower() == 'type' and \
290 if attr.lower() == 'type' and \
291 value in ('a', 'A', 'i', 'I', 'd', 'D'):
291 value in ('a', 'A', 'i', 'I', 'd', 'D'):
292 type = value.upper()
292 type = value.upper()
293
293
294 # -- range support modifications start here
294 # -- range support modifications start here
295 rest = None
295 rest = None
296 range_tup = range_header_to_tuple(req.headers.get('Range', None))
296 range_tup = range_header_to_tuple(req.headers.get('Range', None))
297 assert range_tup != ()
297 assert range_tup != ()
298 if range_tup:
298 if range_tup:
299 (fb, lb) = range_tup
299 (fb, lb) = range_tup
300 if fb > 0:
300 if fb > 0:
301 rest = fb
301 rest = fb
302 # -- range support modifications end here
302 # -- range support modifications end here
303
303
304 fp, retrlen = fw.retrfile(file, type, rest)
304 fp, retrlen = fw.retrfile(file, type, rest)
305
305
306 # -- range support modifications start here
306 # -- range support modifications start here
307 if range_tup:
307 if range_tup:
308 (fb, lb) = range_tup
308 (fb, lb) = range_tup
309 if lb == '':
309 if lb == '':
310 if retrlen is None or retrlen == 0:
310 if retrlen is None or retrlen == 0:
311 raise RangeError('Requested Range Not Satisfiable due to unobtainable file length.')
311 raise RangeError('Requested Range Not Satisfiable due to unobtainable file length.')
312 lb = retrlen
312 lb = retrlen
313 retrlen = lb - fb
313 retrlen = lb - fb
314 if retrlen < 0:
314 if retrlen < 0:
315 # beginning of range is larger than file
315 # beginning of range is larger than file
316 raise RangeError('Requested Range Not Satisfiable')
316 raise RangeError('Requested Range Not Satisfiable')
317 else:
317 else:
318 retrlen = lb - fb
318 retrlen = lb - fb
319 fp = RangeableFileObject(fp, (0, retrlen))
319 fp = RangeableFileObject(fp, (0, retrlen))
320 # -- range support modifications end here
320 # -- range support modifications end here
321
321
322 headers = ""
322 headers = ""
323 mtype = mimetypes.guess_type(req.get_full_url())[0]
323 mtype = mimetypes.guess_type(req.get_full_url())[0]
324 if mtype:
324 if mtype:
325 headers += "Content-Type: %s\n" % mtype
325 headers += "Content-Type: %s\n" % mtype
326 if retrlen is not None and retrlen >= 0:
326 if retrlen is not None and retrlen >= 0:
327 headers += "Content-Length: %d\n" % retrlen
327 headers += "Content-Length: %d\n" % retrlen
328 sf = StringIO(headers)
328 sf = StringIO(headers)
329 headers = mimetools.Message(sf)
329 headers = mimetools.Message(sf)
330 return addinfourl(fp, headers, req.get_full_url())
330 return addinfourl(fp, headers, req.get_full_url())
331 except ftplib.all_errors, msg:
331 except ftplib.all_errors, msg:
332 raise IOError('ftp error', msg), sys.exc_info()[2]
332 raise IOError('ftp error', msg), sys.exc_info()[2]
333
333
334 def connect_ftp(self, user, passwd, host, port, dirs):
334 def connect_ftp(self, user, passwd, host, port, dirs):
335 fw = ftpwrapper(user, passwd, host, port, dirs)
335 fw = ftpwrapper(user, passwd, host, port, dirs)
336 return fw
336 return fw
337
337
338 class ftpwrapper(urllib.ftpwrapper):
338 class ftpwrapper(urllib.ftpwrapper):
339 # range support note:
339 # range support note:
340 # this ftpwrapper code is copied directly from
340 # this ftpwrapper code is copied directly from
341 # urllib. The only enhancement is to add the rest
341 # urllib. The only enhancement is to add the rest
342 # argument and pass it on to ftp.ntransfercmd
342 # argument and pass it on to ftp.ntransfercmd
343 def retrfile(self, file, type, rest=None):
343 def retrfile(self, file, type, rest=None):
344 self.endtransfer()
344 self.endtransfer()
345 if type in ('d', 'D'):
345 if type in ('d', 'D'):
346 cmd = 'TYPE A'
346 cmd = 'TYPE A'
347 isdir = 1
347 isdir = 1
348 else:
348 else:
349 cmd = 'TYPE ' + type
349 cmd = 'TYPE ' + type
350 isdir = 0
350 isdir = 0
351 try:
351 try:
352 self.ftp.voidcmd(cmd)
352 self.ftp.voidcmd(cmd)
353 except ftplib.all_errors:
353 except ftplib.all_errors:
354 self.init()
354 self.init()
355 self.ftp.voidcmd(cmd)
355 self.ftp.voidcmd(cmd)
356 conn = None
356 conn = None
357 if file and not isdir:
357 if file and not isdir:
358 # Use nlst to see if the file exists at all
358 # Use nlst to see if the file exists at all
359 try:
359 try:
360 self.ftp.nlst(file)
360 self.ftp.nlst(file)
361 except ftplib.error_perm, reason:
361 except ftplib.error_perm, reason:
362 raise IOError('ftp error', reason), sys.exc_info()[2]
362 raise IOError('ftp error', reason), sys.exc_info()[2]
363 # Restore the transfer mode!
363 # Restore the transfer mode!
364 self.ftp.voidcmd(cmd)
364 self.ftp.voidcmd(cmd)
365 # Try to retrieve as a file
365 # Try to retrieve as a file
366 try:
366 try:
367 cmd = 'RETR ' + file
367 cmd = 'RETR ' + file
368 conn = self.ftp.ntransfercmd(cmd, rest)
368 conn = self.ftp.ntransfercmd(cmd, rest)
369 except ftplib.error_perm, reason:
369 except ftplib.error_perm, reason:
370 if str(reason).startswith('501'):
370 if str(reason).startswith('501'):
371 # workaround for REST not supported error
371 # workaround for REST not supported error
372 fp, retrlen = self.retrfile(file, type)
372 fp, retrlen = self.retrfile(file, type)
373 fp = RangeableFileObject(fp, (rest,''))
373 fp = RangeableFileObject(fp, (rest,''))
374 return (fp, retrlen)
374 return (fp, retrlen)
375 elif not str(reason).startswith('550'):
375 elif not str(reason).startswith('550'):
376 raise IOError('ftp error', reason), sys.exc_info()[2]
376 raise IOError('ftp error', reason), sys.exc_info()[2]
377 if not conn:
377 if not conn:
378 # Set transfer mode to ASCII!
378 # Set transfer mode to ASCII!
379 self.ftp.voidcmd('TYPE A')
379 self.ftp.voidcmd('TYPE A')
380 # Try a directory listing
380 # Try a directory listing
381 if file:
381 if file:
382 cmd = 'LIST ' + file
382 cmd = 'LIST ' + file
383 else:
383 else:
384 cmd = 'LIST'
384 cmd = 'LIST'
385 conn = self.ftp.ntransfercmd(cmd)
385 conn = self.ftp.ntransfercmd(cmd)
386 self.busy = 1
386 self.busy = 1
387 # Pass back both a suitably decorated object and a retrieval length
387 # Pass back both a suitably decorated object and a retrieval length
388 return (addclosehook(conn[0].makefile('rb'),
388 return (addclosehook(conn[0].makefile('rb'),
389 self.endtransfer), conn[1])
389 self.endtransfer), conn[1])
390
390
391
391
392 ####################################################################
392 ####################################################################
393 # Range Tuple Functions
393 # Range Tuple Functions
394 # XXX: These range tuple functions might go better in a class.
394 # XXX: These range tuple functions might go better in a class.
395
395
396 _rangere = None
396 _rangere = None
397 def range_header_to_tuple(range_header):
397 def range_header_to_tuple(range_header):
398 """Get a (firstbyte,lastbyte) tuple from a Range header value.
398 """Get a (firstbyte,lastbyte) tuple from a Range header value.
399
399
400 Range headers have the form "bytes=<firstbyte>-<lastbyte>". This
400 Range headers have the form "bytes=<firstbyte>-<lastbyte>". This
401 function pulls the firstbyte and lastbyte values and returns
401 function pulls the firstbyte and lastbyte values and returns
402 a (firstbyte,lastbyte) tuple. If lastbyte is not specified in
402 a (firstbyte,lastbyte) tuple. If lastbyte is not specified in
403 the header value, it is returned as an empty string in the
403 the header value, it is returned as an empty string in the
404 tuple.
404 tuple.
405
405
406 Return None if range_header is None
406 Return None if range_header is None
407 Return () if range_header does not conform to the range spec
407 Return () if range_header does not conform to the range spec
408 pattern.
408 pattern.
409
409
410 """
410 """
411 global _rangere
411 global _rangere
412 if range_header is None:
412 if range_header is None:
413 return None
413 return None
414 if _rangere is None:
414 if _rangere is None:
415 import re
415 import re
416 _rangere = re.compile(r'^bytes=(\d{1,})-(\d*)')
416 _rangere = re.compile(r'^bytes=(\d{1,})-(\d*)')
417 match = _rangere.match(range_header)
417 match = _rangere.match(range_header)
418 if match:
418 if match:
419 tup = range_tuple_normalize(match.group(1, 2))
419 tup = range_tuple_normalize(match.group(1, 2))
420 if tup and tup[1]:
420 if tup and tup[1]:
421 tup = (tup[0], tup[1]+1)
421 tup = (tup[0], tup[1]+1)
422 return tup
422 return tup
423 return ()
423 return ()
424
424
425 def range_tuple_to_header(range_tup):
425 def range_tuple_to_header(range_tup):
426 """Convert a range tuple to a Range header value.
426 """Convert a range tuple to a Range header value.
427 Return a string of the form "bytes=<firstbyte>-<lastbyte>" or None
427 Return a string of the form "bytes=<firstbyte>-<lastbyte>" or None
428 if no range is needed.
428 if no range is needed.
429 """
429 """
430 if range_tup is None:
430 if range_tup is None:
431 return None
431 return None
432 range_tup = range_tuple_normalize(range_tup)
432 range_tup = range_tuple_normalize(range_tup)
433 if range_tup:
433 if range_tup:
434 if range_tup[1]:
434 if range_tup[1]:
435 range_tup = (range_tup[0], range_tup[1] - 1)
435 range_tup = (range_tup[0], range_tup[1] - 1)
436 return 'bytes=%s-%s' % range_tup
436 return 'bytes=%s-%s' % range_tup
437
437
438 def range_tuple_normalize(range_tup):
438 def range_tuple_normalize(range_tup):
439 """Normalize a (first_byte,last_byte) range tuple.
439 """Normalize a (first_byte,last_byte) range tuple.
440 Return a tuple whose first element is guaranteed to be an int
440 Return a tuple whose first element is guaranteed to be an int
441 and whose second element will be '' (meaning: the last byte) or
441 and whose second element will be '' (meaning: the last byte) or
442 an int. Finally, return None if the normalized tuple == (0,'')
442 an int. Finally, return None if the normalized tuple == (0,'')
443 as that is equivelant to retrieving the entire file.
443 as that is equivelant to retrieving the entire file.
444 """
444 """
445 if range_tup is None:
445 if range_tup is None:
446 return None
446 return None
447 # handle first byte
447 # handle first byte
448 fb = range_tup[0]
448 fb = range_tup[0]
449 if fb in (None, ''):
449 if fb in (None, ''):
450 fb = 0
450 fb = 0
451 else:
451 else:
452 fb = int(fb)
452 fb = int(fb)
453 # handle last byte
453 # handle last byte
454 try:
454 try:
455 lb = range_tup[1]
455 lb = range_tup[1]
456 except IndexError:
456 except IndexError:
457 lb = ''
457 lb = ''
458 else:
458 else:
459 if lb is None:
459 if lb is None:
460 lb = ''
460 lb = ''
461 elif lb != '':
461 elif lb != '':
462 lb = int(lb)
462 lb = int(lb)
463 # check if range is over the entire file
463 # check if range is over the entire file
464 if (fb, lb) == (0, ''):
464 if (fb, lb) == (0, ''):
465 return None
465 return None
466 # check that the range is valid
466 # check that the range is valid
467 if lb < fb:
467 if lb < fb:
468 raise RangeError('Invalid byte range: %s-%s' % (fb, lb))
468 raise RangeError('Invalid byte range: %s-%s' % (fb, lb))
469 return (fb, lb)
469 return (fb, lb)
@@ -1,3469 +1,3469 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, sys, textwrap, subprocess, difflib, time
11 import os, re, sys, textwrap, subprocess, difflib, time
12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
13 import patch, help, mdiff, tempfile, url, encoding
13 import patch, help, mdiff, tempfile, url, encoding
14 import archival, changegroup, cmdutil, sshserver, hbisect
14 import archival, changegroup, cmdutil, sshserver, hbisect
15 from hgweb import server
15 from hgweb import server
16 import merge as merge_
16 import merge as merge_
17
17
18 # Commands start here, listed alphabetically
18 # Commands start here, listed alphabetically
19
19
20 def add(ui, repo, *pats, **opts):
20 def add(ui, repo, *pats, **opts):
21 """add the specified files on the next commit
21 """add the specified files on the next commit
22
22
23 Schedule files to be version controlled and added to the
23 Schedule files to be version controlled and added to the
24 repository.
24 repository.
25
25
26 The files will be added to the repository at the next commit. To
26 The files will be added to the repository at the next commit. To
27 undo an add before that, see hg revert.
27 undo an add before that, see hg revert.
28
28
29 If no names are given, add all files to the repository.
29 If no names are given, add all files to the repository.
30 """
30 """
31
31
32 rejected = None
32 rejected = None
33 exacts = {}
33 exacts = {}
34 names = []
34 names = []
35 m = cmdutil.match(repo, pats, opts)
35 m = cmdutil.match(repo, pats, opts)
36 m.bad = lambda x,y: True
36 m.bad = lambda x,y: True
37 for abs in repo.walk(m):
37 for abs in repo.walk(m):
38 if m.exact(abs):
38 if m.exact(abs):
39 if ui.verbose:
39 if ui.verbose:
40 ui.status(_('adding %s\n') % m.rel(abs))
40 ui.status(_('adding %s\n') % m.rel(abs))
41 names.append(abs)
41 names.append(abs)
42 exacts[abs] = 1
42 exacts[abs] = 1
43 elif abs not in repo.dirstate:
43 elif abs not in repo.dirstate:
44 ui.status(_('adding %s\n') % m.rel(abs))
44 ui.status(_('adding %s\n') % m.rel(abs))
45 names.append(abs)
45 names.append(abs)
46 if not opts.get('dry_run'):
46 if not opts.get('dry_run'):
47 rejected = repo.add(names)
47 rejected = repo.add(names)
48 rejected = [p for p in rejected if p in exacts]
48 rejected = [p for p in rejected if p in exacts]
49 return rejected and 1 or 0
49 return rejected and 1 or 0
50
50
51 def addremove(ui, repo, *pats, **opts):
51 def addremove(ui, repo, *pats, **opts):
52 """add all new files, delete all missing files
52 """add all new files, delete all missing files
53
53
54 Add all new files and remove all missing files from the
54 Add all new files and remove all missing files from the
55 repository.
55 repository.
56
56
57 New files are ignored if they match any of the patterns in
57 New files are ignored if they match any of the patterns in
58 .hgignore. As with add, these changes take effect at the next
58 .hgignore. As with add, these changes take effect at the next
59 commit.
59 commit.
60
60
61 Use the -s/--similarity option to detect renamed files. With a
61 Use the -s/--similarity option to detect renamed files. With a
62 parameter > 0, this compares every removed file with every added
62 parameter > 0, this compares every removed file with every added
63 file and records those similar enough as renames. This option
63 file and records those similar enough as renames. This option
64 takes a percentage between 0 (disabled) and 100 (files must be
64 takes a percentage between 0 (disabled) and 100 (files must be
65 identical) as its parameter. Detecting renamed files this way can
65 identical) as its parameter. Detecting renamed files this way can
66 be expensive.
66 be expensive.
67 """
67 """
68 try:
68 try:
69 sim = float(opts.get('similarity') or 0)
69 sim = float(opts.get('similarity') or 0)
70 except ValueError:
70 except ValueError:
71 raise util.Abort(_('similarity must be a number'))
71 raise util.Abort(_('similarity must be a number'))
72 if sim < 0 or sim > 100:
72 if sim < 0 or sim > 100:
73 raise util.Abort(_('similarity must be between 0 and 100'))
73 raise util.Abort(_('similarity must be between 0 and 100'))
74 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
74 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
75
75
76 def annotate(ui, repo, *pats, **opts):
76 def annotate(ui, repo, *pats, **opts):
77 """show changeset information per file line
77 """show changeset information per file line
78
78
79 List changes in files, showing the revision id responsible for
79 List changes in files, showing the revision id responsible for
80 each line
80 each line
81
81
82 This command is useful to discover who did a change or when a
82 This command is useful to discover who did a change or when a
83 change took place.
83 change took place.
84
84
85 Without the -a/--text option, annotate will avoid processing files
85 Without the -a/--text option, annotate will avoid processing files
86 it detects as binary. With -a, annotate will generate an
86 it detects as binary. With -a, annotate will generate an
87 annotation anyway, probably with undesirable results.
87 annotation anyway, probably with undesirable results.
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 file name or pattern required'))
93 raise util.Abort(_('at least one file name or pattern 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 unversioned archive of a repository revision
139 '''create 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 back out a changeset other than the tip, a new head is
188 If you back out 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 (current one by default).
190 backout changeset with another head (current one by default).
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 back out change on a different branch'))
218 raise util.Abort(_('cannot back out 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 back out a change with no parents'))
222 raise util.Abort(_('cannot back out 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 back out a merge changeset without '
225 raise util.Abort(_('cannot back out 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 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
250 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
251 commit_opts['force_editor'] = True
251 commit_opts['force_editor'] = True
252 commit(ui, repo, **commit_opts)
252 commit(ui, repo, **commit_opts)
253 def nice(node):
253 def nice(node):
254 return '%d:%s' % (repo.changelog.rev(node), short(node))
254 return '%d:%s' % (repo.changelog.rev(node), short(node))
255 ui.status(_('changeset %s backs out changeset %s\n') %
255 ui.status(_('changeset %s backs out changeset %s\n') %
256 (nice(repo.changelog.tip()), nice(node)))
256 (nice(repo.changelog.tip()), nice(node)))
257 if op1 != node:
257 if op1 != node:
258 hg.clean(repo, op1, show_stats=False)
258 hg.clean(repo, op1, show_stats=False)
259 if opts.get('merge'):
259 if opts.get('merge'):
260 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
260 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
261 hg.merge(repo, hex(repo.changelog.tip()))
261 hg.merge(repo, hex(repo.changelog.tip()))
262 else:
262 else:
263 ui.status(_('the backout changeset is a new head - '
263 ui.status(_('the backout changeset is a new head - '
264 'do not forget to merge\n'))
264 'do not forget to merge\n'))
265 ui.status(_('(use "backout --merge" '
265 ui.status(_('(use "backout --merge" '
266 'if you want to auto-merge)\n'))
266 'if you want to auto-merge)\n'))
267
267
268 def bisect(ui, repo, rev=None, extra=None, command=None,
268 def bisect(ui, repo, rev=None, extra=None, command=None,
269 reset=None, good=None, bad=None, skip=None, noupdate=None):
269 reset=None, good=None, bad=None, skip=None, noupdate=None):
270 """subdivision search of changesets
270 """subdivision search of changesets
271
271
272 This command helps to find changesets which introduce problems. To
272 This command helps to find changesets which introduce problems. To
273 use, mark the earliest changeset you know exhibits the problem as
273 use, mark the earliest changeset you know exhibits the problem as
274 bad, then mark the latest changeset which is free from the problem
274 bad, then mark the latest changeset which is free from the problem
275 as good. Bisect will update your working directory to a revision
275 as good. Bisect will update your working directory to a revision
276 for testing (unless the -U/--noupdate option is specified). Once
276 for testing (unless the -U/--noupdate option is specified). Once
277 you have performed tests, mark the working directory as bad or
277 you have performed tests, mark the working directory as bad or
278 good and bisect will either update to another candidate changeset
278 good and bisect will either update to another candidate changeset
279 or announce that it has found the bad revision.
279 or announce that it has found the bad revision.
280
280
281 As a shortcut, you can also use the revision argument to mark a
281 As a shortcut, you can also use the revision argument to mark a
282 revision as good or bad without checking it out first.
282 revision as good or bad without checking it out first.
283
283
284 If you supply a command it will be used for automatic bisection.
284 If you supply a command it will be used for automatic bisection.
285 Its exit status will be used as flag to mark revision as bad or
285 Its exit status will be used as flag to mark revision as bad or
286 good. In case exit status is 0 the revision is marked as good, 125
286 good. In case exit status is 0 the revision is marked as good, 125
287 - skipped, 127 (command not found) - bisection will be aborted;
287 - skipped, 127 (command not found) - bisection will be aborted;
288 any other status bigger than 0 will mark revision as bad.
288 any other status bigger than 0 will mark revision as bad.
289 """
289 """
290 def print_result(nodes, good):
290 def print_result(nodes, good):
291 displayer = cmdutil.show_changeset(ui, repo, {})
291 displayer = cmdutil.show_changeset(ui, repo, {})
292 if len(nodes) == 1:
292 if len(nodes) == 1:
293 # narrowed it down to a single revision
293 # narrowed it down to a single revision
294 if good:
294 if good:
295 ui.write(_("The first good revision is:\n"))
295 ui.write(_("The first good revision is:\n"))
296 else:
296 else:
297 ui.write(_("The first bad revision is:\n"))
297 ui.write(_("The first bad revision is:\n"))
298 displayer.show(repo[nodes[0]])
298 displayer.show(repo[nodes[0]])
299 else:
299 else:
300 # multiple possible revisions
300 # multiple possible revisions
301 if good:
301 if good:
302 ui.write(_("Due to skipped revisions, the first "
302 ui.write(_("Due to skipped revisions, the first "
303 "good revision could be any of:\n"))
303 "good revision could be any of:\n"))
304 else:
304 else:
305 ui.write(_("Due to skipped revisions, the first "
305 ui.write(_("Due to skipped revisions, the first "
306 "bad revision could be any of:\n"))
306 "bad revision could be any of:\n"))
307 for n in nodes:
307 for n in nodes:
308 displayer.show(repo[n])
308 displayer.show(repo[n])
309
309
310 def check_state(state, interactive=True):
310 def check_state(state, interactive=True):
311 if not state['good'] or not state['bad']:
311 if not state['good'] or not state['bad']:
312 if (good or bad or skip or reset) and interactive:
312 if (good or bad or skip or reset) and interactive:
313 return
313 return
314 if not state['good']:
314 if not state['good']:
315 raise util.Abort(_('cannot bisect (no known good revisions)'))
315 raise util.Abort(_('cannot bisect (no known good revisions)'))
316 else:
316 else:
317 raise util.Abort(_('cannot bisect (no known bad revisions)'))
317 raise util.Abort(_('cannot bisect (no known bad revisions)'))
318 return True
318 return True
319
319
320 # backward compatibility
320 # backward compatibility
321 if rev in "good bad reset init".split():
321 if rev in "good bad reset init".split():
322 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
322 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
323 cmd, rev, extra = rev, extra, None
323 cmd, rev, extra = rev, extra, None
324 if cmd == "good":
324 if cmd == "good":
325 good = True
325 good = True
326 elif cmd == "bad":
326 elif cmd == "bad":
327 bad = True
327 bad = True
328 else:
328 else:
329 reset = True
329 reset = True
330 elif extra or good + bad + skip + reset + bool(command) > 1:
330 elif extra or good + bad + skip + reset + bool(command) > 1:
331 raise util.Abort(_('incompatible arguments'))
331 raise util.Abort(_('incompatible arguments'))
332
332
333 if reset:
333 if reset:
334 p = repo.join("bisect.state")
334 p = repo.join("bisect.state")
335 if os.path.exists(p):
335 if os.path.exists(p):
336 os.unlink(p)
336 os.unlink(p)
337 return
337 return
338
338
339 state = hbisect.load_state(repo)
339 state = hbisect.load_state(repo)
340
340
341 if command:
341 if command:
342 commandpath = util.find_exe(command)
342 commandpath = util.find_exe(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 = subprocess.call([commandpath])
347 status = subprocess.call([commandpath])
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 node = repo.lookup(rev or '.')
359 node = repo.lookup(rev or '.')
360 state[transition].append(node)
360 state[transition].append(node)
361 ui.note(_('Changeset %s: %s\n') % (short(node), transition))
361 ui.note(_('Changeset %s: %s\n') % (short(node), 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, not status)
370 return print_result(nodes, not status)
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 %s:%s "
398 ui.write(_("Testing changeset %s:%s "
399 "(%s changesets remaining, ~%s tests)\n")
399 "(%s changesets remaining, ~%s 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 does not exist
409 set the working directory branch name (the branch does not exist
410 in the repository until the next commit). It is recommended to use
410 in the repository until the next commit). It is recommended to use
411 the 'default' branch as your primary development branch.
411 the 'default' branch as your primary development branch.
412
412
413 Unless -f/--force is specified, branch will not let you set a
413 Unless -f/--force is specified, branch will not let you set a
414 branch name that shadows an existing branch.
414 branch name that shadows an existing branch.
415
415
416 Use -C/--clean to reset the working directory branch to that of
416 Use -C/--clean to reset the working directory branch to that of
417 the parent of the working directory, negating a previous branch
417 the parent of the working directory, negating a previous branch
418 change.
418 change.
419
419
420 Use the command 'hg update' to switch to an existing branch.
420 Use the command 'hg update' to switch to an existing branch.
421 """
421 """
422
422
423 if opts.get('clean'):
423 if opts.get('clean'):
424 label = repo[None].parents()[0].branch()
424 label = repo[None].parents()[0].branch()
425 repo.dirstate.setbranch(label)
425 repo.dirstate.setbranch(label)
426 ui.status(_('reset working directory to branch %s\n') % label)
426 ui.status(_('reset working directory to branch %s\n') % label)
427 elif label:
427 elif label:
428 if not opts.get('force') and label in repo.branchtags():
428 if not opts.get('force') and label in repo.branchtags():
429 if label not in [p.branch() for p in repo.parents()]:
429 if label not in [p.branch() for p in repo.parents()]:
430 raise util.Abort(_('a branch of the same name already exists'
430 raise util.Abort(_('a branch of the same name already exists'
431 ' (use --force to override)'))
431 ' (use --force to override)'))
432 repo.dirstate.setbranch(encoding.fromlocal(label))
432 repo.dirstate.setbranch(encoding.fromlocal(label))
433 ui.status(_('marked working directory as branch %s\n') % label)
433 ui.status(_('marked working directory as branch %s\n') % label)
434 else:
434 else:
435 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
435 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
436
436
437 def branches(ui, repo, active=False):
437 def branches(ui, repo, active=False):
438 """list repository named branches
438 """list repository named branches
439
439
440 List the repository's named branches, indicating which ones are
440 List the repository's named branches, indicating which ones are
441 inactive. If active is specified, only show active branches.
441 inactive. If active is specified, only show active branches.
442
442
443 A branch is considered active if it contains repository heads.
443 A branch is considered active if it contains repository heads.
444
444
445 Use the command 'hg update' to switch to an existing branch.
445 Use the command 'hg update' to switch to an existing branch.
446 """
446 """
447 hexfunc = ui.debugflag and hex or short
447 hexfunc = ui.debugflag and hex or short
448 activebranches = [encoding.tolocal(repo[n].branch())
448 activebranches = [encoding.tolocal(repo[n].branch())
449 for n in repo.heads(closed=False)]
449 for n in repo.heads(closed=False)]
450 branches = sorted([(tag in activebranches, repo.changelog.rev(node), tag)
450 branches = sorted([(tag in activebranches, repo.changelog.rev(node), tag)
451 for tag, node in repo.branchtags().items()],
451 for tag, node in repo.branchtags().items()],
452 reverse=True)
452 reverse=True)
453
453
454 for isactive, node, tag in branches:
454 for isactive, node, tag in branches:
455 if (not active) or isactive:
455 if (not active) or isactive:
456 if ui.quiet:
456 if ui.quiet:
457 ui.write("%s\n" % tag)
457 ui.write("%s\n" % tag)
458 else:
458 else:
459 hn = repo.lookup(node)
459 hn = repo.lookup(node)
460 if isactive:
460 if isactive:
461 notice = ''
461 notice = ''
462 elif hn not in repo.branchheads(tag, closed=False):
462 elif hn not in repo.branchheads(tag, closed=False):
463 notice = ' (closed)'
463 notice = ' (closed)'
464 else:
464 else:
465 notice = ' (inactive)'
465 notice = ' (inactive)'
466 rev = str(node).rjust(31 - encoding.colwidth(tag))
466 rev = str(node).rjust(31 - encoding.colwidth(tag))
467 data = tag, rev, hexfunc(hn), notice
467 data = tag, rev, hexfunc(hn), notice
468 ui.write("%s %s:%s%s\n" % data)
468 ui.write("%s %s:%s%s\n" % data)
469
469
470 def bundle(ui, repo, fname, dest=None, **opts):
470 def bundle(ui, repo, fname, dest=None, **opts):
471 """create a changegroup file
471 """create a changegroup file
472
472
473 Generate a compressed changegroup file collecting changesets not
473 Generate a compressed changegroup file collecting changesets not
474 known to be in another repository.
474 known to be in another repository.
475
475
476 If no destination repository is specified the destination is
476 If no destination repository is specified the destination is
477 assumed to have all the nodes specified by one or more --base
477 assumed to have all the nodes specified by one or more --base
478 parameters. To create a bundle containing all changesets, use
478 parameters. To create a bundle containing all changesets, use
479 -a/--all (or --base null). To change the compression method
479 -a/--all (or --base null). To change the compression method
480 applied, use the -t/--type option (by default, bundles are
480 applied, use the -t/--type option (by default, bundles are
481 compressed using bz2).
481 compressed using bz2).
482
482
483 The bundle file can then be transferred using conventional means
483 The bundle file can then be transferred using conventional means
484 and applied to another repository with the unbundle or pull
484 and applied to another repository with the unbundle or pull
485 command. This is useful when direct push and pull are not
485 command. This is useful when direct push and pull are not
486 available or when exporting an entire repository is undesirable.
486 available or when exporting an entire repository is undesirable.
487
487
488 Applying bundles preserves all changeset contents including
488 Applying bundles preserves all changeset contents including
489 permissions, copy/rename information, and revision history.
489 permissions, copy/rename information, and revision history.
490 """
490 """
491 revs = opts.get('rev') or None
491 revs = opts.get('rev') or None
492 if revs:
492 if revs:
493 revs = [repo.lookup(rev) for rev in revs]
493 revs = [repo.lookup(rev) for rev in revs]
494 if opts.get('all'):
494 if opts.get('all'):
495 base = ['null']
495 base = ['null']
496 else:
496 else:
497 base = opts.get('base')
497 base = opts.get('base')
498 if base:
498 if base:
499 if dest:
499 if dest:
500 raise util.Abort(_("--base is incompatible with specifiying "
500 raise util.Abort(_("--base is incompatible with specifiying "
501 "a destination"))
501 "a destination"))
502 base = [repo.lookup(rev) for rev in base]
502 base = [repo.lookup(rev) for rev in base]
503 # create the right base
503 # create the right base
504 # XXX: nodesbetween / changegroup* should be "fixed" instead
504 # XXX: nodesbetween / changegroup* should be "fixed" instead
505 o = []
505 o = []
506 has = {nullid: None}
506 has = {nullid: None}
507 for n in base:
507 for n in base:
508 has.update(repo.changelog.reachable(n))
508 has.update(repo.changelog.reachable(n))
509 if revs:
509 if revs:
510 visit = list(revs)
510 visit = list(revs)
511 else:
511 else:
512 visit = repo.changelog.heads()
512 visit = repo.changelog.heads()
513 seen = {}
513 seen = {}
514 while visit:
514 while visit:
515 n = visit.pop(0)
515 n = visit.pop(0)
516 parents = [p for p in repo.changelog.parents(n) if p not in has]
516 parents = [p for p in repo.changelog.parents(n) if p not in has]
517 if len(parents) == 0:
517 if len(parents) == 0:
518 o.insert(0, n)
518 o.insert(0, n)
519 else:
519 else:
520 for p in parents:
520 for p in parents:
521 if p not in seen:
521 if p not in seen:
522 seen[p] = 1
522 seen[p] = 1
523 visit.append(p)
523 visit.append(p)
524 else:
524 else:
525 dest, revs, checkout = hg.parseurl(
525 dest, revs, checkout = hg.parseurl(
526 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
526 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
527 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
527 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
528 o = repo.findoutgoing(other, force=opts.get('force'))
528 o = repo.findoutgoing(other, force=opts.get('force'))
529
529
530 if revs:
530 if revs:
531 cg = repo.changegroupsubset(o, revs, 'bundle')
531 cg = repo.changegroupsubset(o, revs, 'bundle')
532 else:
532 else:
533 cg = repo.changegroup(o, 'bundle')
533 cg = repo.changegroup(o, 'bundle')
534
534
535 bundletype = opts.get('type', 'bzip2').lower()
535 bundletype = opts.get('type', 'bzip2').lower()
536 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
536 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
537 bundletype = btypes.get(bundletype)
537 bundletype = btypes.get(bundletype)
538 if bundletype not in changegroup.bundletypes:
538 if bundletype not in changegroup.bundletypes:
539 raise util.Abort(_('unknown bundle type specified with --type'))
539 raise util.Abort(_('unknown bundle type specified with --type'))
540
540
541 changegroup.writebundle(cg, fname, bundletype)
541 changegroup.writebundle(cg, fname, bundletype)
542
542
543 def cat(ui, repo, file1, *pats, **opts):
543 def cat(ui, repo, file1, *pats, **opts):
544 """output the current or given revision of files
544 """output the current or given revision of files
545
545
546 Print the specified files as they were at the given revision. If
546 Print the specified files as they were at the given revision. If
547 no revision is given, the parent of the working directory is used,
547 no revision is given, the parent of the working directory is used,
548 or tip if no revision is checked out.
548 or tip if no revision is checked out.
549
549
550 Output may be to a file, in which case the name of the file is
550 Output may be to a file, in which case the name of the file is
551 given using a format string. The formatting rules are the same as
551 given using a format string. The formatting rules are the same as
552 for the export command, with the following additions:
552 for the export command, with the following additions:
553
553
554 %s basename of file being printed
554 %s basename of file being printed
555 %d dirname of file being printed, or '.' if in repository root
555 %d dirname of file being printed, or '.' if in repository root
556 %p root-relative path name of file being printed
556 %p root-relative path name of file being printed
557 """
557 """
558 ctx = repo[opts.get('rev')]
558 ctx = repo[opts.get('rev')]
559 err = 1
559 err = 1
560 m = cmdutil.match(repo, (file1,) + pats, opts)
560 m = cmdutil.match(repo, (file1,) + pats, opts)
561 for abs in ctx.walk(m):
561 for abs in ctx.walk(m):
562 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
562 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
563 data = ctx[abs].data()
563 data = ctx[abs].data()
564 if opts.get('decode'):
564 if opts.get('decode'):
565 data = repo.wwritedata(abs, data)
565 data = repo.wwritedata(abs, data)
566 fp.write(data)
566 fp.write(data)
567 err = 0
567 err = 0
568 return err
568 return err
569
569
570 def clone(ui, source, dest=None, **opts):
570 def clone(ui, source, dest=None, **opts):
571 """make a copy of an existing repository
571 """make a copy of an existing repository
572
572
573 Create a copy of an existing repository in a new directory.
573 Create a copy of an existing repository in a new directory.
574
574
575 If no destination directory name is specified, it defaults to the
575 If no destination directory name is specified, it defaults to the
576 basename of the source.
576 basename of the source.
577
577
578 The location of the source is added to the new repository's
578 The location of the source is added to the new repository's
579 .hg/hgrc file, as the default to be used for future pulls.
579 .hg/hgrc file, as the default to be used for future pulls.
580
580
581 If you use the -r/--rev option to clone up to a specific revision,
581 If you use the -r/--rev option to clone up to a specific revision,
582 no subsequent revisions (including subsequent tags) will be
582 no subsequent revisions (including subsequent tags) will be
583 present in the cloned repository. This option implies --pull, even
583 present in the cloned repository. This option implies --pull, even
584 on local repositories.
584 on local repositories.
585
585
586 By default, clone will check out the head of the 'default' branch.
586 By default, clone will check out the head of the 'default' branch.
587 If the -U/--noupdate option is used, the new clone will contain
587 If the -U/--noupdate option is used, the new clone will contain
588 only a repository (.hg) and no working copy (the working copy
588 only a repository (.hg) and no working copy (the working copy
589 parent is the null revision).
589 parent is the null revision).
590
590
591 See 'hg help urls' for valid source format details.
591 See 'hg help urls' for valid source format details.
592
592
593 It is possible to specify an ssh:// URL as the destination, but no
593 It is possible to specify an ssh:// URL as the destination, but no
594 .hg/hgrc and working directory will be created on the remote side.
594 .hg/hgrc and working directory will be created on the remote side.
595 Look at the help text for URLs for important details about ssh://
595 Look at the help text for URLs for important details about ssh://
596 URLs.
596 URLs.
597
597
598 For efficiency, hardlinks are used for cloning whenever the source
598 For efficiency, hardlinks are used for cloning whenever the source
599 and destination are on the same filesystem (note this applies only
599 and destination are on the same filesystem (note this applies only
600 to the repository data, not to the checked out files). Some
600 to the repository data, not to the checked out files). Some
601 filesystems, such as AFS, implement hardlinking incorrectly, but
601 filesystems, such as AFS, implement hardlinking incorrectly, but
602 do not report errors. In these cases, use the --pull option to
602 do not report errors. In these cases, use the --pull option to
603 avoid hardlinking.
603 avoid hardlinking.
604
604
605 In some cases, you can clone repositories and checked out files
605 In some cases, you can clone repositories and checked out files
606 using full hardlinks with
606 using full hardlinks with
607
607
608 $ cp -al REPO REPOCLONE
608 $ cp -al REPO REPOCLONE
609
609
610 This is the fastest way to clone, but it is not always safe. The
610 This is the fastest way to clone, but it is not always safe. The
611 operation is not atomic (making sure REPO is not modified during
611 operation is not atomic (making sure REPO is not modified during
612 the operation is up to you) and you have to make sure your editor
612 the operation is up to you) and you have to make sure your editor
613 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
613 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
614 this is not compatible with certain extensions that place their
614 this is not compatible with certain extensions that place their
615 metadata under the .hg directory, such as mq.
615 metadata under the .hg directory, such as mq.
616
616
617 """
617 """
618 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
618 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
619 pull=opts.get('pull'),
619 pull=opts.get('pull'),
620 stream=opts.get('uncompressed'),
620 stream=opts.get('uncompressed'),
621 rev=opts.get('rev'),
621 rev=opts.get('rev'),
622 update=not opts.get('noupdate'))
622 update=not opts.get('noupdate'))
623
623
624 def commit(ui, repo, *pats, **opts):
624 def commit(ui, repo, *pats, **opts):
625 """commit the specified files or all outstanding changes
625 """commit the specified files or all outstanding changes
626
626
627 Commit changes to the given files into the repository. Unlike a
627 Commit changes to the given files into the repository. Unlike a
628 centralized RCS, this operation is a local operation. See hg push
628 centralized RCS, this operation is a local operation. See hg push
629 for means to actively distribute your changes.
629 for means to actively distribute your changes.
630
630
631 If a list of files is omitted, all changes reported by "hg status"
631 If a list of files is omitted, all changes reported by "hg status"
632 will be committed.
632 will be committed.
633
633
634 If you are committing the result of a merge, do not provide any
634 If you are committing the result of a merge, do not provide any
635 file names or -I/-X filters.
635 file names or -I/-X filters.
636
636
637 If no commit message is specified, the configured editor is
637 If no commit message is specified, the configured editor is
638 started to prompt you for a message.
638 started to prompt you for a message.
639
639
640 See 'hg help dates' for a list of formats valid for -d/--date.
640 See 'hg help dates' for a list of formats valid for -d/--date.
641 """
641 """
642 extra = {}
642 extra = {}
643 if opts.get('close_branch'):
643 if opts.get('close_branch'):
644 extra['close'] = 1
644 extra['close'] = 1
645 def commitfunc(ui, repo, message, match, opts):
645 def commitfunc(ui, repo, message, match, opts):
646 return repo.commit(match.files(), message, opts.get('user'),
646 return repo.commit(match.files(), message, opts.get('user'),
647 opts.get('date'), match, force_editor=opts.get('force_editor'),
647 opts.get('date'), match, force_editor=opts.get('force_editor'),
648 extra=extra)
648 extra=extra)
649
649
650 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
650 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
651 if not node:
651 if not node:
652 return
652 return
653 cl = repo.changelog
653 cl = repo.changelog
654 rev = cl.rev(node)
654 rev = cl.rev(node)
655 parents = cl.parentrevs(rev)
655 parents = cl.parentrevs(rev)
656 if rev - 1 in parents:
656 if rev - 1 in parents:
657 # one of the parents was the old tip
657 # one of the parents was the old tip
658 pass
658 pass
659 elif (parents == (nullrev, nullrev) or
659 elif (parents == (nullrev, nullrev) or
660 len(cl.heads(cl.node(parents[0]))) > 1 and
660 len(cl.heads(cl.node(parents[0]))) > 1 and
661 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
661 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
662 ui.status(_('created new head\n'))
662 ui.status(_('created new head\n'))
663
663
664 if ui.debugflag:
664 if ui.debugflag:
665 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
665 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
666 elif ui.verbose:
666 elif ui.verbose:
667 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
667 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
668
668
669 def copy(ui, repo, *pats, **opts):
669 def copy(ui, repo, *pats, **opts):
670 """mark files as copied for the next commit
670 """mark files as copied for the next commit
671
671
672 Mark dest as having copies of source files. If dest is a
672 Mark dest as having copies of source files. If dest is a
673 directory, copies are put in that directory. If dest is a file,
673 directory, copies are put in that directory. If dest is a file,
674 the source must be a single file.
674 the source must be a single file.
675
675
676 By default, this command copies the contents of files as they
676 By default, this command copies the contents of files as they
677 stand in the working directory. If invoked with -A/--after, the
677 stand in the working directory. If invoked with -A/--after, the
678 operation is recorded, but no copying is performed.
678 operation is recorded, but no copying is performed.
679
679
680 This command takes effect with the next commit. To undo a copy
680 This command takes effect with the next commit. To undo a copy
681 before that, see hg revert.
681 before that, see hg revert.
682 """
682 """
683 wlock = repo.wlock(False)
683 wlock = repo.wlock(False)
684 try:
684 try:
685 return cmdutil.copy(ui, repo, pats, opts)
685 return cmdutil.copy(ui, repo, pats, opts)
686 finally:
686 finally:
687 wlock.release()
687 wlock.release()
688
688
689 def debugancestor(ui, repo, *args):
689 def debugancestor(ui, repo, *args):
690 """find the ancestor revision of two revisions in a given index"""
690 """find the ancestor revision of two revisions in a given index"""
691 if len(args) == 3:
691 if len(args) == 3:
692 index, rev1, rev2 = args
692 index, rev1, rev2 = args
693 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
693 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
694 lookup = r.lookup
694 lookup = r.lookup
695 elif len(args) == 2:
695 elif len(args) == 2:
696 if not repo:
696 if not repo:
697 raise util.Abort(_("There is no Mercurial repository here "
697 raise util.Abort(_("There is no Mercurial repository here "
698 "(.hg not found)"))
698 "(.hg not found)"))
699 rev1, rev2 = args
699 rev1, rev2 = args
700 r = repo.changelog
700 r = repo.changelog
701 lookup = repo.lookup
701 lookup = repo.lookup
702 else:
702 else:
703 raise util.Abort(_('either two or three arguments required'))
703 raise util.Abort(_('either two or three arguments required'))
704 a = r.ancestor(lookup(rev1), lookup(rev2))
704 a = r.ancestor(lookup(rev1), lookup(rev2))
705 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
705 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
706
706
707 def debugcommands(ui, cmd='', *args):
707 def debugcommands(ui, cmd='', *args):
708 for cmd, vals in sorted(table.iteritems()):
708 for cmd, vals in sorted(table.iteritems()):
709 cmd = cmd.split('|')[0].strip('^')
709 cmd = cmd.split('|')[0].strip('^')
710 opts = ', '.join([i[1] for i in vals[1]])
710 opts = ', '.join([i[1] for i in vals[1]])
711 ui.write('%s: %s\n' % (cmd, opts))
711 ui.write('%s: %s\n' % (cmd, opts))
712
712
713 def debugcomplete(ui, cmd='', **opts):
713 def debugcomplete(ui, cmd='', **opts):
714 """returns the completion list associated with the given command"""
714 """returns the completion list associated with the given command"""
715
715
716 if opts.get('options'):
716 if opts.get('options'):
717 options = []
717 options = []
718 otables = [globalopts]
718 otables = [globalopts]
719 if cmd:
719 if cmd:
720 aliases, entry = cmdutil.findcmd(cmd, table, False)
720 aliases, entry = cmdutil.findcmd(cmd, table, False)
721 otables.append(entry[1])
721 otables.append(entry[1])
722 for t in otables:
722 for t in otables:
723 for o in t:
723 for o in t:
724 if o[0]:
724 if o[0]:
725 options.append('-%s' % o[0])
725 options.append('-%s' % o[0])
726 options.append('--%s' % o[1])
726 options.append('--%s' % o[1])
727 ui.write("%s\n" % "\n".join(options))
727 ui.write("%s\n" % "\n".join(options))
728 return
728 return
729
729
730 cmdlist = cmdutil.findpossible(cmd, table)
730 cmdlist = cmdutil.findpossible(cmd, table)
731 if ui.verbose:
731 if ui.verbose:
732 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
732 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
733 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
733 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
734
734
735 def debugfsinfo(ui, path = "."):
735 def debugfsinfo(ui, path = "."):
736 file('.debugfsinfo', 'w').write('')
736 file('.debugfsinfo', 'w').write('')
737 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
737 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
738 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
738 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
739 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
739 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
740 and 'yes' or 'no'))
740 and 'yes' or 'no'))
741 os.unlink('.debugfsinfo')
741 os.unlink('.debugfsinfo')
742
742
743 def debugrebuildstate(ui, repo, rev="tip"):
743 def debugrebuildstate(ui, repo, rev="tip"):
744 """rebuild the dirstate as it would look like for the given revision"""
744 """rebuild the dirstate as it would look like for the given revision"""
745 ctx = repo[rev]
745 ctx = repo[rev]
746 wlock = repo.wlock()
746 wlock = repo.wlock()
747 try:
747 try:
748 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
748 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
749 finally:
749 finally:
750 wlock.release()
750 wlock.release()
751
751
752 def debugcheckstate(ui, repo):
752 def debugcheckstate(ui, repo):
753 """validate the correctness of the current dirstate"""
753 """validate the correctness of the current dirstate"""
754 parent1, parent2 = repo.dirstate.parents()
754 parent1, parent2 = repo.dirstate.parents()
755 m1 = repo[parent1].manifest()
755 m1 = repo[parent1].manifest()
756 m2 = repo[parent2].manifest()
756 m2 = repo[parent2].manifest()
757 errors = 0
757 errors = 0
758 for f in repo.dirstate:
758 for f in repo.dirstate:
759 state = repo.dirstate[f]
759 state = repo.dirstate[f]
760 if state in "nr" and f not in m1:
760 if state in "nr" and f not in m1:
761 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
761 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
762 errors += 1
762 errors += 1
763 if state in "a" and f in m1:
763 if state in "a" and f in m1:
764 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
764 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
765 errors += 1
765 errors += 1
766 if state in "m" and f not in m1 and f not in m2:
766 if state in "m" and f not in m1 and f not in m2:
767 ui.warn(_("%s in state %s, but not in either manifest\n") %
767 ui.warn(_("%s in state %s, but not in either manifest\n") %
768 (f, state))
768 (f, state))
769 errors += 1
769 errors += 1
770 for f in m1:
770 for f in m1:
771 state = repo.dirstate[f]
771 state = repo.dirstate[f]
772 if state not in "nrm":
772 if state not in "nrm":
773 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
773 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
774 errors += 1
774 errors += 1
775 if errors:
775 if errors:
776 error = _(".hg/dirstate inconsistent with current parent's manifest")
776 error = _(".hg/dirstate inconsistent with current parent's manifest")
777 raise util.Abort(error)
777 raise util.Abort(error)
778
778
779 def showconfig(ui, repo, *values, **opts):
779 def showconfig(ui, repo, *values, **opts):
780 """show combined config settings from all hgrc files
780 """show combined config settings from all hgrc files
781
781
782 With no args, print names and values of all config items.
782 With no args, print names and values of all config items.
783
783
784 With one arg of the form section.name, print just the value of
784 With one arg of the form section.name, print just the value of
785 that config item.
785 that config item.
786
786
787 With multiple args, print names and values of all config items
787 With multiple args, print names and values of all config items
788 with matching section names.
788 with matching section names.
789
789
790 With the --debug flag, the source (filename and line number) is
790 With the --debug flag, the source (filename and line number) is
791 printed for each config item.
791 printed for each config item.
792 """
792 """
793
793
794 untrusted = bool(opts.get('untrusted'))
794 untrusted = bool(opts.get('untrusted'))
795 if values:
795 if values:
796 if len([v for v in values if '.' in v]) > 1:
796 if len([v for v in values if '.' in v]) > 1:
797 raise util.Abort(_('only one config item permitted'))
797 raise util.Abort(_('only one config item permitted'))
798 for section, name, value in ui.walkconfig(untrusted=untrusted):
798 for section, name, value in ui.walkconfig(untrusted=untrusted):
799 sectname = section + '.' + name
799 sectname = section + '.' + name
800 if values:
800 if values:
801 for v in values:
801 for v in values:
802 if v == section:
802 if v == section:
803 ui.debug('%s: ' %
803 ui.debug('%s: ' %
804 ui.configsource(section, name, untrusted))
804 ui.configsource(section, name, untrusted))
805 ui.write('%s=%s\n' % (sectname, value))
805 ui.write('%s=%s\n' % (sectname, value))
806 elif v == sectname:
806 elif v == sectname:
807 ui.debug('%s: ' %
807 ui.debug('%s: ' %
808 ui.configsource(section, name, untrusted))
808 ui.configsource(section, name, untrusted))
809 ui.write(value, '\n')
809 ui.write(value, '\n')
810 else:
810 else:
811 ui.debug('%s: ' %
811 ui.debug('%s: ' %
812 ui.configsource(section, name, untrusted))
812 ui.configsource(section, name, untrusted))
813 ui.write('%s=%s\n' % (sectname, value))
813 ui.write('%s=%s\n' % (sectname, value))
814
814
815 def debugsetparents(ui, repo, rev1, rev2=None):
815 def debugsetparents(ui, repo, rev1, rev2=None):
816 """manually set the parents of the current working directory
816 """manually set the parents of the current working directory
817
817
818 This is useful for writing repository conversion tools, but should
818 This is useful for writing repository conversion tools, but should
819 be used with care.
819 be used with care.
820 """
820 """
821
821
822 if not rev2:
822 if not rev2:
823 rev2 = hex(nullid)
823 rev2 = hex(nullid)
824
824
825 wlock = repo.wlock()
825 wlock = repo.wlock()
826 try:
826 try:
827 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
827 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
828 finally:
828 finally:
829 wlock.release()
829 wlock.release()
830
830
831 def debugstate(ui, repo, nodates=None):
831 def debugstate(ui, repo, nodates=None):
832 """show the contents of the current dirstate"""
832 """show the contents of the current dirstate"""
833 timestr = ""
833 timestr = ""
834 showdate = not nodates
834 showdate = not nodates
835 for file_, ent in sorted(repo.dirstate._map.iteritems()):
835 for file_, ent in sorted(repo.dirstate._map.iteritems()):
836 if showdate:
836 if showdate:
837 if ent[3] == -1:
837 if ent[3] == -1:
838 # Pad or slice to locale representation
838 # Pad or slice to locale representation
839 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
839 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
840 timestr = 'unset'
840 timestr = 'unset'
841 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
841 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
842 else:
842 else:
843 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
843 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
844 if ent[1] & 020000:
844 if ent[1] & 020000:
845 mode = 'lnk'
845 mode = 'lnk'
846 else:
846 else:
847 mode = '%3o' % (ent[1] & 0777)
847 mode = '%3o' % (ent[1] & 0777)
848 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
848 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
849 for f in repo.dirstate.copies():
849 for f in repo.dirstate.copies():
850 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
850 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
851
851
852 def debugdata(ui, file_, rev):
852 def debugdata(ui, file_, rev):
853 """dump the contents of a data file revision"""
853 """dump the contents of a data file revision"""
854 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
854 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
855 try:
855 try:
856 ui.write(r.revision(r.lookup(rev)))
856 ui.write(r.revision(r.lookup(rev)))
857 except KeyError:
857 except KeyError:
858 raise util.Abort(_('invalid revision identifier %s') % rev)
858 raise util.Abort(_('invalid revision identifier %s') % rev)
859
859
860 def debugdate(ui, date, range=None, **opts):
860 def debugdate(ui, date, range=None, **opts):
861 """parse and display a date"""
861 """parse and display a date"""
862 if opts["extended"]:
862 if opts["extended"]:
863 d = util.parsedate(date, util.extendeddateformats)
863 d = util.parsedate(date, util.extendeddateformats)
864 else:
864 else:
865 d = util.parsedate(date)
865 d = util.parsedate(date)
866 ui.write("internal: %s %s\n" % d)
866 ui.write("internal: %s %s\n" % d)
867 ui.write("standard: %s\n" % util.datestr(d))
867 ui.write("standard: %s\n" % util.datestr(d))
868 if range:
868 if range:
869 m = util.matchdate(range)
869 m = util.matchdate(range)
870 ui.write("match: %s\n" % m(d[0]))
870 ui.write("match: %s\n" % m(d[0]))
871
871
872 def debugindex(ui, file_):
872 def debugindex(ui, file_):
873 """dump the contents of an index file"""
873 """dump the contents of an index file"""
874 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
874 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
875 ui.write(" rev offset length base linkrev"
875 ui.write(" rev offset length base linkrev"
876 " nodeid p1 p2\n")
876 " nodeid p1 p2\n")
877 for i in r:
877 for i in r:
878 node = r.node(i)
878 node = r.node(i)
879 try:
879 try:
880 pp = r.parents(node)
880 pp = r.parents(node)
881 except:
881 except:
882 pp = [nullid, nullid]
882 pp = [nullid, nullid]
883 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
883 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
884 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
884 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
885 short(node), short(pp[0]), short(pp[1])))
885 short(node), short(pp[0]), short(pp[1])))
886
886
887 def debugindexdot(ui, file_):
887 def debugindexdot(ui, file_):
888 """dump an index DAG as a .dot file"""
888 """dump an index DAG as a .dot file"""
889 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
889 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
890 ui.write("digraph G {\n")
890 ui.write("digraph G {\n")
891 for i in r:
891 for i in r:
892 node = r.node(i)
892 node = r.node(i)
893 pp = r.parents(node)
893 pp = r.parents(node)
894 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
894 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
895 if pp[1] != nullid:
895 if pp[1] != nullid:
896 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
896 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
897 ui.write("}\n")
897 ui.write("}\n")
898
898
899 def debuginstall(ui):
899 def debuginstall(ui):
900 '''test Mercurial installation'''
900 '''test Mercurial installation'''
901
901
902 def writetemp(contents):
902 def writetemp(contents):
903 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
903 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
904 f = os.fdopen(fd, "wb")
904 f = os.fdopen(fd, "wb")
905 f.write(contents)
905 f.write(contents)
906 f.close()
906 f.close()
907 return name
907 return name
908
908
909 problems = 0
909 problems = 0
910
910
911 # encoding
911 # encoding
912 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
912 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
913 try:
913 try:
914 encoding.fromlocal("test")
914 encoding.fromlocal("test")
915 except util.Abort, inst:
915 except util.Abort, inst:
916 ui.write(" %s\n" % inst)
916 ui.write(" %s\n" % inst)
917 ui.write(_(" (check that your locale is properly set)\n"))
917 ui.write(_(" (check that your locale is properly set)\n"))
918 problems += 1
918 problems += 1
919
919
920 # compiled modules
920 # compiled modules
921 ui.status(_("Checking extensions...\n"))
921 ui.status(_("Checking extensions...\n"))
922 try:
922 try:
923 import bdiff, mpatch, base85
923 import bdiff, mpatch, base85
924 except Exception, inst:
924 except Exception, inst:
925 ui.write(" %s\n" % inst)
925 ui.write(" %s\n" % inst)
926 ui.write(_(" One or more extensions could not be found"))
926 ui.write(_(" One or more extensions could not be found"))
927 ui.write(_(" (check that you compiled the extensions)\n"))
927 ui.write(_(" (check that you compiled the extensions)\n"))
928 problems += 1
928 problems += 1
929
929
930 # templates
930 # templates
931 ui.status(_("Checking templates...\n"))
931 ui.status(_("Checking templates...\n"))
932 try:
932 try:
933 import templater
933 import templater
934 templater.templater(templater.templatepath("map-cmdline.default"))
934 templater.templater(templater.templatepath("map-cmdline.default"))
935 except Exception, inst:
935 except Exception, inst:
936 ui.write(" %s\n" % inst)
936 ui.write(" %s\n" % inst)
937 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
937 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
938 problems += 1
938 problems += 1
939
939
940 # patch
940 # patch
941 ui.status(_("Checking patch...\n"))
941 ui.status(_("Checking patch...\n"))
942 patchproblems = 0
942 patchproblems = 0
943 a = "1\n2\n3\n4\n"
943 a = "1\n2\n3\n4\n"
944 b = "1\n2\n3\ninsert\n4\n"
944 b = "1\n2\n3\ninsert\n4\n"
945 fa = writetemp(a)
945 fa = writetemp(a)
946 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
946 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
947 os.path.basename(fa))
947 os.path.basename(fa))
948 fd = writetemp(d)
948 fd = writetemp(d)
949
949
950 files = {}
950 files = {}
951 try:
951 try:
952 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
952 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
953 except util.Abort, e:
953 except util.Abort, e:
954 ui.write(_(" patch call failed:\n"))
954 ui.write(_(" patch call failed:\n"))
955 ui.write(" " + str(e) + "\n")
955 ui.write(" " + str(e) + "\n")
956 patchproblems += 1
956 patchproblems += 1
957 else:
957 else:
958 if list(files) != [os.path.basename(fa)]:
958 if list(files) != [os.path.basename(fa)]:
959 ui.write(_(" unexpected patch output!\n"))
959 ui.write(_(" unexpected patch output!\n"))
960 patchproblems += 1
960 patchproblems += 1
961 a = file(fa).read()
961 a = file(fa).read()
962 if a != b:
962 if a != b:
963 ui.write(_(" patch test failed!\n"))
963 ui.write(_(" patch test failed!\n"))
964 patchproblems += 1
964 patchproblems += 1
965
965
966 if patchproblems:
966 if patchproblems:
967 if ui.config('ui', 'patch'):
967 if ui.config('ui', 'patch'):
968 ui.write(_(" (Current patch tool may be incompatible with patch,"
968 ui.write(_(" (Current patch tool may be incompatible with patch,"
969 " or misconfigured. Please check your .hgrc file)\n"))
969 " or misconfigured. Please check your .hgrc file)\n"))
970 else:
970 else:
971 ui.write(_(" Internal patcher failure, please report this error"
971 ui.write(_(" Internal patcher failure, please report this error"
972 " to http://www.selenic.com/mercurial/bts\n"))
972 " to http://www.selenic.com/mercurial/bts\n"))
973 problems += patchproblems
973 problems += patchproblems
974
974
975 os.unlink(fa)
975 os.unlink(fa)
976 os.unlink(fd)
976 os.unlink(fd)
977
977
978 # editor
978 # editor
979 ui.status(_("Checking commit editor...\n"))
979 ui.status(_("Checking commit editor...\n"))
980 editor = ui.geteditor()
980 editor = ui.geteditor()
981 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
981 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
982 if not cmdpath:
982 if not cmdpath:
983 if editor == 'vi':
983 if editor == 'vi':
984 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
984 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
985 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
985 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
986 else:
986 else:
987 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
987 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
988 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
988 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
989 problems += 1
989 problems += 1
990
990
991 # check username
991 # check username
992 ui.status(_("Checking username...\n"))
992 ui.status(_("Checking username...\n"))
993 user = os.environ.get("HGUSER")
993 user = os.environ.get("HGUSER")
994 if user is None:
994 if user is None:
995 user = ui.config("ui", "username")
995 user = ui.config("ui", "username")
996 if user is None:
996 if user is None:
997 user = os.environ.get("EMAIL")
997 user = os.environ.get("EMAIL")
998 if not user:
998 if not user:
999 ui.warn(" ")
999 ui.warn(" ")
1000 ui.username()
1000 ui.username()
1001 ui.write(_(" (specify a username in your .hgrc file)\n"))
1001 ui.write(_(" (specify a username in your .hgrc file)\n"))
1002
1002
1003 if not problems:
1003 if not problems:
1004 ui.status(_("No problems detected\n"))
1004 ui.status(_("No problems detected\n"))
1005 else:
1005 else:
1006 ui.write(_("%s problems detected,"
1006 ui.write(_("%s problems detected,"
1007 " please check your install!\n") % problems)
1007 " please check your install!\n") % problems)
1008
1008
1009 return problems
1009 return problems
1010
1010
1011 def debugrename(ui, repo, file1, *pats, **opts):
1011 def debugrename(ui, repo, file1, *pats, **opts):
1012 """dump rename information"""
1012 """dump rename information"""
1013
1013
1014 ctx = repo[opts.get('rev')]
1014 ctx = repo[opts.get('rev')]
1015 m = cmdutil.match(repo, (file1,) + pats, opts)
1015 m = cmdutil.match(repo, (file1,) + pats, opts)
1016 for abs in ctx.walk(m):
1016 for abs in ctx.walk(m):
1017 fctx = ctx[abs]
1017 fctx = ctx[abs]
1018 o = fctx.filelog().renamed(fctx.filenode())
1018 o = fctx.filelog().renamed(fctx.filenode())
1019 rel = m.rel(abs)
1019 rel = m.rel(abs)
1020 if o:
1020 if o:
1021 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1021 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1022 else:
1022 else:
1023 ui.write(_("%s not renamed\n") % rel)
1023 ui.write(_("%s not renamed\n") % rel)
1024
1024
1025 def debugwalk(ui, repo, *pats, **opts):
1025 def debugwalk(ui, repo, *pats, **opts):
1026 """show how files match on given patterns"""
1026 """show how files match on given patterns"""
1027 m = cmdutil.match(repo, pats, opts)
1027 m = cmdutil.match(repo, pats, opts)
1028 items = list(repo.walk(m))
1028 items = list(repo.walk(m))
1029 if not items:
1029 if not items:
1030 return
1030 return
1031 fmt = 'f %%-%ds %%-%ds %%s' % (
1031 fmt = 'f %%-%ds %%-%ds %%s' % (
1032 max([len(abs) for abs in items]),
1032 max([len(abs) for abs in items]),
1033 max([len(m.rel(abs)) for abs in items]))
1033 max([len(m.rel(abs)) for abs in items]))
1034 for abs in items:
1034 for abs in items:
1035 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1035 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1036 ui.write("%s\n" % line.rstrip())
1036 ui.write("%s\n" % line.rstrip())
1037
1037
1038 def diff(ui, repo, *pats, **opts):
1038 def diff(ui, repo, *pats, **opts):
1039 """diff repository (or selected files)
1039 """diff repository (or selected files)
1040
1040
1041 Show differences between revisions for the specified files.
1041 Show differences between revisions for the specified files.
1042
1042
1043 Differences between files are shown using the unified diff format.
1043 Differences between files are shown using the unified diff format.
1044
1044
1045 NOTE: diff may generate unexpected results for merges, as it will
1045 NOTE: diff may generate unexpected results for merges, as it will
1046 default to comparing against the working directory's first parent
1046 default to comparing against the working directory's first parent
1047 changeset if no revisions are specified.
1047 changeset if no revisions are specified.
1048
1048
1049 When two revision arguments are given, then changes are shown
1049 When two revision arguments are given, then changes are shown
1050 between those revisions. If only one revision is specified then
1050 between those revisions. If only one revision is specified then
1051 that revision is compared to the working directory, and, when no
1051 that revision is compared to the working directory, and, when no
1052 revisions are specified, the working directory files are compared
1052 revisions are specified, the working directory files are compared
1053 to its parent.
1053 to its parent.
1054
1054
1055 Without the -a/--text option, diff will avoid generating diffs of
1055 Without the -a/--text option, diff will avoid generating diffs of
1056 files it detects as binary. With -a, diff will generate a diff
1056 files it detects as binary. With -a, diff will generate a diff
1057 anyway, probably with undesirable results.
1057 anyway, probably with undesirable results.
1058
1058
1059 Use the -g/--git option to generate diffs in the git extended diff
1059 Use the -g/--git option to generate diffs in the git extended diff
1060 format. For more information, read 'hg help diffs'.
1060 format. For more information, read 'hg help diffs'.
1061 """
1061 """
1062
1062
1063 revs = opts.get('rev')
1063 revs = opts.get('rev')
1064 change = opts.get('change')
1064 change = opts.get('change')
1065
1065
1066 if revs and change:
1066 if revs and change:
1067 msg = _('cannot specify --rev and --change at the same time')
1067 msg = _('cannot specify --rev and --change at the same time')
1068 raise util.Abort(msg)
1068 raise util.Abort(msg)
1069 elif change:
1069 elif change:
1070 node2 = repo.lookup(change)
1070 node2 = repo.lookup(change)
1071 node1 = repo[node2].parents()[0].node()
1071 node1 = repo[node2].parents()[0].node()
1072 else:
1072 else:
1073 node1, node2 = cmdutil.revpair(repo, revs)
1073 node1, node2 = cmdutil.revpair(repo, revs)
1074
1074
1075 m = cmdutil.match(repo, pats, opts)
1075 m = cmdutil.match(repo, pats, opts)
1076 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1076 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1077 for chunk in it:
1077 for chunk in it:
1078 repo.ui.write(chunk)
1078 repo.ui.write(chunk)
1079
1079
1080 def export(ui, repo, *changesets, **opts):
1080 def export(ui, repo, *changesets, **opts):
1081 """dump the header and diffs for one or more changesets
1081 """dump the header and diffs for one or more changesets
1082
1082
1083 Print the changeset header and diffs for one or more revisions.
1083 Print the changeset header and diffs for one or more revisions.
1084
1084
1085 The information shown in the changeset header is: author,
1085 The information shown in the changeset header is: author,
1086 changeset hash, parent(s) and commit comment.
1086 changeset hash, parent(s) and commit comment.
1087
1087
1088 NOTE: export may generate unexpected diff output for merge
1088 NOTE: export may generate unexpected diff output for merge
1089 changesets, as it will compare the merge changeset against its
1089 changesets, as it will compare the merge changeset against its
1090 first parent only.
1090 first parent only.
1091
1091
1092 Output may be to a file, in which case the name of the file is
1092 Output may be to a file, in which case the name of the file is
1093 given using a format string. The formatting rules are as follows:
1093 given using a format string. The formatting rules are as follows:
1094
1094
1095 %% literal "%" character
1095 %% literal "%" character
1096 %H changeset hash (40 bytes of hexadecimal)
1096 %H changeset hash (40 bytes of hexadecimal)
1097 %N number of patches being generated
1097 %N number of patches being generated
1098 %R changeset revision number
1098 %R changeset revision number
1099 %b basename of the exporting repository
1099 %b basename of the exporting repository
1100 %h short-form changeset hash (12 bytes of hexadecimal)
1100 %h short-form changeset hash (12 bytes of hexadecimal)
1101 %n zero-padded sequence number, starting at 1
1101 %n zero-padded sequence number, starting at 1
1102 %r zero-padded changeset revision number
1102 %r zero-padded changeset revision number
1103
1103
1104 Without the -a/--text option, export will avoid generating diffs
1104 Without the -a/--text option, export will avoid generating diffs
1105 of files it detects as binary. With -a, export will generate a
1105 of files it detects as binary. With -a, export will generate a
1106 diff anyway, probably with undesirable results.
1106 diff anyway, probably with undesirable results.
1107
1107
1108 Use the -g/--git option to generate diffs in the git extended diff
1108 Use the -g/--git option to generate diffs in the git extended diff
1109 format. Read the diffs help topic for more information.
1109 format. Read the diffs help topic for more information.
1110
1110
1111 With the --switch-parent option, the diff will be against the
1111 With the --switch-parent option, the diff will be against the
1112 second parent. It can be useful to review a merge.
1112 second parent. It can be useful to review a merge.
1113 """
1113 """
1114 if not changesets:
1114 if not changesets:
1115 raise util.Abort(_("export requires at least one changeset"))
1115 raise util.Abort(_("export requires at least one changeset"))
1116 revs = cmdutil.revrange(repo, changesets)
1116 revs = cmdutil.revrange(repo, changesets)
1117 if len(revs) > 1:
1117 if len(revs) > 1:
1118 ui.note(_('exporting patches:\n'))
1118 ui.note(_('exporting patches:\n'))
1119 else:
1119 else:
1120 ui.note(_('exporting patch:\n'))
1120 ui.note(_('exporting patch:\n'))
1121 patch.export(repo, revs, template=opts.get('output'),
1121 patch.export(repo, revs, template=opts.get('output'),
1122 switch_parent=opts.get('switch_parent'),
1122 switch_parent=opts.get('switch_parent'),
1123 opts=patch.diffopts(ui, opts))
1123 opts=patch.diffopts(ui, opts))
1124
1124
1125 def grep(ui, repo, pattern, *pats, **opts):
1125 def grep(ui, repo, pattern, *pats, **opts):
1126 """search for a pattern in specified files and revisions
1126 """search for a pattern in specified files and revisions
1127
1127
1128 Search revisions of files for a regular expression.
1128 Search revisions of files for a regular expression.
1129
1129
1130 This command behaves differently than Unix grep. It only accepts
1130 This command behaves differently than Unix grep. It only accepts
1131 Python/Perl regexps. It searches repository history, not the
1131 Python/Perl regexps. It searches repository history, not the
1132 working directory. It always prints the revision number in which a
1132 working directory. It always prints the revision number in which a
1133 match appears.
1133 match appears.
1134
1134
1135 By default, grep only prints output for the first revision of a
1135 By default, grep only prints output for the first revision of a
1136 file in which it finds a match. To get it to print every revision
1136 file in which it finds a match. To get it to print every revision
1137 that contains a change in match status ("-" for a match that
1137 that contains a change in match status ("-" for a match that
1138 becomes a non-match, or "+" for a non-match that becomes a match),
1138 becomes a non-match, or "+" for a non-match that becomes a match),
1139 use the --all flag.
1139 use the --all flag.
1140 """
1140 """
1141 reflags = 0
1141 reflags = 0
1142 if opts.get('ignore_case'):
1142 if opts.get('ignore_case'):
1143 reflags |= re.I
1143 reflags |= re.I
1144 try:
1144 try:
1145 regexp = re.compile(pattern, reflags)
1145 regexp = re.compile(pattern, reflags)
1146 except Exception, inst:
1146 except Exception, inst:
1147 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1147 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1148 return None
1148 return None
1149 sep, eol = ':', '\n'
1149 sep, eol = ':', '\n'
1150 if opts.get('print0'):
1150 if opts.get('print0'):
1151 sep = eol = '\0'
1151 sep = eol = '\0'
1152
1152
1153 fcache = {}
1153 fcache = {}
1154 def getfile(fn):
1154 def getfile(fn):
1155 if fn not in fcache:
1155 if fn not in fcache:
1156 fcache[fn] = repo.file(fn)
1156 fcache[fn] = repo.file(fn)
1157 return fcache[fn]
1157 return fcache[fn]
1158
1158
1159 def matchlines(body):
1159 def matchlines(body):
1160 begin = 0
1160 begin = 0
1161 linenum = 0
1161 linenum = 0
1162 while True:
1162 while True:
1163 match = regexp.search(body, begin)
1163 match = regexp.search(body, begin)
1164 if not match:
1164 if not match:
1165 break
1165 break
1166 mstart, mend = match.span()
1166 mstart, mend = match.span()
1167 linenum += body.count('\n', begin, mstart) + 1
1167 linenum += body.count('\n', begin, mstart) + 1
1168 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1168 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1169 begin = body.find('\n', mend) + 1 or len(body)
1169 begin = body.find('\n', mend) + 1 or len(body)
1170 lend = begin - 1
1170 lend = begin - 1
1171 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1171 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1172
1172
1173 class linestate(object):
1173 class linestate(object):
1174 def __init__(self, line, linenum, colstart, colend):
1174 def __init__(self, line, linenum, colstart, colend):
1175 self.line = line
1175 self.line = line
1176 self.linenum = linenum
1176 self.linenum = linenum
1177 self.colstart = colstart
1177 self.colstart = colstart
1178 self.colend = colend
1178 self.colend = colend
1179
1179
1180 def __hash__(self):
1180 def __hash__(self):
1181 return hash((self.linenum, self.line))
1181 return hash((self.linenum, self.line))
1182
1182
1183 def __eq__(self, other):
1183 def __eq__(self, other):
1184 return self.line == other.line
1184 return self.line == other.line
1185
1185
1186 matches = {}
1186 matches = {}
1187 copies = {}
1187 copies = {}
1188 def grepbody(fn, rev, body):
1188 def grepbody(fn, rev, body):
1189 matches[rev].setdefault(fn, [])
1189 matches[rev].setdefault(fn, [])
1190 m = matches[rev][fn]
1190 m = matches[rev][fn]
1191 for lnum, cstart, cend, line in matchlines(body):
1191 for lnum, cstart, cend, line in matchlines(body):
1192 s = linestate(line, lnum, cstart, cend)
1192 s = linestate(line, lnum, cstart, cend)
1193 m.append(s)
1193 m.append(s)
1194
1194
1195 def difflinestates(a, b):
1195 def difflinestates(a, b):
1196 sm = difflib.SequenceMatcher(None, a, b)
1196 sm = difflib.SequenceMatcher(None, a, b)
1197 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1197 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1198 if tag == 'insert':
1198 if tag == 'insert':
1199 for i in xrange(blo, bhi):
1199 for i in xrange(blo, bhi):
1200 yield ('+', b[i])
1200 yield ('+', b[i])
1201 elif tag == 'delete':
1201 elif tag == 'delete':
1202 for i in xrange(alo, ahi):
1202 for i in xrange(alo, ahi):
1203 yield ('-', a[i])
1203 yield ('-', a[i])
1204 elif tag == 'replace':
1204 elif tag == 'replace':
1205 for i in xrange(alo, ahi):
1205 for i in xrange(alo, ahi):
1206 yield ('-', a[i])
1206 yield ('-', a[i])
1207 for i in xrange(blo, bhi):
1207 for i in xrange(blo, bhi):
1208 yield ('+', b[i])
1208 yield ('+', b[i])
1209
1209
1210 prev = {}
1210 prev = {}
1211 def display(fn, rev, states, prevstates):
1211 def display(fn, rev, states, prevstates):
1212 datefunc = ui.quiet and util.shortdate or util.datestr
1212 datefunc = ui.quiet and util.shortdate or util.datestr
1213 found = False
1213 found = False
1214 filerevmatches = {}
1214 filerevmatches = {}
1215 r = prev.get(fn, -1)
1215 r = prev.get(fn, -1)
1216 if opts.get('all'):
1216 if opts.get('all'):
1217 iter = difflinestates(states, prevstates)
1217 iter = difflinestates(states, prevstates)
1218 else:
1218 else:
1219 iter = [('', l) for l in prevstates]
1219 iter = [('', l) for l in prevstates]
1220 for change, l in iter:
1220 for change, l in iter:
1221 cols = [fn, str(r)]
1221 cols = [fn, str(r)]
1222 if opts.get('line_number'):
1222 if opts.get('line_number'):
1223 cols.append(str(l.linenum))
1223 cols.append(str(l.linenum))
1224 if opts.get('all'):
1224 if opts.get('all'):
1225 cols.append(change)
1225 cols.append(change)
1226 if opts.get('user'):
1226 if opts.get('user'):
1227 cols.append(ui.shortuser(get(r)[1]))
1227 cols.append(ui.shortuser(get(r)[1]))
1228 if opts.get('date'):
1228 if opts.get('date'):
1229 cols.append(datefunc(get(r)[2]))
1229 cols.append(datefunc(get(r)[2]))
1230 if opts.get('files_with_matches'):
1230 if opts.get('files_with_matches'):
1231 c = (fn, r)
1231 c = (fn, r)
1232 if c in filerevmatches:
1232 if c in filerevmatches:
1233 continue
1233 continue
1234 filerevmatches[c] = 1
1234 filerevmatches[c] = 1
1235 else:
1235 else:
1236 cols.append(l.line)
1236 cols.append(l.line)
1237 ui.write(sep.join(cols), eol)
1237 ui.write(sep.join(cols), eol)
1238 found = True
1238 found = True
1239 return found
1239 return found
1240
1240
1241 fstate = {}
1241 fstate = {}
1242 skip = {}
1242 skip = {}
1243 get = util.cachefunc(lambda r: repo[r].changeset())
1243 get = util.cachefunc(lambda r: repo[r].changeset())
1244 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1244 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1245 found = False
1245 found = False
1246 follow = opts.get('follow')
1246 follow = opts.get('follow')
1247 for st, rev, fns in changeiter:
1247 for st, rev, fns in changeiter:
1248 if st == 'window':
1248 if st == 'window':
1249 matches.clear()
1249 matches.clear()
1250 elif st == 'add':
1250 elif st == 'add':
1251 ctx = repo[rev]
1251 ctx = repo[rev]
1252 matches[rev] = {}
1252 matches[rev] = {}
1253 for fn in fns:
1253 for fn in fns:
1254 if fn in skip:
1254 if fn in skip:
1255 continue
1255 continue
1256 try:
1256 try:
1257 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1257 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1258 fstate.setdefault(fn, [])
1258 fstate.setdefault(fn, [])
1259 if follow:
1259 if follow:
1260 copied = getfile(fn).renamed(ctx.filenode(fn))
1260 copied = getfile(fn).renamed(ctx.filenode(fn))
1261 if copied:
1261 if copied:
1262 copies.setdefault(rev, {})[fn] = copied[0]
1262 copies.setdefault(rev, {})[fn] = copied[0]
1263 except error.LookupError:
1263 except error.LookupError:
1264 pass
1264 pass
1265 elif st == 'iter':
1265 elif st == 'iter':
1266 for fn, m in sorted(matches[rev].items()):
1266 for fn, m in sorted(matches[rev].items()):
1267 copy = copies.get(rev, {}).get(fn)
1267 copy = copies.get(rev, {}).get(fn)
1268 if fn in skip:
1268 if fn in skip:
1269 if copy:
1269 if copy:
1270 skip[copy] = True
1270 skip[copy] = True
1271 continue
1271 continue
1272 if fn in prev or fstate[fn]:
1272 if fn in prev or fstate[fn]:
1273 r = display(fn, rev, m, fstate[fn])
1273 r = display(fn, rev, m, fstate[fn])
1274 found = found or r
1274 found = found or r
1275 if r and not opts.get('all'):
1275 if r and not opts.get('all'):
1276 skip[fn] = True
1276 skip[fn] = True
1277 if copy:
1277 if copy:
1278 skip[copy] = True
1278 skip[copy] = True
1279 fstate[fn] = m
1279 fstate[fn] = m
1280 if copy:
1280 if copy:
1281 fstate[copy] = m
1281 fstate[copy] = m
1282 prev[fn] = rev
1282 prev[fn] = rev
1283
1283
1284 for fn, state in sorted(fstate.items()):
1284 for fn, state in sorted(fstate.items()):
1285 if fn in skip:
1285 if fn in skip:
1286 continue
1286 continue
1287 if fn not in copies.get(prev[fn], {}):
1287 if fn not in copies.get(prev[fn], {}):
1288 found = display(fn, rev, {}, state) or found
1288 found = display(fn, rev, {}, state) or found
1289 return (not found and 1) or 0
1289 return (not found and 1) or 0
1290
1290
1291 def heads(ui, repo, *branchrevs, **opts):
1291 def heads(ui, repo, *branchrevs, **opts):
1292 """show current repository heads or show branch heads
1292 """show current repository heads or show branch heads
1293
1293
1294 With no arguments, show all repository head changesets.
1294 With no arguments, show all repository head changesets.
1295
1295
1296 If branch or revisions names are given this will show the heads of
1296 If branch or revisions names are given this will show the heads of
1297 the specified branches or the branches those revisions are tagged
1297 the specified branches or the branches those revisions are tagged
1298 with.
1298 with.
1299
1299
1300 Repository "heads" are changesets that don't have child
1300 Repository "heads" are changesets that don't have child
1301 changesets. They are where development generally takes place and
1301 changesets. They are where development generally takes place and
1302 are the usual targets for update and merge operations.
1302 are the usual targets for update and merge operations.
1303
1303
1304 Branch heads are changesets that have a given branch tag, but have
1304 Branch heads are changesets that have a given branch tag, but have
1305 no child changesets with that tag. They are usually where
1305 no child changesets with that tag. They are usually where
1306 development on the given branch takes place.
1306 development on the given branch takes place.
1307 """
1307 """
1308 if opts.get('rev'):
1308 if opts.get('rev'):
1309 start = repo.lookup(opts['rev'])
1309 start = repo.lookup(opts['rev'])
1310 else:
1310 else:
1311 start = None
1311 start = None
1312 closed = not opts.get('active')
1312 closed = not opts.get('active')
1313 if not branchrevs:
1313 if not branchrevs:
1314 # Assume we're looking repo-wide heads if no revs were specified.
1314 # Assume we're looking repo-wide heads if no revs were specified.
1315 heads = repo.heads(start, closed=closed)
1315 heads = repo.heads(start, closed=closed)
1316 else:
1316 else:
1317 heads = []
1317 heads = []
1318 visitedset = set()
1318 visitedset = set()
1319 for branchrev in branchrevs:
1319 for branchrev in branchrevs:
1320 branch = repo[branchrev].branch()
1320 branch = repo[branchrev].branch()
1321 if branch in visitedset:
1321 if branch in visitedset:
1322 continue
1322 continue
1323 visitedset.add(branch)
1323 visitedset.add(branch)
1324 bheads = repo.branchheads(branch, start, closed=closed)
1324 bheads = repo.branchheads(branch, start, closed=closed)
1325 if not bheads:
1325 if not bheads:
1326 if branch != branchrev:
1326 if branch != branchrev:
1327 ui.warn(_("no changes on branch %s containing %s are "
1327 ui.warn(_("no changes on branch %s containing %s are "
1328 "reachable from %s\n")
1328 "reachable from %s\n")
1329 % (branch, branchrev, opts.get('rev')))
1329 % (branch, branchrev, opts.get('rev')))
1330 else:
1330 else:
1331 ui.warn(_("no changes on branch %s are reachable from %s\n")
1331 ui.warn(_("no changes on branch %s are reachable from %s\n")
1332 % (branch, opts.get('rev')))
1332 % (branch, opts.get('rev')))
1333 heads.extend(bheads)
1333 heads.extend(bheads)
1334 if not heads:
1334 if not heads:
1335 return 1
1335 return 1
1336 displayer = cmdutil.show_changeset(ui, repo, opts)
1336 displayer = cmdutil.show_changeset(ui, repo, opts)
1337 for n in heads:
1337 for n in heads:
1338 displayer.show(repo[n])
1338 displayer.show(repo[n])
1339
1339
1340 def help_(ui, name=None, with_version=False):
1340 def help_(ui, name=None, with_version=False):
1341 """show help for a given topic or a help overview
1341 """show help for a given topic or a help overview
1342
1342
1343 With no arguments, print a list of commands and short help.
1343 With no arguments, print a list of commands and short help.
1344
1344
1345 Given a topic, extension, or command name, print help for that
1345 Given a topic, extension, or command name, print help for that
1346 topic."""
1346 topic."""
1347 option_lists = []
1347 option_lists = []
1348
1348
1349 def addglobalopts(aliases):
1349 def addglobalopts(aliases):
1350 if ui.verbose:
1350 if ui.verbose:
1351 option_lists.append((_("global options:"), globalopts))
1351 option_lists.append((_("global options:"), globalopts))
1352 if name == 'shortlist':
1352 if name == 'shortlist':
1353 option_lists.append((_('use "hg help" for the full list '
1353 option_lists.append((_('use "hg help" for the full list '
1354 'of commands'), ()))
1354 'of commands'), ()))
1355 else:
1355 else:
1356 if name == 'shortlist':
1356 if name == 'shortlist':
1357 msg = _('use "hg help" for the full list of commands '
1357 msg = _('use "hg help" for the full list of commands '
1358 'or "hg -v" for details')
1358 'or "hg -v" for details')
1359 elif aliases:
1359 elif aliases:
1360 msg = _('use "hg -v help%s" to show aliases and '
1360 msg = _('use "hg -v help%s" to show aliases and '
1361 'global options') % (name and " " + name or "")
1361 'global options') % (name and " " + name or "")
1362 else:
1362 else:
1363 msg = _('use "hg -v help %s" to show global options') % name
1363 msg = _('use "hg -v help %s" to show global options') % name
1364 option_lists.append((msg, ()))
1364 option_lists.append((msg, ()))
1365
1365
1366 def helpcmd(name):
1366 def helpcmd(name):
1367 if with_version:
1367 if with_version:
1368 version_(ui)
1368 version_(ui)
1369 ui.write('\n')
1369 ui.write('\n')
1370
1370
1371 try:
1371 try:
1372 aliases, i = cmdutil.findcmd(name, table, False)
1372 aliases, i = cmdutil.findcmd(name, table, False)
1373 except error.AmbiguousCommand, inst:
1373 except error.AmbiguousCommand, inst:
1374 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1374 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1375 helplist(_('list of commands:\n\n'), select)
1375 helplist(_('list of commands:\n\n'), select)
1376 return
1376 return
1377
1377
1378 # synopsis
1378 # synopsis
1379 if len(i) > 2:
1379 if len(i) > 2:
1380 if i[2].startswith('hg'):
1380 if i[2].startswith('hg'):
1381 ui.write("%s\n" % i[2])
1381 ui.write("%s\n" % i[2])
1382 else:
1382 else:
1383 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1383 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1384 else:
1384 else:
1385 ui.write('hg %s\n' % aliases[0])
1385 ui.write('hg %s\n' % aliases[0])
1386
1386
1387 # aliases
1387 # aliases
1388 if not ui.quiet and len(aliases) > 1:
1388 if not ui.quiet and len(aliases) > 1:
1389 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1389 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1390
1390
1391 # description
1391 # description
1392 doc = gettext(i[0].__doc__)
1392 doc = gettext(i[0].__doc__)
1393 if not doc:
1393 if not doc:
1394 doc = _("(no help text available)")
1394 doc = _("(no help text available)")
1395 if ui.quiet:
1395 if ui.quiet:
1396 doc = doc.splitlines(0)[0]
1396 doc = doc.splitlines(0)[0]
1397 ui.write("\n%s\n" % doc.rstrip())
1397 ui.write("\n%s\n" % doc.rstrip())
1398
1398
1399 if not ui.quiet:
1399 if not ui.quiet:
1400 # options
1400 # options
1401 if i[1]:
1401 if i[1]:
1402 option_lists.append((_("options:\n"), i[1]))
1402 option_lists.append((_("options:\n"), i[1]))
1403
1403
1404 addglobalopts(False)
1404 addglobalopts(False)
1405
1405
1406 def helplist(header, select=None):
1406 def helplist(header, select=None):
1407 h = {}
1407 h = {}
1408 cmds = {}
1408 cmds = {}
1409 for c, e in table.iteritems():
1409 for c, e in table.iteritems():
1410 f = c.split("|", 1)[0]
1410 f = c.split("|", 1)[0]
1411 if select and not select(f):
1411 if select and not select(f):
1412 continue
1412 continue
1413 if (not select and name != 'shortlist' and
1413 if (not select and name != 'shortlist' and
1414 e[0].__module__ != __name__):
1414 e[0].__module__ != __name__):
1415 continue
1415 continue
1416 if name == "shortlist" and not f.startswith("^"):
1416 if name == "shortlist" and not f.startswith("^"):
1417 continue
1417 continue
1418 f = f.lstrip("^")
1418 f = f.lstrip("^")
1419 if not ui.debugflag and f.startswith("debug"):
1419 if not ui.debugflag and f.startswith("debug"):
1420 continue
1420 continue
1421 doc = gettext(e[0].__doc__)
1421 doc = gettext(e[0].__doc__)
1422 if not doc:
1422 if not doc:
1423 doc = _("(no help text available)")
1423 doc = _("(no help text available)")
1424 h[f] = doc.splitlines(0)[0].rstrip()
1424 h[f] = doc.splitlines(0)[0].rstrip()
1425 cmds[f] = c.lstrip("^")
1425 cmds[f] = c.lstrip("^")
1426
1426
1427 if not h:
1427 if not h:
1428 ui.status(_('no commands defined\n'))
1428 ui.status(_('no commands defined\n'))
1429 return
1429 return
1430
1430
1431 ui.status(header)
1431 ui.status(header)
1432 fns = sorted(h)
1432 fns = sorted(h)
1433 m = max(map(len, fns))
1433 m = max(map(len, fns))
1434 for f in fns:
1434 for f in fns:
1435 if ui.verbose:
1435 if ui.verbose:
1436 commands = cmds[f].replace("|",", ")
1436 commands = cmds[f].replace("|",", ")
1437 ui.write(" %s:\n %s\n"%(commands, h[f]))
1437 ui.write(" %s:\n %s\n"%(commands, h[f]))
1438 else:
1438 else:
1439 ui.write(' %-*s %s\n' % (m, f, h[f]))
1439 ui.write(' %-*s %s\n' % (m, f, h[f]))
1440
1440
1441 exts = list(extensions.extensions())
1441 exts = list(extensions.extensions())
1442 if exts and name != 'shortlist':
1442 if exts and name != 'shortlist':
1443 ui.write(_('\nenabled extensions:\n\n'))
1443 ui.write(_('\nenabled extensions:\n\n'))
1444 maxlength = 0
1444 maxlength = 0
1445 exthelps = []
1445 exthelps = []
1446 for ename, ext in exts:
1446 for ename, ext in exts:
1447 doc = (gettext(ext.__doc__) or _('(no help text available)'))
1447 doc = (gettext(ext.__doc__) or _('(no help text available)'))
1448 ename = ename.split('.')[-1]
1448 ename = ename.split('.')[-1]
1449 maxlength = max(len(ename), maxlength)
1449 maxlength = max(len(ename), maxlength)
1450 exthelps.append((ename, doc.splitlines(0)[0].strip()))
1450 exthelps.append((ename, doc.splitlines(0)[0].strip()))
1451 for ename, text in exthelps:
1451 for ename, text in exthelps:
1452 ui.write(_(' %s %s\n') % (ename.ljust(maxlength), text))
1452 ui.write(_(' %s %s\n') % (ename.ljust(maxlength), text))
1453
1453
1454 if not ui.quiet:
1454 if not ui.quiet:
1455 addglobalopts(True)
1455 addglobalopts(True)
1456
1456
1457 def helptopic(name):
1457 def helptopic(name):
1458 for names, header, doc in help.helptable:
1458 for names, header, doc in help.helptable:
1459 if name in names:
1459 if name in names:
1460 break
1460 break
1461 else:
1461 else:
1462 raise error.UnknownCommand(name)
1462 raise error.UnknownCommand(name)
1463
1463
1464 # description
1464 # description
1465 if not doc:
1465 if not doc:
1466 doc = _("(no help text available)")
1466 doc = _("(no help text available)")
1467 if callable(doc):
1467 if hasattr(doc, '__call__'):
1468 doc = doc()
1468 doc = doc()
1469
1469
1470 ui.write("%s\n" % header)
1470 ui.write("%s\n" % header)
1471 ui.write("%s\n" % doc.rstrip())
1471 ui.write("%s\n" % doc.rstrip())
1472
1472
1473 def helpext(name):
1473 def helpext(name):
1474 try:
1474 try:
1475 mod = extensions.find(name)
1475 mod = extensions.find(name)
1476 except KeyError:
1476 except KeyError:
1477 raise error.UnknownCommand(name)
1477 raise error.UnknownCommand(name)
1478
1478
1479 doc = gettext(mod.__doc__) or _('no help text available')
1479 doc = gettext(mod.__doc__) or _('no help text available')
1480 doc = doc.splitlines(0)
1480 doc = doc.splitlines(0)
1481 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1481 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1482 for d in doc[1:]:
1482 for d in doc[1:]:
1483 ui.write(d, '\n')
1483 ui.write(d, '\n')
1484
1484
1485 ui.status('\n')
1485 ui.status('\n')
1486
1486
1487 try:
1487 try:
1488 ct = mod.cmdtable
1488 ct = mod.cmdtable
1489 except AttributeError:
1489 except AttributeError:
1490 ct = {}
1490 ct = {}
1491
1491
1492 modcmds = set([c.split('|', 1)[0] for c in ct])
1492 modcmds = set([c.split('|', 1)[0] for c in ct])
1493 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1493 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1494
1494
1495 if name and name != 'shortlist':
1495 if name and name != 'shortlist':
1496 i = None
1496 i = None
1497 for f in (helptopic, helpcmd, helpext):
1497 for f in (helptopic, helpcmd, helpext):
1498 try:
1498 try:
1499 f(name)
1499 f(name)
1500 i = None
1500 i = None
1501 break
1501 break
1502 except error.UnknownCommand, inst:
1502 except error.UnknownCommand, inst:
1503 i = inst
1503 i = inst
1504 if i:
1504 if i:
1505 raise i
1505 raise i
1506
1506
1507 else:
1507 else:
1508 # program name
1508 # program name
1509 if ui.verbose or with_version:
1509 if ui.verbose or with_version:
1510 version_(ui)
1510 version_(ui)
1511 else:
1511 else:
1512 ui.status(_("Mercurial Distributed SCM\n"))
1512 ui.status(_("Mercurial Distributed SCM\n"))
1513 ui.status('\n')
1513 ui.status('\n')
1514
1514
1515 # list of commands
1515 # list of commands
1516 if name == "shortlist":
1516 if name == "shortlist":
1517 header = _('basic commands:\n\n')
1517 header = _('basic commands:\n\n')
1518 else:
1518 else:
1519 header = _('list of commands:\n\n')
1519 header = _('list of commands:\n\n')
1520
1520
1521 helplist(header)
1521 helplist(header)
1522
1522
1523 # list all option lists
1523 # list all option lists
1524 opt_output = []
1524 opt_output = []
1525 for title, options in option_lists:
1525 for title, options in option_lists:
1526 opt_output.append(("\n%s" % title, None))
1526 opt_output.append(("\n%s" % title, None))
1527 for shortopt, longopt, default, desc in options:
1527 for shortopt, longopt, default, desc in options:
1528 if "DEPRECATED" in desc and not ui.verbose: continue
1528 if "DEPRECATED" in desc and not ui.verbose: continue
1529 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1529 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1530 longopt and " --%s" % longopt),
1530 longopt and " --%s" % longopt),
1531 "%s%s" % (desc,
1531 "%s%s" % (desc,
1532 default
1532 default
1533 and _(" (default: %s)") % default
1533 and _(" (default: %s)") % default
1534 or "")))
1534 or "")))
1535
1535
1536 if not name:
1536 if not name:
1537 ui.write(_("\nadditional help topics:\n\n"))
1537 ui.write(_("\nadditional help topics:\n\n"))
1538 topics = []
1538 topics = []
1539 for names, header, doc in help.helptable:
1539 for names, header, doc in help.helptable:
1540 names = [(-len(name), name) for name in names]
1540 names = [(-len(name), name) for name in names]
1541 names.sort()
1541 names.sort()
1542 topics.append((names[0][1], header))
1542 topics.append((names[0][1], header))
1543 topics_len = max([len(s[0]) for s in topics])
1543 topics_len = max([len(s[0]) for s in topics])
1544 for t, desc in topics:
1544 for t, desc in topics:
1545 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1545 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1546
1546
1547 if opt_output:
1547 if opt_output:
1548 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1548 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1549 for first, second in opt_output:
1549 for first, second in opt_output:
1550 if second:
1550 if second:
1551 # wrap descriptions at 70 characters, just like the
1551 # wrap descriptions at 70 characters, just like the
1552 # main help texts
1552 # main help texts
1553 second = textwrap.wrap(second, width=70 - opts_len - 3)
1553 second = textwrap.wrap(second, width=70 - opts_len - 3)
1554 pad = '\n' + ' ' * (opts_len + 3)
1554 pad = '\n' + ' ' * (opts_len + 3)
1555 ui.write(" %-*s %s\n" % (opts_len, first, pad.join(second)))
1555 ui.write(" %-*s %s\n" % (opts_len, first, pad.join(second)))
1556 else:
1556 else:
1557 ui.write("%s\n" % first)
1557 ui.write("%s\n" % first)
1558
1558
1559 def identify(ui, repo, source=None,
1559 def identify(ui, repo, source=None,
1560 rev=None, num=None, id=None, branch=None, tags=None):
1560 rev=None, num=None, id=None, branch=None, tags=None):
1561 """identify the working copy or specified revision
1561 """identify the working copy or specified revision
1562
1562
1563 With no revision, print a summary of the current state of the
1563 With no revision, print a summary of the current state of the
1564 repository.
1564 repository.
1565
1565
1566 With a path, do a lookup in another repository.
1566 With a path, do a lookup in another repository.
1567
1567
1568 This summary identifies the repository state using one or two
1568 This summary identifies the repository state using one or two
1569 parent hash identifiers, followed by a "+" if there are
1569 parent hash identifiers, followed by a "+" if there are
1570 uncommitted changes in the working directory, a list of tags for
1570 uncommitted changes in the working directory, a list of tags for
1571 this revision and a branch name for non-default branches.
1571 this revision and a branch name for non-default branches.
1572 """
1572 """
1573
1573
1574 if not repo and not source:
1574 if not repo and not source:
1575 raise util.Abort(_("There is no Mercurial repository here "
1575 raise util.Abort(_("There is no Mercurial repository here "
1576 "(.hg not found)"))
1576 "(.hg not found)"))
1577
1577
1578 hexfunc = ui.debugflag and hex or short
1578 hexfunc = ui.debugflag and hex or short
1579 default = not (num or id or branch or tags)
1579 default = not (num or id or branch or tags)
1580 output = []
1580 output = []
1581
1581
1582 revs = []
1582 revs = []
1583 if source:
1583 if source:
1584 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1584 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1585 repo = hg.repository(ui, source)
1585 repo = hg.repository(ui, source)
1586
1586
1587 if not repo.local():
1587 if not repo.local():
1588 if not rev and revs:
1588 if not rev and revs:
1589 rev = revs[0]
1589 rev = revs[0]
1590 if not rev:
1590 if not rev:
1591 rev = "tip"
1591 rev = "tip"
1592 if num or branch or tags:
1592 if num or branch or tags:
1593 raise util.Abort(
1593 raise util.Abort(
1594 "can't query remote revision number, branch, or tags")
1594 "can't query remote revision number, branch, or tags")
1595 output = [hexfunc(repo.lookup(rev))]
1595 output = [hexfunc(repo.lookup(rev))]
1596 elif not rev:
1596 elif not rev:
1597 ctx = repo[None]
1597 ctx = repo[None]
1598 parents = ctx.parents()
1598 parents = ctx.parents()
1599 changed = False
1599 changed = False
1600 if default or id or num:
1600 if default or id or num:
1601 changed = ctx.files() + ctx.deleted()
1601 changed = ctx.files() + ctx.deleted()
1602 if default or id:
1602 if default or id:
1603 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1603 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1604 (changed) and "+" or "")]
1604 (changed) and "+" or "")]
1605 if num:
1605 if num:
1606 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1606 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1607 (changed) and "+" or ""))
1607 (changed) and "+" or ""))
1608 else:
1608 else:
1609 ctx = repo[rev]
1609 ctx = repo[rev]
1610 if default or id:
1610 if default or id:
1611 output = [hexfunc(ctx.node())]
1611 output = [hexfunc(ctx.node())]
1612 if num:
1612 if num:
1613 output.append(str(ctx.rev()))
1613 output.append(str(ctx.rev()))
1614
1614
1615 if repo.local() and default and not ui.quiet:
1615 if repo.local() and default and not ui.quiet:
1616 b = encoding.tolocal(ctx.branch())
1616 b = encoding.tolocal(ctx.branch())
1617 if b != 'default':
1617 if b != 'default':
1618 output.append("(%s)" % b)
1618 output.append("(%s)" % b)
1619
1619
1620 # multiple tags for a single parent separated by '/'
1620 # multiple tags for a single parent separated by '/'
1621 t = "/".join(ctx.tags())
1621 t = "/".join(ctx.tags())
1622 if t:
1622 if t:
1623 output.append(t)
1623 output.append(t)
1624
1624
1625 if branch:
1625 if branch:
1626 output.append(encoding.tolocal(ctx.branch()))
1626 output.append(encoding.tolocal(ctx.branch()))
1627
1627
1628 if tags:
1628 if tags:
1629 output.extend(ctx.tags())
1629 output.extend(ctx.tags())
1630
1630
1631 ui.write("%s\n" % ' '.join(output))
1631 ui.write("%s\n" % ' '.join(output))
1632
1632
1633 def import_(ui, repo, patch1, *patches, **opts):
1633 def import_(ui, repo, patch1, *patches, **opts):
1634 """import an ordered set of patches
1634 """import an ordered set of patches
1635
1635
1636 Import a list of patches and commit them individually.
1636 Import a list of patches and commit them individually.
1637
1637
1638 If there are outstanding changes in the working directory, import
1638 If there are outstanding changes in the working directory, import
1639 will abort unless given the -f/--force flag.
1639 will abort unless given the -f/--force flag.
1640
1640
1641 You can import a patch straight from a mail message. Even patches
1641 You can import a patch straight from a mail message. Even patches
1642 as attachments work (body part must be type text/plain or
1642 as attachments work (body part must be type text/plain or
1643 text/x-patch to be used). From and Subject headers of email
1643 text/x-patch to be used). From and Subject headers of email
1644 message are used as default committer and commit message. All
1644 message are used as default committer and commit message. All
1645 text/plain body parts before first diff are added to commit
1645 text/plain body parts before first diff are added to commit
1646 message.
1646 message.
1647
1647
1648 If the imported patch was generated by hg export, user and
1648 If the imported patch was generated by hg export, user and
1649 description from patch override values from message headers and
1649 description from patch override values from message headers and
1650 body. Values given on command line with -m/--message and -u/--user
1650 body. Values given on command line with -m/--message and -u/--user
1651 override these.
1651 override these.
1652
1652
1653 If --exact is specified, import will set the working directory to
1653 If --exact is specified, import will set the working directory to
1654 the parent of each patch before applying it, and will abort if the
1654 the parent of each patch before applying it, and will abort if the
1655 resulting changeset has a different ID than the one recorded in
1655 resulting changeset has a different ID than the one recorded in
1656 the patch. This may happen due to character set problems or other
1656 the patch. This may happen due to character set problems or other
1657 deficiencies in the text patch format.
1657 deficiencies in the text patch format.
1658
1658
1659 With -s/--similarity, hg will attempt to discover renames and
1659 With -s/--similarity, hg will attempt to discover renames and
1660 copies in the patch in the same way as 'addremove'.
1660 copies in the patch in the same way as 'addremove'.
1661
1661
1662 To read a patch from standard input, use patch name "-". See 'hg
1662 To read a patch from standard input, use patch name "-". See 'hg
1663 help dates' for a list of formats valid for -d/--date.
1663 help dates' for a list of formats valid for -d/--date.
1664 """
1664 """
1665 patches = (patch1,) + patches
1665 patches = (patch1,) + patches
1666
1666
1667 date = opts.get('date')
1667 date = opts.get('date')
1668 if date:
1668 if date:
1669 opts['date'] = util.parsedate(date)
1669 opts['date'] = util.parsedate(date)
1670
1670
1671 try:
1671 try:
1672 sim = float(opts.get('similarity') or 0)
1672 sim = float(opts.get('similarity') or 0)
1673 except ValueError:
1673 except ValueError:
1674 raise util.Abort(_('similarity must be a number'))
1674 raise util.Abort(_('similarity must be a number'))
1675 if sim < 0 or sim > 100:
1675 if sim < 0 or sim > 100:
1676 raise util.Abort(_('similarity must be between 0 and 100'))
1676 raise util.Abort(_('similarity must be between 0 and 100'))
1677
1677
1678 if opts.get('exact') or not opts.get('force'):
1678 if opts.get('exact') or not opts.get('force'):
1679 cmdutil.bail_if_changed(repo)
1679 cmdutil.bail_if_changed(repo)
1680
1680
1681 d = opts["base"]
1681 d = opts["base"]
1682 strip = opts["strip"]
1682 strip = opts["strip"]
1683 wlock = lock = None
1683 wlock = lock = None
1684 try:
1684 try:
1685 wlock = repo.wlock()
1685 wlock = repo.wlock()
1686 lock = repo.lock()
1686 lock = repo.lock()
1687 for p in patches:
1687 for p in patches:
1688 pf = os.path.join(d, p)
1688 pf = os.path.join(d, p)
1689
1689
1690 if pf == '-':
1690 if pf == '-':
1691 ui.status(_("applying patch from stdin\n"))
1691 ui.status(_("applying patch from stdin\n"))
1692 pf = sys.stdin
1692 pf = sys.stdin
1693 else:
1693 else:
1694 ui.status(_("applying %s\n") % p)
1694 ui.status(_("applying %s\n") % p)
1695 pf = url.open(ui, pf)
1695 pf = url.open(ui, pf)
1696 data = patch.extract(ui, pf)
1696 data = patch.extract(ui, pf)
1697 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1697 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1698
1698
1699 if tmpname is None:
1699 if tmpname is None:
1700 raise util.Abort(_('no diffs found'))
1700 raise util.Abort(_('no diffs found'))
1701
1701
1702 try:
1702 try:
1703 cmdline_message = cmdutil.logmessage(opts)
1703 cmdline_message = cmdutil.logmessage(opts)
1704 if cmdline_message:
1704 if cmdline_message:
1705 # pickup the cmdline msg
1705 # pickup the cmdline msg
1706 message = cmdline_message
1706 message = cmdline_message
1707 elif message:
1707 elif message:
1708 # pickup the patch msg
1708 # pickup the patch msg
1709 message = message.strip()
1709 message = message.strip()
1710 else:
1710 else:
1711 # launch the editor
1711 # launch the editor
1712 message = None
1712 message = None
1713 ui.debug(_('message:\n%s\n') % message)
1713 ui.debug(_('message:\n%s\n') % message)
1714
1714
1715 wp = repo.parents()
1715 wp = repo.parents()
1716 if opts.get('exact'):
1716 if opts.get('exact'):
1717 if not nodeid or not p1:
1717 if not nodeid or not p1:
1718 raise util.Abort(_('not a mercurial patch'))
1718 raise util.Abort(_('not a mercurial patch'))
1719 p1 = repo.lookup(p1)
1719 p1 = repo.lookup(p1)
1720 p2 = repo.lookup(p2 or hex(nullid))
1720 p2 = repo.lookup(p2 or hex(nullid))
1721
1721
1722 if p1 != wp[0].node():
1722 if p1 != wp[0].node():
1723 hg.clean(repo, p1)
1723 hg.clean(repo, p1)
1724 repo.dirstate.setparents(p1, p2)
1724 repo.dirstate.setparents(p1, p2)
1725 elif p2:
1725 elif p2:
1726 try:
1726 try:
1727 p1 = repo.lookup(p1)
1727 p1 = repo.lookup(p1)
1728 p2 = repo.lookup(p2)
1728 p2 = repo.lookup(p2)
1729 if p1 == wp[0].node():
1729 if p1 == wp[0].node():
1730 repo.dirstate.setparents(p1, p2)
1730 repo.dirstate.setparents(p1, p2)
1731 except error.RepoError:
1731 except error.RepoError:
1732 pass
1732 pass
1733 if opts.get('exact') or opts.get('import_branch'):
1733 if opts.get('exact') or opts.get('import_branch'):
1734 repo.dirstate.setbranch(branch or 'default')
1734 repo.dirstate.setbranch(branch or 'default')
1735
1735
1736 files = {}
1736 files = {}
1737 try:
1737 try:
1738 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1738 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1739 files=files)
1739 files=files)
1740 finally:
1740 finally:
1741 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1741 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1742 if not opts.get('no_commit'):
1742 if not opts.get('no_commit'):
1743 n = repo.commit(files, message, opts.get('user') or user,
1743 n = repo.commit(files, message, opts.get('user') or user,
1744 opts.get('date') or date)
1744 opts.get('date') or date)
1745 if opts.get('exact'):
1745 if opts.get('exact'):
1746 if hex(n) != nodeid:
1746 if hex(n) != nodeid:
1747 repo.rollback()
1747 repo.rollback()
1748 raise util.Abort(_('patch is damaged'
1748 raise util.Abort(_('patch is damaged'
1749 ' or loses information'))
1749 ' or loses information'))
1750 # Force a dirstate write so that the next transaction
1750 # Force a dirstate write so that the next transaction
1751 # backups an up-do-date file.
1751 # backups an up-do-date file.
1752 repo.dirstate.write()
1752 repo.dirstate.write()
1753 finally:
1753 finally:
1754 os.unlink(tmpname)
1754 os.unlink(tmpname)
1755 finally:
1755 finally:
1756 release(lock, wlock)
1756 release(lock, wlock)
1757
1757
1758 def incoming(ui, repo, source="default", **opts):
1758 def incoming(ui, repo, source="default", **opts):
1759 """show new changesets found in source
1759 """show new changesets found in source
1760
1760
1761 Show new changesets found in the specified path/URL or the default
1761 Show new changesets found in the specified path/URL or the default
1762 pull location. These are the changesets that would be pulled if a
1762 pull location. These are the changesets that would be pulled if a
1763 pull was requested.
1763 pull was requested.
1764
1764
1765 For remote repository, using --bundle avoids downloading the
1765 For remote repository, using --bundle avoids downloading the
1766 changesets twice if the incoming is followed by a pull.
1766 changesets twice if the incoming is followed by a pull.
1767
1767
1768 See pull for valid source format details.
1768 See pull for valid source format details.
1769 """
1769 """
1770 limit = cmdutil.loglimit(opts)
1770 limit = cmdutil.loglimit(opts)
1771 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1771 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1772 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1772 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1773 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1773 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1774 if revs:
1774 if revs:
1775 revs = [other.lookup(rev) for rev in revs]
1775 revs = [other.lookup(rev) for rev in revs]
1776 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1776 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1777 force=opts["force"])
1777 force=opts["force"])
1778 if not incoming:
1778 if not incoming:
1779 try:
1779 try:
1780 os.unlink(opts["bundle"])
1780 os.unlink(opts["bundle"])
1781 except:
1781 except:
1782 pass
1782 pass
1783 ui.status(_("no changes found\n"))
1783 ui.status(_("no changes found\n"))
1784 return 1
1784 return 1
1785
1785
1786 cleanup = None
1786 cleanup = None
1787 try:
1787 try:
1788 fname = opts["bundle"]
1788 fname = opts["bundle"]
1789 if fname or not other.local():
1789 if fname or not other.local():
1790 # create a bundle (uncompressed if other repo is not local)
1790 # create a bundle (uncompressed if other repo is not local)
1791
1791
1792 if revs is None and other.capable('changegroupsubset'):
1792 if revs is None and other.capable('changegroupsubset'):
1793 revs = rheads
1793 revs = rheads
1794
1794
1795 if revs is None:
1795 if revs is None:
1796 cg = other.changegroup(incoming, "incoming")
1796 cg = other.changegroup(incoming, "incoming")
1797 else:
1797 else:
1798 cg = other.changegroupsubset(incoming, revs, 'incoming')
1798 cg = other.changegroupsubset(incoming, revs, 'incoming')
1799 bundletype = other.local() and "HG10BZ" or "HG10UN"
1799 bundletype = other.local() and "HG10BZ" or "HG10UN"
1800 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1800 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1801 # keep written bundle?
1801 # keep written bundle?
1802 if opts["bundle"]:
1802 if opts["bundle"]:
1803 cleanup = None
1803 cleanup = None
1804 if not other.local():
1804 if not other.local():
1805 # use the created uncompressed bundlerepo
1805 # use the created uncompressed bundlerepo
1806 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1806 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1807
1807
1808 o = other.changelog.nodesbetween(incoming, revs)[0]
1808 o = other.changelog.nodesbetween(incoming, revs)[0]
1809 if opts.get('newest_first'):
1809 if opts.get('newest_first'):
1810 o.reverse()
1810 o.reverse()
1811 displayer = cmdutil.show_changeset(ui, other, opts)
1811 displayer = cmdutil.show_changeset(ui, other, opts)
1812 count = 0
1812 count = 0
1813 for n in o:
1813 for n in o:
1814 if count >= limit:
1814 if count >= limit:
1815 break
1815 break
1816 parents = [p for p in other.changelog.parents(n) if p != nullid]
1816 parents = [p for p in other.changelog.parents(n) if p != nullid]
1817 if opts.get('no_merges') and len(parents) == 2:
1817 if opts.get('no_merges') and len(parents) == 2:
1818 continue
1818 continue
1819 count += 1
1819 count += 1
1820 displayer.show(other[n])
1820 displayer.show(other[n])
1821 finally:
1821 finally:
1822 if hasattr(other, 'close'):
1822 if hasattr(other, 'close'):
1823 other.close()
1823 other.close()
1824 if cleanup:
1824 if cleanup:
1825 os.unlink(cleanup)
1825 os.unlink(cleanup)
1826
1826
1827 def init(ui, dest=".", **opts):
1827 def init(ui, dest=".", **opts):
1828 """create a new repository in the given directory
1828 """create a new repository in the given directory
1829
1829
1830 Initialize a new repository in the given directory. If the given
1830 Initialize a new repository in the given directory. If the given
1831 directory does not exist, it is created.
1831 directory does not exist, it is created.
1832
1832
1833 If no directory is given, the current directory is used.
1833 If no directory is given, the current directory is used.
1834
1834
1835 It is possible to specify an ssh:// URL as the destination.
1835 It is possible to specify an ssh:// URL as the destination.
1836 See 'hg help urls' for more information.
1836 See 'hg help urls' for more information.
1837 """
1837 """
1838 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1838 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1839
1839
1840 def locate(ui, repo, *pats, **opts):
1840 def locate(ui, repo, *pats, **opts):
1841 """locate files matching specific patterns
1841 """locate files matching specific patterns
1842
1842
1843 Print all files under Mercurial control whose names match the
1843 Print all files under Mercurial control whose names match the
1844 given patterns.
1844 given patterns.
1845
1845
1846 This command searches the entire repository by default. To search
1846 This command searches the entire repository by default. To search
1847 just the current directory and its subdirectories, use
1847 just the current directory and its subdirectories, use
1848 "--include .".
1848 "--include .".
1849
1849
1850 If no patterns are given to match, this command prints all file
1850 If no patterns are given to match, this command prints all file
1851 names.
1851 names.
1852
1852
1853 If you want to feed the output of this command into the "xargs"
1853 If you want to feed the output of this command into the "xargs"
1854 command, use the -0 option to both this command and "xargs". This
1854 command, use the -0 option to both this command and "xargs". This
1855 will avoid the problem of "xargs" treating single filenames that
1855 will avoid the problem of "xargs" treating single filenames that
1856 contain white space as multiple filenames.
1856 contain white space as multiple filenames.
1857 """
1857 """
1858 end = opts.get('print0') and '\0' or '\n'
1858 end = opts.get('print0') and '\0' or '\n'
1859 rev = opts.get('rev') or None
1859 rev = opts.get('rev') or None
1860
1860
1861 ret = 1
1861 ret = 1
1862 m = cmdutil.match(repo, pats, opts, default='relglob')
1862 m = cmdutil.match(repo, pats, opts, default='relglob')
1863 m.bad = lambda x,y: False
1863 m.bad = lambda x,y: False
1864 for abs in repo[rev].walk(m):
1864 for abs in repo[rev].walk(m):
1865 if not rev and abs not in repo.dirstate:
1865 if not rev and abs not in repo.dirstate:
1866 continue
1866 continue
1867 if opts.get('fullpath'):
1867 if opts.get('fullpath'):
1868 ui.write(repo.wjoin(abs), end)
1868 ui.write(repo.wjoin(abs), end)
1869 else:
1869 else:
1870 ui.write(((pats and m.rel(abs)) or abs), end)
1870 ui.write(((pats and m.rel(abs)) or abs), end)
1871 ret = 0
1871 ret = 0
1872
1872
1873 return ret
1873 return ret
1874
1874
1875 def log(ui, repo, *pats, **opts):
1875 def log(ui, repo, *pats, **opts):
1876 """show revision history of entire repository or files
1876 """show revision history of entire repository or files
1877
1877
1878 Print the revision history of the specified files or the entire
1878 Print the revision history of the specified files or the entire
1879 project.
1879 project.
1880
1880
1881 File history is shown without following rename or copy history of
1881 File history is shown without following rename or copy history of
1882 files. Use -f/--follow with a file name to follow history across
1882 files. Use -f/--follow with a file name to follow history across
1883 renames and copies. --follow without a file name will only show
1883 renames and copies. --follow without a file name will only show
1884 ancestors or descendants of the starting revision. --follow-first
1884 ancestors or descendants of the starting revision. --follow-first
1885 only follows the first parent of merge revisions.
1885 only follows the first parent of merge revisions.
1886
1886
1887 If no revision range is specified, the default is tip:0 unless
1887 If no revision range is specified, the default is tip:0 unless
1888 --follow is set, in which case the working directory parent is
1888 --follow is set, in which case the working directory parent is
1889 used as the starting revision.
1889 used as the starting revision.
1890
1890
1891 See 'hg help dates' for a list of formats valid for -d/--date.
1891 See 'hg help dates' for a list of formats valid for -d/--date.
1892
1892
1893 By default this command outputs: changeset id and hash, tags,
1893 By default this command outputs: changeset id and hash, tags,
1894 non-trivial parents, user, date and time, and a summary for each
1894 non-trivial parents, user, date and time, and a summary for each
1895 commit. When the -v/--verbose switch is used, the list of changed
1895 commit. When the -v/--verbose switch is used, the list of changed
1896 files and full commit message is shown.
1896 files and full commit message is shown.
1897
1897
1898 NOTE: log -p/--patch may generate unexpected diff output for merge
1898 NOTE: log -p/--patch may generate unexpected diff output for merge
1899 changesets, as it will only compare the merge changeset against
1899 changesets, as it will only compare the merge changeset against
1900 its first parent. Also, the files: list will only reflect files
1900 its first parent. Also, the files: list will only reflect files
1901 that are different from BOTH parents.
1901 that are different from BOTH parents.
1902
1902
1903 """
1903 """
1904
1904
1905 get = util.cachefunc(lambda r: repo[r].changeset())
1905 get = util.cachefunc(lambda r: repo[r].changeset())
1906 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1906 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1907
1907
1908 limit = cmdutil.loglimit(opts)
1908 limit = cmdutil.loglimit(opts)
1909 count = 0
1909 count = 0
1910
1910
1911 if opts.get('copies') and opts.get('rev'):
1911 if opts.get('copies') and opts.get('rev'):
1912 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1912 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1913 else:
1913 else:
1914 endrev = len(repo)
1914 endrev = len(repo)
1915 rcache = {}
1915 rcache = {}
1916 ncache = {}
1916 ncache = {}
1917 def getrenamed(fn, rev):
1917 def getrenamed(fn, rev):
1918 '''looks up all renames for a file (up to endrev) the first
1918 '''looks up all renames for a file (up to endrev) the first
1919 time the file is given. It indexes on the changerev and only
1919 time the file is given. It indexes on the changerev and only
1920 parses the manifest if linkrev != changerev.
1920 parses the manifest if linkrev != changerev.
1921 Returns rename info for fn at changerev rev.'''
1921 Returns rename info for fn at changerev rev.'''
1922 if fn not in rcache:
1922 if fn not in rcache:
1923 rcache[fn] = {}
1923 rcache[fn] = {}
1924 ncache[fn] = {}
1924 ncache[fn] = {}
1925 fl = repo.file(fn)
1925 fl = repo.file(fn)
1926 for i in fl:
1926 for i in fl:
1927 node = fl.node(i)
1927 node = fl.node(i)
1928 lr = fl.linkrev(i)
1928 lr = fl.linkrev(i)
1929 renamed = fl.renamed(node)
1929 renamed = fl.renamed(node)
1930 rcache[fn][lr] = renamed
1930 rcache[fn][lr] = renamed
1931 if renamed:
1931 if renamed:
1932 ncache[fn][node] = renamed
1932 ncache[fn][node] = renamed
1933 if lr >= endrev:
1933 if lr >= endrev:
1934 break
1934 break
1935 if rev in rcache[fn]:
1935 if rev in rcache[fn]:
1936 return rcache[fn][rev]
1936 return rcache[fn][rev]
1937
1937
1938 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1938 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1939 # filectx logic.
1939 # filectx logic.
1940
1940
1941 try:
1941 try:
1942 return repo[rev][fn].renamed()
1942 return repo[rev][fn].renamed()
1943 except error.LookupError:
1943 except error.LookupError:
1944 pass
1944 pass
1945 return None
1945 return None
1946
1946
1947 df = False
1947 df = False
1948 if opts["date"]:
1948 if opts["date"]:
1949 df = util.matchdate(opts["date"])
1949 df = util.matchdate(opts["date"])
1950
1950
1951 only_branches = opts.get('only_branch')
1951 only_branches = opts.get('only_branch')
1952
1952
1953 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1953 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1954 for st, rev, fns in changeiter:
1954 for st, rev, fns in changeiter:
1955 if st == 'add':
1955 if st == 'add':
1956 parents = [p for p in repo.changelog.parentrevs(rev)
1956 parents = [p for p in repo.changelog.parentrevs(rev)
1957 if p != nullrev]
1957 if p != nullrev]
1958 if opts.get('no_merges') and len(parents) == 2:
1958 if opts.get('no_merges') and len(parents) == 2:
1959 continue
1959 continue
1960 if opts.get('only_merges') and len(parents) != 2:
1960 if opts.get('only_merges') and len(parents) != 2:
1961 continue
1961 continue
1962
1962
1963 if only_branches:
1963 if only_branches:
1964 revbranch = get(rev)[5]['branch']
1964 revbranch = get(rev)[5]['branch']
1965 if revbranch not in only_branches:
1965 if revbranch not in only_branches:
1966 continue
1966 continue
1967
1967
1968 if df:
1968 if df:
1969 changes = get(rev)
1969 changes = get(rev)
1970 if not df(changes[2][0]):
1970 if not df(changes[2][0]):
1971 continue
1971 continue
1972
1972
1973 if opts.get('keyword'):
1973 if opts.get('keyword'):
1974 changes = get(rev)
1974 changes = get(rev)
1975 miss = 0
1975 miss = 0
1976 for k in [kw.lower() for kw in opts['keyword']]:
1976 for k in [kw.lower() for kw in opts['keyword']]:
1977 if not (k in changes[1].lower() or
1977 if not (k in changes[1].lower() or
1978 k in changes[4].lower() or
1978 k in changes[4].lower() or
1979 k in " ".join(changes[3]).lower()):
1979 k in " ".join(changes[3]).lower()):
1980 miss = 1
1980 miss = 1
1981 break
1981 break
1982 if miss:
1982 if miss:
1983 continue
1983 continue
1984
1984
1985 if opts['user']:
1985 if opts['user']:
1986 changes = get(rev)
1986 changes = get(rev)
1987 if not [k for k in opts['user'] if k in changes[1]]:
1987 if not [k for k in opts['user'] if k in changes[1]]:
1988 continue
1988 continue
1989
1989
1990 copies = []
1990 copies = []
1991 if opts.get('copies') and rev:
1991 if opts.get('copies') and rev:
1992 for fn in get(rev)[3]:
1992 for fn in get(rev)[3]:
1993 rename = getrenamed(fn, rev)
1993 rename = getrenamed(fn, rev)
1994 if rename:
1994 if rename:
1995 copies.append((fn, rename[0]))
1995 copies.append((fn, rename[0]))
1996 displayer.show(context.changectx(repo, rev), copies=copies)
1996 displayer.show(context.changectx(repo, rev), copies=copies)
1997 elif st == 'iter':
1997 elif st == 'iter':
1998 if count == limit: break
1998 if count == limit: break
1999 if displayer.flush(rev):
1999 if displayer.flush(rev):
2000 count += 1
2000 count += 1
2001
2001
2002 def manifest(ui, repo, node=None, rev=None):
2002 def manifest(ui, repo, node=None, rev=None):
2003 """output the current or given revision of the project manifest
2003 """output the current or given revision of the project manifest
2004
2004
2005 Print a list of version controlled files for the given revision.
2005 Print a list of version controlled files for the given revision.
2006 If no revision is given, the first parent of the working directory
2006 If no revision is given, the first parent of the working directory
2007 is used, or the null revision if none is checked out.
2007 is used, or the null revision if none is checked out.
2008
2008
2009 With -v flag, print file permissions, symlink and executable bits.
2009 With -v flag, print file permissions, symlink and executable bits.
2010 With --debug flag, print file revision hashes.
2010 With --debug flag, print file revision hashes.
2011 """
2011 """
2012
2012
2013 if rev and node:
2013 if rev and node:
2014 raise util.Abort(_("please specify just one revision"))
2014 raise util.Abort(_("please specify just one revision"))
2015
2015
2016 if not node:
2016 if not node:
2017 node = rev
2017 node = rev
2018
2018
2019 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2019 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2020 ctx = repo[node]
2020 ctx = repo[node]
2021 for f in ctx:
2021 for f in ctx:
2022 if ui.debugflag:
2022 if ui.debugflag:
2023 ui.write("%40s " % hex(ctx.manifest()[f]))
2023 ui.write("%40s " % hex(ctx.manifest()[f]))
2024 if ui.verbose:
2024 if ui.verbose:
2025 ui.write(decor[ctx.flags(f)])
2025 ui.write(decor[ctx.flags(f)])
2026 ui.write("%s\n" % f)
2026 ui.write("%s\n" % f)
2027
2027
2028 def merge(ui, repo, node=None, force=None, rev=None):
2028 def merge(ui, repo, node=None, force=None, rev=None):
2029 """merge working directory with another revision
2029 """merge working directory with another revision
2030
2030
2031 The contents of the current working directory is updated with all
2031 The contents of the current working directory is updated with all
2032 changes made in the requested revision since the last common
2032 changes made in the requested revision since the last common
2033 predecessor revision.
2033 predecessor revision.
2034
2034
2035 Files that changed between either parent are marked as changed for
2035 Files that changed between either parent are marked as changed for
2036 the next commit and a commit must be performed before any further
2036 the next commit and a commit must be performed before any further
2037 updates are allowed. The next commit has two parents.
2037 updates are allowed. The next commit has two parents.
2038
2038
2039 If no revision is specified, the working directory's parent is a
2039 If no revision is specified, the working directory's parent is a
2040 head revision, and the current branch contains exactly one other
2040 head revision, and the current branch contains exactly one other
2041 head, the other head is merged with by default. Otherwise, an
2041 head, the other head is merged with by default. Otherwise, an
2042 explicit revision to merge with must be provided.
2042 explicit revision to merge with must be provided.
2043 """
2043 """
2044
2044
2045 if rev and node:
2045 if rev and node:
2046 raise util.Abort(_("please specify just one revision"))
2046 raise util.Abort(_("please specify just one revision"))
2047 if not node:
2047 if not node:
2048 node = rev
2048 node = rev
2049
2049
2050 if not node:
2050 if not node:
2051 branch = repo.changectx(None).branch()
2051 branch = repo.changectx(None).branch()
2052 bheads = repo.branchheads(branch)
2052 bheads = repo.branchheads(branch)
2053 if len(bheads) > 2:
2053 if len(bheads) > 2:
2054 raise util.Abort(_("branch '%s' has %d heads - "
2054 raise util.Abort(_("branch '%s' has %d heads - "
2055 "please merge with an explicit rev") %
2055 "please merge with an explicit rev") %
2056 (branch, len(bheads)))
2056 (branch, len(bheads)))
2057
2057
2058 parent = repo.dirstate.parents()[0]
2058 parent = repo.dirstate.parents()[0]
2059 if len(bheads) == 1:
2059 if len(bheads) == 1:
2060 if len(repo.heads()) > 1:
2060 if len(repo.heads()) > 1:
2061 raise util.Abort(_("branch '%s' has one head - "
2061 raise util.Abort(_("branch '%s' has one head - "
2062 "please merge with an explicit rev") %
2062 "please merge with an explicit rev") %
2063 branch)
2063 branch)
2064 msg = _('there is nothing to merge')
2064 msg = _('there is nothing to merge')
2065 if parent != repo.lookup(repo[None].branch()):
2065 if parent != repo.lookup(repo[None].branch()):
2066 msg = _('%s - use "hg update" instead') % msg
2066 msg = _('%s - use "hg update" instead') % msg
2067 raise util.Abort(msg)
2067 raise util.Abort(msg)
2068
2068
2069 if parent not in bheads:
2069 if parent not in bheads:
2070 raise util.Abort(_('working dir not at a head rev - '
2070 raise util.Abort(_('working dir not at a head rev - '
2071 'use "hg update" or merge with an explicit rev'))
2071 'use "hg update" or merge with an explicit rev'))
2072 node = parent == bheads[0] and bheads[-1] or bheads[0]
2072 node = parent == bheads[0] and bheads[-1] or bheads[0]
2073 return hg.merge(repo, node, force=force)
2073 return hg.merge(repo, node, force=force)
2074
2074
2075 def outgoing(ui, repo, dest=None, **opts):
2075 def outgoing(ui, repo, dest=None, **opts):
2076 """show changesets not found in destination
2076 """show changesets not found in destination
2077
2077
2078 Show changesets not found in the specified destination repository
2078 Show changesets not found in the specified destination repository
2079 or the default push location. These are the changesets that would
2079 or the default push location. These are the changesets that would
2080 be pushed if a push was requested.
2080 be pushed if a push was requested.
2081
2081
2082 See pull for valid destination format details.
2082 See pull for valid destination format details.
2083 """
2083 """
2084 limit = cmdutil.loglimit(opts)
2084 limit = cmdutil.loglimit(opts)
2085 dest, revs, checkout = hg.parseurl(
2085 dest, revs, checkout = hg.parseurl(
2086 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2086 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2087 if revs:
2087 if revs:
2088 revs = [repo.lookup(rev) for rev in revs]
2088 revs = [repo.lookup(rev) for rev in revs]
2089
2089
2090 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2090 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2091 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2091 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2092 o = repo.findoutgoing(other, force=opts.get('force'))
2092 o = repo.findoutgoing(other, force=opts.get('force'))
2093 if not o:
2093 if not o:
2094 ui.status(_("no changes found\n"))
2094 ui.status(_("no changes found\n"))
2095 return 1
2095 return 1
2096 o = repo.changelog.nodesbetween(o, revs)[0]
2096 o = repo.changelog.nodesbetween(o, revs)[0]
2097 if opts.get('newest_first'):
2097 if opts.get('newest_first'):
2098 o.reverse()
2098 o.reverse()
2099 displayer = cmdutil.show_changeset(ui, repo, opts)
2099 displayer = cmdutil.show_changeset(ui, repo, opts)
2100 count = 0
2100 count = 0
2101 for n in o:
2101 for n in o:
2102 if count >= limit:
2102 if count >= limit:
2103 break
2103 break
2104 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2104 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2105 if opts.get('no_merges') and len(parents) == 2:
2105 if opts.get('no_merges') and len(parents) == 2:
2106 continue
2106 continue
2107 count += 1
2107 count += 1
2108 displayer.show(repo[n])
2108 displayer.show(repo[n])
2109
2109
2110 def parents(ui, repo, file_=None, **opts):
2110 def parents(ui, repo, file_=None, **opts):
2111 """show the parents of the working directory or revision
2111 """show the parents of the working directory or revision
2112
2112
2113 Print the working directory's parent revisions. If a revision is
2113 Print the working directory's parent revisions. If a revision is
2114 given via -r/--rev, the parent of that revision will be printed.
2114 given via -r/--rev, the parent of that revision will be printed.
2115 If a file argument is given, revision in which the file was last
2115 If a file argument is given, revision in which the file was last
2116 changed (before the working directory revision or the argument to
2116 changed (before the working directory revision or the argument to
2117 --rev if given) is printed.
2117 --rev if given) is printed.
2118 """
2118 """
2119 rev = opts.get('rev')
2119 rev = opts.get('rev')
2120 if rev:
2120 if rev:
2121 ctx = repo[rev]
2121 ctx = repo[rev]
2122 else:
2122 else:
2123 ctx = repo[None]
2123 ctx = repo[None]
2124
2124
2125 if file_:
2125 if file_:
2126 m = cmdutil.match(repo, (file_,), opts)
2126 m = cmdutil.match(repo, (file_,), opts)
2127 if m.anypats() or len(m.files()) != 1:
2127 if m.anypats() or len(m.files()) != 1:
2128 raise util.Abort(_('can only specify an explicit file name'))
2128 raise util.Abort(_('can only specify an explicit file name'))
2129 file_ = m.files()[0]
2129 file_ = m.files()[0]
2130 filenodes = []
2130 filenodes = []
2131 for cp in ctx.parents():
2131 for cp in ctx.parents():
2132 if not cp:
2132 if not cp:
2133 continue
2133 continue
2134 try:
2134 try:
2135 filenodes.append(cp.filenode(file_))
2135 filenodes.append(cp.filenode(file_))
2136 except error.LookupError:
2136 except error.LookupError:
2137 pass
2137 pass
2138 if not filenodes:
2138 if not filenodes:
2139 raise util.Abort(_("'%s' not found in manifest!") % file_)
2139 raise util.Abort(_("'%s' not found in manifest!") % file_)
2140 fl = repo.file(file_)
2140 fl = repo.file(file_)
2141 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2141 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2142 else:
2142 else:
2143 p = [cp.node() for cp in ctx.parents()]
2143 p = [cp.node() for cp in ctx.parents()]
2144
2144
2145 displayer = cmdutil.show_changeset(ui, repo, opts)
2145 displayer = cmdutil.show_changeset(ui, repo, opts)
2146 for n in p:
2146 for n in p:
2147 if n != nullid:
2147 if n != nullid:
2148 displayer.show(repo[n])
2148 displayer.show(repo[n])
2149
2149
2150 def paths(ui, repo, search=None):
2150 def paths(ui, repo, search=None):
2151 """show aliases for remote repositories
2151 """show aliases for remote repositories
2152
2152
2153 Show definition of symbolic path name NAME. If no name is given,
2153 Show definition of symbolic path name NAME. If no name is given,
2154 show definition of available names.
2154 show definition of available names.
2155
2155
2156 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2156 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2157 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2157 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2158
2158
2159 See 'hg help urls' for more information.
2159 See 'hg help urls' for more information.
2160 """
2160 """
2161 if search:
2161 if search:
2162 for name, path in ui.configitems("paths"):
2162 for name, path in ui.configitems("paths"):
2163 if name == search:
2163 if name == search:
2164 ui.write("%s\n" % url.hidepassword(path))
2164 ui.write("%s\n" % url.hidepassword(path))
2165 return
2165 return
2166 ui.warn(_("not found!\n"))
2166 ui.warn(_("not found!\n"))
2167 return 1
2167 return 1
2168 else:
2168 else:
2169 for name, path in ui.configitems("paths"):
2169 for name, path in ui.configitems("paths"):
2170 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2170 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2171
2171
2172 def postincoming(ui, repo, modheads, optupdate, checkout):
2172 def postincoming(ui, repo, modheads, optupdate, checkout):
2173 if modheads == 0:
2173 if modheads == 0:
2174 return
2174 return
2175 if optupdate:
2175 if optupdate:
2176 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2176 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2177 return hg.update(repo, checkout)
2177 return hg.update(repo, checkout)
2178 else:
2178 else:
2179 ui.status(_("not updating, since new heads added\n"))
2179 ui.status(_("not updating, since new heads added\n"))
2180 if modheads > 1:
2180 if modheads > 1:
2181 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2181 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2182 else:
2182 else:
2183 ui.status(_("(run 'hg update' to get a working copy)\n"))
2183 ui.status(_("(run 'hg update' to get a working copy)\n"))
2184
2184
2185 def pull(ui, repo, source="default", **opts):
2185 def pull(ui, repo, source="default", **opts):
2186 """pull changes from the specified source
2186 """pull changes from the specified source
2187
2187
2188 Pull changes from a remote repository to the local one.
2188 Pull changes from a remote repository to the local one.
2189
2189
2190 This finds all changes from the repository at the specified path
2190 This finds all changes from the repository at the specified path
2191 or URL and adds them to the local repository. By default, this
2191 or URL and adds them to the local repository. By default, this
2192 does not update the copy of the project in the working directory.
2192 does not update the copy of the project in the working directory.
2193
2193
2194 Use hg incoming if you want to see what will be added by the next
2194 Use hg incoming if you want to see what will be added by the next
2195 pull without actually adding the changes to the repository.
2195 pull without actually adding the changes to the repository.
2196
2196
2197 If SOURCE is omitted, the 'default' path will be used.
2197 If SOURCE is omitted, the 'default' path will be used.
2198 See 'hg help urls' for more information.
2198 See 'hg help urls' for more information.
2199 """
2199 """
2200 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2200 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2201 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2201 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2202 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2202 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2203 if revs:
2203 if revs:
2204 try:
2204 try:
2205 revs = [other.lookup(rev) for rev in revs]
2205 revs = [other.lookup(rev) for rev in revs]
2206 except error.CapabilityError:
2206 except error.CapabilityError:
2207 err = _("Other repository doesn't support revision lookup, "
2207 err = _("Other repository doesn't support revision lookup, "
2208 "so a rev cannot be specified.")
2208 "so a rev cannot be specified.")
2209 raise util.Abort(err)
2209 raise util.Abort(err)
2210
2210
2211 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2211 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2212 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2212 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2213
2213
2214 def push(ui, repo, dest=None, **opts):
2214 def push(ui, repo, dest=None, **opts):
2215 """push changes to the specified destination
2215 """push changes to the specified destination
2216
2216
2217 Push changes from the local repository to the given destination.
2217 Push changes from the local repository to the given destination.
2218
2218
2219 This is the symmetrical operation for pull. It moves changes from
2219 This is the symmetrical operation for pull. It moves changes from
2220 the current repository to a different one. If the destination is
2220 the current repository to a different one. If the destination is
2221 local this is identical to a pull in that directory from the
2221 local this is identical to a pull in that directory from the
2222 current one.
2222 current one.
2223
2223
2224 By default, push will refuse to run if it detects the result would
2224 By default, push will refuse to run if it detects the result would
2225 increase the number of remote heads. This generally indicates the
2225 increase the number of remote heads. This generally indicates the
2226 the client has forgotten to pull and merge before pushing.
2226 the client has forgotten to pull and merge before pushing.
2227
2227
2228 If -r/--rev is used, the named revision and all its ancestors will
2228 If -r/--rev is used, the named revision and all its ancestors will
2229 be pushed to the remote repository.
2229 be pushed to the remote repository.
2230
2230
2231 Look at the help text for URLs for important details about ssh://
2231 Look at the help text for URLs for important details about ssh://
2232 URLs. If DESTINATION is omitted, a default path will be used.
2232 URLs. If DESTINATION is omitted, a default path will be used.
2233 See 'hg help urls' for more information.
2233 See 'hg help urls' for more information.
2234 """
2234 """
2235 dest, revs, checkout = hg.parseurl(
2235 dest, revs, checkout = hg.parseurl(
2236 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2236 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2237 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2237 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2238 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2238 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2239 if revs:
2239 if revs:
2240 revs = [repo.lookup(rev) for rev in revs]
2240 revs = [repo.lookup(rev) for rev in revs]
2241 r = repo.push(other, opts.get('force'), revs=revs)
2241 r = repo.push(other, opts.get('force'), revs=revs)
2242 return r == 0
2242 return r == 0
2243
2243
2244 def rawcommit(ui, repo, *pats, **opts):
2244 def rawcommit(ui, repo, *pats, **opts):
2245 """raw commit interface (DEPRECATED)
2245 """raw commit interface (DEPRECATED)
2246
2246
2247 (DEPRECATED)
2247 (DEPRECATED)
2248 Lowlevel commit, for use in helper scripts.
2248 Lowlevel commit, for use in helper scripts.
2249
2249
2250 This command is not intended to be used by normal users, as it is
2250 This command is not intended to be used by normal users, as it is
2251 primarily useful for importing from other SCMs.
2251 primarily useful for importing from other SCMs.
2252
2252
2253 This command is now deprecated and will be removed in a future
2253 This command is now deprecated and will be removed in a future
2254 release, please use debugsetparents and commit instead.
2254 release, please use debugsetparents and commit instead.
2255 """
2255 """
2256
2256
2257 ui.warn(_("(the rawcommit command is deprecated)\n"))
2257 ui.warn(_("(the rawcommit command is deprecated)\n"))
2258
2258
2259 message = cmdutil.logmessage(opts)
2259 message = cmdutil.logmessage(opts)
2260
2260
2261 files = cmdutil.match(repo, pats, opts).files()
2261 files = cmdutil.match(repo, pats, opts).files()
2262 if opts.get('files'):
2262 if opts.get('files'):
2263 files += open(opts['files']).read().splitlines()
2263 files += open(opts['files']).read().splitlines()
2264
2264
2265 parents = [repo.lookup(p) for p in opts['parent']]
2265 parents = [repo.lookup(p) for p in opts['parent']]
2266
2266
2267 try:
2267 try:
2268 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2268 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2269 except ValueError, inst:
2269 except ValueError, inst:
2270 raise util.Abort(str(inst))
2270 raise util.Abort(str(inst))
2271
2271
2272 def recover(ui, repo):
2272 def recover(ui, repo):
2273 """roll back an interrupted transaction
2273 """roll back an interrupted transaction
2274
2274
2275 Recover from an interrupted commit or pull.
2275 Recover from an interrupted commit or pull.
2276
2276
2277 This command tries to fix the repository status after an
2277 This command tries to fix the repository status after an
2278 interrupted operation. It should only be necessary when Mercurial
2278 interrupted operation. It should only be necessary when Mercurial
2279 suggests it.
2279 suggests it.
2280 """
2280 """
2281 if repo.recover():
2281 if repo.recover():
2282 return hg.verify(repo)
2282 return hg.verify(repo)
2283 return 1
2283 return 1
2284
2284
2285 def remove(ui, repo, *pats, **opts):
2285 def remove(ui, repo, *pats, **opts):
2286 """remove the specified files on the next commit
2286 """remove the specified files on the next commit
2287
2287
2288 Schedule the indicated files for removal from the repository.
2288 Schedule the indicated files for removal from the repository.
2289
2289
2290 This only removes files from the current branch, not from the
2290 This only removes files from the current branch, not from the
2291 entire project history. -A/--after can be used to remove only
2291 entire project history. -A/--after can be used to remove only
2292 files that have already been deleted, -f/--force can be used to
2292 files that have already been deleted, -f/--force can be used to
2293 force deletion, and -Af can be used to remove files from the next
2293 force deletion, and -Af can be used to remove files from the next
2294 revision without deleting them.
2294 revision without deleting them.
2295
2295
2296 The following table details the behavior of remove for different
2296 The following table details the behavior of remove for different
2297 file states (columns) and option combinations (rows). The file
2297 file states (columns) and option combinations (rows). The file
2298 states are Added, Clean, Modified and Missing (as reported by hg
2298 states are Added, Clean, Modified and Missing (as reported by hg
2299 status). The actions are Warn, Remove (from branch) and Delete
2299 status). The actions are Warn, Remove (from branch) and Delete
2300 (from disk).
2300 (from disk).
2301
2301
2302 A C M !
2302 A C M !
2303 none W RD W R
2303 none W RD W R
2304 -f R RD RD R
2304 -f R RD RD R
2305 -A W W W R
2305 -A W W W R
2306 -Af R R R R
2306 -Af R R R R
2307
2307
2308 This command schedules the files to be removed at the next commit.
2308 This command schedules the files to be removed at the next commit.
2309 To undo a remove before that, see hg revert.
2309 To undo a remove before that, see hg revert.
2310 """
2310 """
2311
2311
2312 after, force = opts.get('after'), opts.get('force')
2312 after, force = opts.get('after'), opts.get('force')
2313 if not pats and not after:
2313 if not pats and not after:
2314 raise util.Abort(_('no files specified'))
2314 raise util.Abort(_('no files specified'))
2315
2315
2316 m = cmdutil.match(repo, pats, opts)
2316 m = cmdutil.match(repo, pats, opts)
2317 s = repo.status(match=m, clean=True)
2317 s = repo.status(match=m, clean=True)
2318 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2318 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2319
2319
2320 def warn(files, reason):
2320 def warn(files, reason):
2321 for f in files:
2321 for f in files:
2322 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2322 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2323 % (m.rel(f), reason))
2323 % (m.rel(f), reason))
2324
2324
2325 if force:
2325 if force:
2326 remove, forget = modified + deleted + clean, added
2326 remove, forget = modified + deleted + clean, added
2327 elif after:
2327 elif after:
2328 remove, forget = deleted, []
2328 remove, forget = deleted, []
2329 warn(modified + added + clean, _('still exists'))
2329 warn(modified + added + clean, _('still exists'))
2330 else:
2330 else:
2331 remove, forget = deleted + clean, []
2331 remove, forget = deleted + clean, []
2332 warn(modified, _('is modified'))
2332 warn(modified, _('is modified'))
2333 warn(added, _('has been marked for add'))
2333 warn(added, _('has been marked for add'))
2334
2334
2335 for f in sorted(remove + forget):
2335 for f in sorted(remove + forget):
2336 if ui.verbose or not m.exact(f):
2336 if ui.verbose or not m.exact(f):
2337 ui.status(_('removing %s\n') % m.rel(f))
2337 ui.status(_('removing %s\n') % m.rel(f))
2338
2338
2339 repo.forget(forget)
2339 repo.forget(forget)
2340 repo.remove(remove, unlink=not after)
2340 repo.remove(remove, unlink=not after)
2341
2341
2342 def rename(ui, repo, *pats, **opts):
2342 def rename(ui, repo, *pats, **opts):
2343 """rename files; equivalent of copy + remove
2343 """rename files; equivalent of copy + remove
2344
2344
2345 Mark dest as copies of sources; mark sources for deletion. If dest
2345 Mark dest as copies of sources; mark sources for deletion. If dest
2346 is a directory, copies are put in that directory. If dest is a
2346 is a directory, copies are put in that directory. If dest is a
2347 file, there can only be one source.
2347 file, there can only be one source.
2348
2348
2349 By default, this command copies the contents of files as they
2349 By default, this command copies the contents of files as they
2350 exist in the working directory. If invoked with -A/--after, the
2350 exist in the working directory. If invoked with -A/--after, the
2351 operation is recorded, but no copying is performed.
2351 operation is recorded, but no copying is performed.
2352
2352
2353 This command takes effect at the next commit. To undo a rename
2353 This command takes effect at the next commit. To undo a rename
2354 before that, see hg revert.
2354 before that, see hg revert.
2355 """
2355 """
2356 wlock = repo.wlock(False)
2356 wlock = repo.wlock(False)
2357 try:
2357 try:
2358 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2358 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2359 finally:
2359 finally:
2360 wlock.release()
2360 wlock.release()
2361
2361
2362 def resolve(ui, repo, *pats, **opts):
2362 def resolve(ui, repo, *pats, **opts):
2363 """retry file merges from a merge or update
2363 """retry file merges from a merge or update
2364
2364
2365 This command will cleanly retry unresolved file merges using file
2365 This command will cleanly retry unresolved file merges using file
2366 revisions preserved from the last update or merge. To attempt to
2366 revisions preserved from the last update or merge. To attempt to
2367 resolve all unresolved files, use the -a/--all switch.
2367 resolve all unresolved files, use the -a/--all switch.
2368
2368
2369 If a conflict is resolved manually, please note that the changes
2369 If a conflict is resolved manually, please note that the changes
2370 will be overwritten if the merge is retried with resolve. The
2370 will be overwritten if the merge is retried with resolve. The
2371 -m/--mark switch should be used to mark the file as resolved.
2371 -m/--mark switch should be used to mark the file as resolved.
2372
2372
2373 This command will also allow listing resolved files and manually
2373 This command will also allow listing resolved files and manually
2374 marking and unmarking files as resolved. All files must be marked
2374 marking and unmarking files as resolved. All files must be marked
2375 as resolved before the new commits are permitted.
2375 as resolved before the new commits are permitted.
2376
2376
2377 The codes used to show the status of files are:
2377 The codes used to show the status of files are:
2378 U = unresolved
2378 U = unresolved
2379 R = resolved
2379 R = resolved
2380 """
2380 """
2381
2381
2382 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2382 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2383
2383
2384 if (show and (mark or unmark)) or (mark and unmark):
2384 if (show and (mark or unmark)) or (mark and unmark):
2385 raise util.Abort(_("too many options specified"))
2385 raise util.Abort(_("too many options specified"))
2386 if pats and all:
2386 if pats and all:
2387 raise util.Abort(_("can't specify --all and patterns"))
2387 raise util.Abort(_("can't specify --all and patterns"))
2388 if not (all or pats or show or mark or unmark):
2388 if not (all or pats or show or mark or unmark):
2389 raise util.Abort(_('no files or directories specified; '
2389 raise util.Abort(_('no files or directories specified; '
2390 'use --all to remerge all files'))
2390 'use --all to remerge all files'))
2391
2391
2392 ms = merge_.mergestate(repo)
2392 ms = merge_.mergestate(repo)
2393 m = cmdutil.match(repo, pats, opts)
2393 m = cmdutil.match(repo, pats, opts)
2394
2394
2395 for f in ms:
2395 for f in ms:
2396 if m(f):
2396 if m(f):
2397 if show:
2397 if show:
2398 ui.write("%s %s\n" % (ms[f].upper(), f))
2398 ui.write("%s %s\n" % (ms[f].upper(), f))
2399 elif mark:
2399 elif mark:
2400 ms.mark(f, "r")
2400 ms.mark(f, "r")
2401 elif unmark:
2401 elif unmark:
2402 ms.mark(f, "u")
2402 ms.mark(f, "u")
2403 else:
2403 else:
2404 wctx = repo[None]
2404 wctx = repo[None]
2405 mctx = wctx.parents()[-1]
2405 mctx = wctx.parents()[-1]
2406
2406
2407 # backup pre-resolve (merge uses .orig for its own purposes)
2407 # backup pre-resolve (merge uses .orig for its own purposes)
2408 a = repo.wjoin(f)
2408 a = repo.wjoin(f)
2409 util.copyfile(a, a + ".resolve")
2409 util.copyfile(a, a + ".resolve")
2410
2410
2411 # resolve file
2411 # resolve file
2412 ms.resolve(f, wctx, mctx)
2412 ms.resolve(f, wctx, mctx)
2413
2413
2414 # replace filemerge's .orig file with our resolve file
2414 # replace filemerge's .orig file with our resolve file
2415 util.rename(a + ".resolve", a + ".orig")
2415 util.rename(a + ".resolve", a + ".orig")
2416
2416
2417 def revert(ui, repo, *pats, **opts):
2417 def revert(ui, repo, *pats, **opts):
2418 """restore individual files or directories to an earlier state
2418 """restore individual files or directories to an earlier state
2419
2419
2420 (Use update -r to check out earlier revisions, revert does not
2420 (Use update -r to check out earlier revisions, revert does not
2421 change the working directory parents.)
2421 change the working directory parents.)
2422
2422
2423 With no revision specified, revert the named files or directories
2423 With no revision specified, revert the named files or directories
2424 to the contents they had in the parent of the working directory.
2424 to the contents they had in the parent of the working directory.
2425 This restores the contents of the affected files to an unmodified
2425 This restores the contents of the affected files to an unmodified
2426 state and unschedules adds, removes, copies, and renames. If the
2426 state and unschedules adds, removes, copies, and renames. If the
2427 working directory has two parents, you must explicitly specify the
2427 working directory has two parents, you must explicitly specify the
2428 revision to revert to.
2428 revision to revert to.
2429
2429
2430 Using the -r/--rev option, revert the given files or directories
2430 Using the -r/--rev option, revert the given files or directories
2431 to their contents as of a specific revision. This can be helpful
2431 to their contents as of a specific revision. This can be helpful
2432 to "roll back" some or all of an earlier change. See 'hg help
2432 to "roll back" some or all of an earlier change. See 'hg help
2433 dates' for a list of formats valid for -d/--date.
2433 dates' for a list of formats valid for -d/--date.
2434
2434
2435 Revert modifies the working directory. It does not commit any
2435 Revert modifies the working directory. It does not commit any
2436 changes, or change the parent of the working directory. If you
2436 changes, or change the parent of the working directory. If you
2437 revert to a revision other than the parent of the working
2437 revert to a revision other than the parent of the working
2438 directory, the reverted files will thus appear modified
2438 directory, the reverted files will thus appear modified
2439 afterwards.
2439 afterwards.
2440
2440
2441 If a file has been deleted, it is restored. If the executable mode
2441 If a file has been deleted, it is restored. If the executable mode
2442 of a file was changed, it is reset.
2442 of a file was changed, it is reset.
2443
2443
2444 If names are given, all files matching the names are reverted.
2444 If names are given, all files matching the names are reverted.
2445 If no arguments are given, no files are reverted.
2445 If no arguments are given, no files are reverted.
2446
2446
2447 Modified files are saved with a .orig suffix before reverting.
2447 Modified files are saved with a .orig suffix before reverting.
2448 To disable these backups, use --no-backup.
2448 To disable these backups, use --no-backup.
2449 """
2449 """
2450
2450
2451 if opts["date"]:
2451 if opts["date"]:
2452 if opts["rev"]:
2452 if opts["rev"]:
2453 raise util.Abort(_("you can't specify a revision and a date"))
2453 raise util.Abort(_("you can't specify a revision and a date"))
2454 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2454 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2455
2455
2456 if not pats and not opts.get('all'):
2456 if not pats and not opts.get('all'):
2457 raise util.Abort(_('no files or directories specified; '
2457 raise util.Abort(_('no files or directories specified; '
2458 'use --all to revert the whole repo'))
2458 'use --all to revert the whole repo'))
2459
2459
2460 parent, p2 = repo.dirstate.parents()
2460 parent, p2 = repo.dirstate.parents()
2461 if not opts.get('rev') and p2 != nullid:
2461 if not opts.get('rev') and p2 != nullid:
2462 raise util.Abort(_('uncommitted merge - please provide a '
2462 raise util.Abort(_('uncommitted merge - please provide a '
2463 'specific revision'))
2463 'specific revision'))
2464 ctx = repo[opts.get('rev')]
2464 ctx = repo[opts.get('rev')]
2465 node = ctx.node()
2465 node = ctx.node()
2466 mf = ctx.manifest()
2466 mf = ctx.manifest()
2467 if node == parent:
2467 if node == parent:
2468 pmf = mf
2468 pmf = mf
2469 else:
2469 else:
2470 pmf = None
2470 pmf = None
2471
2471
2472 # need all matching names in dirstate and manifest of target rev,
2472 # need all matching names in dirstate and manifest of target rev,
2473 # so have to walk both. do not print errors if files exist in one
2473 # so have to walk both. do not print errors if files exist in one
2474 # but not other.
2474 # but not other.
2475
2475
2476 names = {}
2476 names = {}
2477
2477
2478 wlock = repo.wlock()
2478 wlock = repo.wlock()
2479 try:
2479 try:
2480 # walk dirstate.
2480 # walk dirstate.
2481
2481
2482 m = cmdutil.match(repo, pats, opts)
2482 m = cmdutil.match(repo, pats, opts)
2483 m.bad = lambda x,y: False
2483 m.bad = lambda x,y: False
2484 for abs in repo.walk(m):
2484 for abs in repo.walk(m):
2485 names[abs] = m.rel(abs), m.exact(abs)
2485 names[abs] = m.rel(abs), m.exact(abs)
2486
2486
2487 # walk target manifest.
2487 # walk target manifest.
2488
2488
2489 def badfn(path, msg):
2489 def badfn(path, msg):
2490 if path in names:
2490 if path in names:
2491 return False
2491 return False
2492 path_ = path + '/'
2492 path_ = path + '/'
2493 for f in names:
2493 for f in names:
2494 if f.startswith(path_):
2494 if f.startswith(path_):
2495 return False
2495 return False
2496 repo.ui.warn("%s: %s\n" % (m.rel(path), msg))
2496 repo.ui.warn("%s: %s\n" % (m.rel(path), msg))
2497 return False
2497 return False
2498
2498
2499 m = cmdutil.match(repo, pats, opts)
2499 m = cmdutil.match(repo, pats, opts)
2500 m.bad = badfn
2500 m.bad = badfn
2501 for abs in repo[node].walk(m):
2501 for abs in repo[node].walk(m):
2502 if abs not in names:
2502 if abs not in names:
2503 names[abs] = m.rel(abs), m.exact(abs)
2503 names[abs] = m.rel(abs), m.exact(abs)
2504
2504
2505 m = cmdutil.matchfiles(repo, names)
2505 m = cmdutil.matchfiles(repo, names)
2506 changes = repo.status(match=m)[:4]
2506 changes = repo.status(match=m)[:4]
2507 modified, added, removed, deleted = map(set, changes)
2507 modified, added, removed, deleted = map(set, changes)
2508
2508
2509 # if f is a rename, also revert the source
2509 # if f is a rename, also revert the source
2510 cwd = repo.getcwd()
2510 cwd = repo.getcwd()
2511 for f in added:
2511 for f in added:
2512 src = repo.dirstate.copied(f)
2512 src = repo.dirstate.copied(f)
2513 if src and src not in names and repo.dirstate[src] == 'r':
2513 if src and src not in names and repo.dirstate[src] == 'r':
2514 removed.add(src)
2514 removed.add(src)
2515 names[src] = (repo.pathto(src, cwd), True)
2515 names[src] = (repo.pathto(src, cwd), True)
2516
2516
2517 def removeforget(abs):
2517 def removeforget(abs):
2518 if repo.dirstate[abs] == 'a':
2518 if repo.dirstate[abs] == 'a':
2519 return _('forgetting %s\n')
2519 return _('forgetting %s\n')
2520 return _('removing %s\n')
2520 return _('removing %s\n')
2521
2521
2522 revert = ([], _('reverting %s\n'))
2522 revert = ([], _('reverting %s\n'))
2523 add = ([], _('adding %s\n'))
2523 add = ([], _('adding %s\n'))
2524 remove = ([], removeforget)
2524 remove = ([], removeforget)
2525 undelete = ([], _('undeleting %s\n'))
2525 undelete = ([], _('undeleting %s\n'))
2526
2526
2527 disptable = (
2527 disptable = (
2528 # dispatch table:
2528 # dispatch table:
2529 # file state
2529 # file state
2530 # action if in target manifest
2530 # action if in target manifest
2531 # action if not in target manifest
2531 # action if not in target manifest
2532 # make backup if in target manifest
2532 # make backup if in target manifest
2533 # make backup if not in target manifest
2533 # make backup if not in target manifest
2534 (modified, revert, remove, True, True),
2534 (modified, revert, remove, True, True),
2535 (added, revert, remove, True, False),
2535 (added, revert, remove, True, False),
2536 (removed, undelete, None, False, False),
2536 (removed, undelete, None, False, False),
2537 (deleted, revert, remove, False, False),
2537 (deleted, revert, remove, False, False),
2538 )
2538 )
2539
2539
2540 for abs, (rel, exact) in sorted(names.items()):
2540 for abs, (rel, exact) in sorted(names.items()):
2541 mfentry = mf.get(abs)
2541 mfentry = mf.get(abs)
2542 target = repo.wjoin(abs)
2542 target = repo.wjoin(abs)
2543 def handle(xlist, dobackup):
2543 def handle(xlist, dobackup):
2544 xlist[0].append(abs)
2544 xlist[0].append(abs)
2545 if dobackup and not opts.get('no_backup') and util.lexists(target):
2545 if dobackup and not opts.get('no_backup') and util.lexists(target):
2546 bakname = "%s.orig" % rel
2546 bakname = "%s.orig" % rel
2547 ui.note(_('saving current version of %s as %s\n') %
2547 ui.note(_('saving current version of %s as %s\n') %
2548 (rel, bakname))
2548 (rel, bakname))
2549 if not opts.get('dry_run'):
2549 if not opts.get('dry_run'):
2550 util.copyfile(target, bakname)
2550 util.copyfile(target, bakname)
2551 if ui.verbose or not exact:
2551 if ui.verbose or not exact:
2552 msg = xlist[1]
2552 msg = xlist[1]
2553 if not isinstance(msg, basestring):
2553 if not isinstance(msg, basestring):
2554 msg = msg(abs)
2554 msg = msg(abs)
2555 ui.status(msg % rel)
2555 ui.status(msg % rel)
2556 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2556 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2557 if abs not in table: continue
2557 if abs not in table: continue
2558 # file has changed in dirstate
2558 # file has changed in dirstate
2559 if mfentry:
2559 if mfentry:
2560 handle(hitlist, backuphit)
2560 handle(hitlist, backuphit)
2561 elif misslist is not None:
2561 elif misslist is not None:
2562 handle(misslist, backupmiss)
2562 handle(misslist, backupmiss)
2563 break
2563 break
2564 else:
2564 else:
2565 if abs not in repo.dirstate:
2565 if abs not in repo.dirstate:
2566 if mfentry:
2566 if mfentry:
2567 handle(add, True)
2567 handle(add, True)
2568 elif exact:
2568 elif exact:
2569 ui.warn(_('file not managed: %s\n') % rel)
2569 ui.warn(_('file not managed: %s\n') % rel)
2570 continue
2570 continue
2571 # file has not changed in dirstate
2571 # file has not changed in dirstate
2572 if node == parent:
2572 if node == parent:
2573 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2573 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2574 continue
2574 continue
2575 if pmf is None:
2575 if pmf is None:
2576 # only need parent manifest in this unlikely case,
2576 # only need parent manifest in this unlikely case,
2577 # so do not read by default
2577 # so do not read by default
2578 pmf = repo[parent].manifest()
2578 pmf = repo[parent].manifest()
2579 if abs in pmf:
2579 if abs in pmf:
2580 if mfentry:
2580 if mfentry:
2581 # if version of file is same in parent and target
2581 # if version of file is same in parent and target
2582 # manifests, do nothing
2582 # manifests, do nothing
2583 if (pmf[abs] != mfentry or
2583 if (pmf[abs] != mfentry or
2584 pmf.flags(abs) != mf.flags(abs)):
2584 pmf.flags(abs) != mf.flags(abs)):
2585 handle(revert, False)
2585 handle(revert, False)
2586 else:
2586 else:
2587 handle(remove, False)
2587 handle(remove, False)
2588
2588
2589 if not opts.get('dry_run'):
2589 if not opts.get('dry_run'):
2590 def checkout(f):
2590 def checkout(f):
2591 fc = ctx[f]
2591 fc = ctx[f]
2592 repo.wwrite(f, fc.data(), fc.flags())
2592 repo.wwrite(f, fc.data(), fc.flags())
2593
2593
2594 audit_path = util.path_auditor(repo.root)
2594 audit_path = util.path_auditor(repo.root)
2595 for f in remove[0]:
2595 for f in remove[0]:
2596 if repo.dirstate[f] == 'a':
2596 if repo.dirstate[f] == 'a':
2597 repo.dirstate.forget(f)
2597 repo.dirstate.forget(f)
2598 continue
2598 continue
2599 audit_path(f)
2599 audit_path(f)
2600 try:
2600 try:
2601 util.unlink(repo.wjoin(f))
2601 util.unlink(repo.wjoin(f))
2602 except OSError:
2602 except OSError:
2603 pass
2603 pass
2604 repo.dirstate.remove(f)
2604 repo.dirstate.remove(f)
2605
2605
2606 normal = None
2606 normal = None
2607 if node == parent:
2607 if node == parent:
2608 # We're reverting to our parent. If possible, we'd like status
2608 # We're reverting to our parent. If possible, we'd like status
2609 # to report the file as clean. We have to use normallookup for
2609 # to report the file as clean. We have to use normallookup for
2610 # merges to avoid losing information about merged/dirty files.
2610 # merges to avoid losing information about merged/dirty files.
2611 if p2 != nullid:
2611 if p2 != nullid:
2612 normal = repo.dirstate.normallookup
2612 normal = repo.dirstate.normallookup
2613 else:
2613 else:
2614 normal = repo.dirstate.normal
2614 normal = repo.dirstate.normal
2615 for f in revert[0]:
2615 for f in revert[0]:
2616 checkout(f)
2616 checkout(f)
2617 if normal:
2617 if normal:
2618 normal(f)
2618 normal(f)
2619
2619
2620 for f in add[0]:
2620 for f in add[0]:
2621 checkout(f)
2621 checkout(f)
2622 repo.dirstate.add(f)
2622 repo.dirstate.add(f)
2623
2623
2624 normal = repo.dirstate.normallookup
2624 normal = repo.dirstate.normallookup
2625 if node == parent and p2 == nullid:
2625 if node == parent and p2 == nullid:
2626 normal = repo.dirstate.normal
2626 normal = repo.dirstate.normal
2627 for f in undelete[0]:
2627 for f in undelete[0]:
2628 checkout(f)
2628 checkout(f)
2629 normal(f)
2629 normal(f)
2630
2630
2631 finally:
2631 finally:
2632 wlock.release()
2632 wlock.release()
2633
2633
2634 def rollback(ui, repo):
2634 def rollback(ui, repo):
2635 """roll back the last transaction
2635 """roll back the last transaction
2636
2636
2637 This command should be used with care. There is only one level of
2637 This command should be used with care. There is only one level of
2638 rollback, and there is no way to undo a rollback. It will also
2638 rollback, and there is no way to undo a rollback. It will also
2639 restore the dirstate at the time of the last transaction, losing
2639 restore the dirstate at the time of the last transaction, losing
2640 any dirstate changes since that time.
2640 any dirstate changes since that time.
2641
2641
2642 Transactions are used to encapsulate the effects of all commands
2642 Transactions are used to encapsulate the effects of all commands
2643 that create new changesets or propagate existing changesets into a
2643 that create new changesets or propagate existing changesets into a
2644 repository. For example, the following commands are transactional,
2644 repository. For example, the following commands are transactional,
2645 and their effects can be rolled back:
2645 and their effects can be rolled back:
2646
2646
2647 commit
2647 commit
2648 import
2648 import
2649 pull
2649 pull
2650 push (with this repository as destination)
2650 push (with this repository as destination)
2651 unbundle
2651 unbundle
2652
2652
2653 This command is not intended for use on public repositories. Once
2653 This command is not intended for use on public repositories. Once
2654 changes are visible for pull by other users, rolling a transaction
2654 changes are visible for pull by other users, rolling a transaction
2655 back locally is ineffective (someone else may already have pulled
2655 back locally is ineffective (someone else may already have pulled
2656 the changes). Furthermore, a race is possible with readers of the
2656 the changes). Furthermore, a race is possible with readers of the
2657 repository; for example an in-progress pull from the repository
2657 repository; for example an in-progress pull from the repository
2658 may fail if a rollback is performed.
2658 may fail if a rollback is performed.
2659 """
2659 """
2660 repo.rollback()
2660 repo.rollback()
2661
2661
2662 def root(ui, repo):
2662 def root(ui, repo):
2663 """print the root (top) of the current working directory
2663 """print the root (top) of the current working directory
2664
2664
2665 Print the root directory of the current repository.
2665 Print the root directory of the current repository.
2666 """
2666 """
2667 ui.write(repo.root + "\n")
2667 ui.write(repo.root + "\n")
2668
2668
2669 def serve(ui, repo, **opts):
2669 def serve(ui, repo, **opts):
2670 """export the repository via HTTP
2670 """export the repository via HTTP
2671
2671
2672 Start a local HTTP repository browser and pull server.
2672 Start a local HTTP repository browser and pull server.
2673
2673
2674 By default, the server logs accesses to stdout and errors to
2674 By default, the server logs accesses to stdout and errors to
2675 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2675 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2676 files.
2676 files.
2677 """
2677 """
2678
2678
2679 if opts["stdio"]:
2679 if opts["stdio"]:
2680 if repo is None:
2680 if repo is None:
2681 raise error.RepoError(_("There is no Mercurial repository here"
2681 raise error.RepoError(_("There is no Mercurial repository here"
2682 " (.hg not found)"))
2682 " (.hg not found)"))
2683 s = sshserver.sshserver(ui, repo)
2683 s = sshserver.sshserver(ui, repo)
2684 s.serve_forever()
2684 s.serve_forever()
2685
2685
2686 baseui = repo and repo.baseui or ui
2686 baseui = repo and repo.baseui or ui
2687 optlist = ("name templates style address port prefix ipv6"
2687 optlist = ("name templates style address port prefix ipv6"
2688 " accesslog errorlog webdir_conf certificate")
2688 " accesslog errorlog webdir_conf certificate")
2689 for o in optlist.split():
2689 for o in optlist.split():
2690 if opts[o]:
2690 if opts[o]:
2691 baseui.setconfig("web", o, str(opts[o]))
2691 baseui.setconfig("web", o, str(opts[o]))
2692 if (repo is not None) and (repo.ui != baseui):
2692 if (repo is not None) and (repo.ui != baseui):
2693 repo.ui.setconfig("web", o, str(opts[o]))
2693 repo.ui.setconfig("web", o, str(opts[o]))
2694
2694
2695 if repo is None and not ui.config("web", "webdir_conf"):
2695 if repo is None and not ui.config("web", "webdir_conf"):
2696 raise error.RepoError(_("There is no Mercurial repository here"
2696 raise error.RepoError(_("There is no Mercurial repository here"
2697 " (.hg not found)"))
2697 " (.hg not found)"))
2698
2698
2699 class service:
2699 class service:
2700 def init(self):
2700 def init(self):
2701 util.set_signal_handler()
2701 util.set_signal_handler()
2702 self.httpd = server.create_server(baseui, repo)
2702 self.httpd = server.create_server(baseui, repo)
2703
2703
2704 if not ui.verbose: return
2704 if not ui.verbose: return
2705
2705
2706 if self.httpd.prefix:
2706 if self.httpd.prefix:
2707 prefix = self.httpd.prefix.strip('/') + '/'
2707 prefix = self.httpd.prefix.strip('/') + '/'
2708 else:
2708 else:
2709 prefix = ''
2709 prefix = ''
2710
2710
2711 port = ':%d' % self.httpd.port
2711 port = ':%d' % self.httpd.port
2712 if port == ':80':
2712 if port == ':80':
2713 port = ''
2713 port = ''
2714
2714
2715 bindaddr = self.httpd.addr
2715 bindaddr = self.httpd.addr
2716 if bindaddr == '0.0.0.0':
2716 if bindaddr == '0.0.0.0':
2717 bindaddr = '*'
2717 bindaddr = '*'
2718 elif ':' in bindaddr: # IPv6
2718 elif ':' in bindaddr: # IPv6
2719 bindaddr = '[%s]' % bindaddr
2719 bindaddr = '[%s]' % bindaddr
2720
2720
2721 fqaddr = self.httpd.fqaddr
2721 fqaddr = self.httpd.fqaddr
2722 if ':' in fqaddr:
2722 if ':' in fqaddr:
2723 fqaddr = '[%s]' % fqaddr
2723 fqaddr = '[%s]' % fqaddr
2724 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2724 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2725 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2725 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2726
2726
2727 def run(self):
2727 def run(self):
2728 self.httpd.serve_forever()
2728 self.httpd.serve_forever()
2729
2729
2730 service = service()
2730 service = service()
2731
2731
2732 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2732 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2733
2733
2734 def status(ui, repo, *pats, **opts):
2734 def status(ui, repo, *pats, **opts):
2735 """show changed files in the working directory
2735 """show changed files in the working directory
2736
2736
2737 Show status of files in the repository. If names are given, only
2737 Show status of files in the repository. If names are given, only
2738 files that match are shown. Files that are clean or ignored or
2738 files that match are shown. Files that are clean or ignored or
2739 source of a copy/move operation, are not listed unless -c/--clean,
2739 source of a copy/move operation, are not listed unless -c/--clean,
2740 -i/--ignored, -C/--copies or -A/--all is given. Unless options
2740 -i/--ignored, -C/--copies or -A/--all is given. Unless options
2741 described with "show only ..." are given, the options -mardu are
2741 described with "show only ..." are given, the options -mardu are
2742 used.
2742 used.
2743
2743
2744 Option -q/--quiet hides untracked (unknown and ignored) files
2744 Option -q/--quiet hides untracked (unknown and ignored) files
2745 unless explicitly requested with -u/--unknown or -i/--ignored.
2745 unless explicitly requested with -u/--unknown or -i/--ignored.
2746
2746
2747 NOTE: status may appear to disagree with diff if permissions have
2747 NOTE: status may appear to disagree with diff if permissions have
2748 changed or a merge has occurred. The standard diff format does not
2748 changed or a merge has occurred. The standard diff format does not
2749 report permission changes and diff only reports changes relative
2749 report permission changes and diff only reports changes relative
2750 to one merge parent.
2750 to one merge parent.
2751
2751
2752 If one revision is given, it is used as the base revision.
2752 If one revision is given, it is used as the base revision.
2753 If two revisions are given, the difference between them is shown.
2753 If two revisions are given, the difference between them is shown.
2754
2754
2755 The codes used to show the status of files are:
2755 The codes used to show the status of files are:
2756 M = modified
2756 M = modified
2757 A = added
2757 A = added
2758 R = removed
2758 R = removed
2759 C = clean
2759 C = clean
2760 ! = missing (deleted by non-hg command, but still tracked)
2760 ! = missing (deleted by non-hg command, but still tracked)
2761 ? = not tracked
2761 ? = not tracked
2762 I = ignored
2762 I = ignored
2763 = the previous added file was copied from here
2763 = the previous added file was copied from here
2764 """
2764 """
2765
2765
2766 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2766 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2767 cwd = (pats and repo.getcwd()) or ''
2767 cwd = (pats and repo.getcwd()) or ''
2768 end = opts.get('print0') and '\0' or '\n'
2768 end = opts.get('print0') and '\0' or '\n'
2769 copy = {}
2769 copy = {}
2770 states = 'modified added removed deleted unknown ignored clean'.split()
2770 states = 'modified added removed deleted unknown ignored clean'.split()
2771 show = [k for k in states if opts.get(k)]
2771 show = [k for k in states if opts.get(k)]
2772 if opts.get('all'):
2772 if opts.get('all'):
2773 show += ui.quiet and (states[:4] + ['clean']) or states
2773 show += ui.quiet and (states[:4] + ['clean']) or states
2774 if not show:
2774 if not show:
2775 show = ui.quiet and states[:4] or states[:5]
2775 show = ui.quiet and states[:4] or states[:5]
2776
2776
2777 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2777 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2778 'ignored' in show, 'clean' in show, 'unknown' in show)
2778 'ignored' in show, 'clean' in show, 'unknown' in show)
2779 changestates = zip(states, 'MAR!?IC', stat)
2779 changestates = zip(states, 'MAR!?IC', stat)
2780
2780
2781 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2781 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2782 ctxn = repo[nullid]
2782 ctxn = repo[nullid]
2783 ctx1 = repo[node1]
2783 ctx1 = repo[node1]
2784 ctx2 = repo[node2]
2784 ctx2 = repo[node2]
2785 added = stat[1]
2785 added = stat[1]
2786 if node2 is None:
2786 if node2 is None:
2787 added = stat[0] + stat[1] # merged?
2787 added = stat[0] + stat[1] # merged?
2788
2788
2789 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2789 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2790 if k in added:
2790 if k in added:
2791 copy[k] = v
2791 copy[k] = v
2792 elif v in added:
2792 elif v in added:
2793 copy[v] = k
2793 copy[v] = k
2794
2794
2795 for state, char, files in changestates:
2795 for state, char, files in changestates:
2796 if state in show:
2796 if state in show:
2797 format = "%s %%s%s" % (char, end)
2797 format = "%s %%s%s" % (char, end)
2798 if opts.get('no_status'):
2798 if opts.get('no_status'):
2799 format = "%%s%s" % end
2799 format = "%%s%s" % end
2800
2800
2801 for f in files:
2801 for f in files:
2802 ui.write(format % repo.pathto(f, cwd))
2802 ui.write(format % repo.pathto(f, cwd))
2803 if f in copy:
2803 if f in copy:
2804 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2804 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2805
2805
2806 def tag(ui, repo, name1, *names, **opts):
2806 def tag(ui, repo, name1, *names, **opts):
2807 """add one or more tags for the current or given revision
2807 """add one or more tags for the current or given revision
2808
2808
2809 Name a particular revision using <name>.
2809 Name a particular revision using <name>.
2810
2810
2811 Tags are used to name particular revisions of the repository and are
2811 Tags are used to name particular revisions of the repository and are
2812 very useful to compare different revisions, to go back to significant
2812 very useful to compare different revisions, to go back to significant
2813 earlier versions or to mark branch points as releases, etc.
2813 earlier versions or to mark branch points as releases, etc.
2814
2814
2815 If no revision is given, the parent of the working directory is
2815 If no revision is given, the parent of the working directory is
2816 used, or tip if no revision is checked out.
2816 used, or tip if no revision is checked out.
2817
2817
2818 To facilitate version control, distribution, and merging of tags,
2818 To facilitate version control, distribution, and merging of tags,
2819 they are stored as a file named ".hgtags" which is managed
2819 they are stored as a file named ".hgtags" which is managed
2820 similarly to other project files and can be hand-edited if
2820 similarly to other project files and can be hand-edited if
2821 necessary. The file '.hg/localtags' is used for local tags (not
2821 necessary. The file '.hg/localtags' is used for local tags (not
2822 shared among repositories).
2822 shared among repositories).
2823
2823
2824 See 'hg help dates' for a list of formats valid for -d/--date.
2824 See 'hg help dates' for a list of formats valid for -d/--date.
2825 """
2825 """
2826
2826
2827 rev_ = "."
2827 rev_ = "."
2828 names = (name1,) + names
2828 names = (name1,) + names
2829 if len(names) != len(set(names)):
2829 if len(names) != len(set(names)):
2830 raise util.Abort(_('tag names must be unique'))
2830 raise util.Abort(_('tag names must be unique'))
2831 for n in names:
2831 for n in names:
2832 if n in ['tip', '.', 'null']:
2832 if n in ['tip', '.', 'null']:
2833 raise util.Abort(_('the name \'%s\' is reserved') % n)
2833 raise util.Abort(_('the name \'%s\' is reserved') % n)
2834 if opts.get('rev') and opts.get('remove'):
2834 if opts.get('rev') and opts.get('remove'):
2835 raise util.Abort(_("--rev and --remove are incompatible"))
2835 raise util.Abort(_("--rev and --remove are incompatible"))
2836 if opts.get('rev'):
2836 if opts.get('rev'):
2837 rev_ = opts['rev']
2837 rev_ = opts['rev']
2838 message = opts.get('message')
2838 message = opts.get('message')
2839 if opts.get('remove'):
2839 if opts.get('remove'):
2840 expectedtype = opts.get('local') and 'local' or 'global'
2840 expectedtype = opts.get('local') and 'local' or 'global'
2841 for n in names:
2841 for n in names:
2842 if not repo.tagtype(n):
2842 if not repo.tagtype(n):
2843 raise util.Abort(_('tag \'%s\' does not exist') % n)
2843 raise util.Abort(_('tag \'%s\' does not exist') % n)
2844 if repo.tagtype(n) != expectedtype:
2844 if repo.tagtype(n) != expectedtype:
2845 if expectedtype == 'global':
2845 if expectedtype == 'global':
2846 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
2846 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
2847 else:
2847 else:
2848 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
2848 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
2849 rev_ = nullid
2849 rev_ = nullid
2850 if not message:
2850 if not message:
2851 message = _('Removed tag %s') % ', '.join(names)
2851 message = _('Removed tag %s') % ', '.join(names)
2852 elif not opts.get('force'):
2852 elif not opts.get('force'):
2853 for n in names:
2853 for n in names:
2854 if n in repo.tags():
2854 if n in repo.tags():
2855 raise util.Abort(_('tag \'%s\' already exists '
2855 raise util.Abort(_('tag \'%s\' already exists '
2856 '(use -f to force)') % n)
2856 '(use -f to force)') % n)
2857 if not rev_ and repo.dirstate.parents()[1] != nullid:
2857 if not rev_ and repo.dirstate.parents()[1] != nullid:
2858 raise util.Abort(_('uncommitted merge - please provide a '
2858 raise util.Abort(_('uncommitted merge - please provide a '
2859 'specific revision'))
2859 'specific revision'))
2860 r = repo[rev_].node()
2860 r = repo[rev_].node()
2861
2861
2862 if not message:
2862 if not message:
2863 message = (_('Added tag %s for changeset %s') %
2863 message = (_('Added tag %s for changeset %s') %
2864 (', '.join(names), short(r)))
2864 (', '.join(names), short(r)))
2865
2865
2866 date = opts.get('date')
2866 date = opts.get('date')
2867 if date:
2867 if date:
2868 date = util.parsedate(date)
2868 date = util.parsedate(date)
2869
2869
2870 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2870 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2871
2871
2872 def tags(ui, repo):
2872 def tags(ui, repo):
2873 """list repository tags
2873 """list repository tags
2874
2874
2875 This lists both regular and local tags. When the -v/--verbose
2875 This lists both regular and local tags. When the -v/--verbose
2876 switch is used, a third column "local" is printed for local tags.
2876 switch is used, a third column "local" is printed for local tags.
2877 """
2877 """
2878
2878
2879 hexfunc = ui.debugflag and hex or short
2879 hexfunc = ui.debugflag and hex or short
2880 tagtype = ""
2880 tagtype = ""
2881
2881
2882 for t, n in reversed(repo.tagslist()):
2882 for t, n in reversed(repo.tagslist()):
2883 if ui.quiet:
2883 if ui.quiet:
2884 ui.write("%s\n" % t)
2884 ui.write("%s\n" % t)
2885 continue
2885 continue
2886
2886
2887 try:
2887 try:
2888 hn = hexfunc(n)
2888 hn = hexfunc(n)
2889 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2889 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2890 except error.LookupError:
2890 except error.LookupError:
2891 r = " ?:%s" % hn
2891 r = " ?:%s" % hn
2892 else:
2892 else:
2893 spaces = " " * (30 - encoding.colwidth(t))
2893 spaces = " " * (30 - encoding.colwidth(t))
2894 if ui.verbose:
2894 if ui.verbose:
2895 if repo.tagtype(t) == 'local':
2895 if repo.tagtype(t) == 'local':
2896 tagtype = " local"
2896 tagtype = " local"
2897 else:
2897 else:
2898 tagtype = ""
2898 tagtype = ""
2899 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2899 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2900
2900
2901 def tip(ui, repo, **opts):
2901 def tip(ui, repo, **opts):
2902 """show the tip revision
2902 """show the tip revision
2903
2903
2904 The tip revision (usually just called the tip) is the most
2904 The tip revision (usually just called the tip) is the most
2905 recently added changeset in the repository, the most recently
2905 recently added changeset in the repository, the most recently
2906 changed head.
2906 changed head.
2907
2907
2908 If you have just made a commit, that commit will be the tip. If
2908 If you have just made a commit, that commit will be the tip. If
2909 you have just pulled changes from another repository, the tip of
2909 you have just pulled changes from another repository, the tip of
2910 that repository becomes the current tip. The "tip" tag is special
2910 that repository becomes the current tip. The "tip" tag is special
2911 and cannot be renamed or assigned to a different changeset.
2911 and cannot be renamed or assigned to a different changeset.
2912 """
2912 """
2913 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2913 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2914
2914
2915 def unbundle(ui, repo, fname1, *fnames, **opts):
2915 def unbundle(ui, repo, fname1, *fnames, **opts):
2916 """apply one or more changegroup files
2916 """apply one or more changegroup files
2917
2917
2918 Apply one or more compressed changegroup files generated by the
2918 Apply one or more compressed changegroup files generated by the
2919 bundle command.
2919 bundle command.
2920 """
2920 """
2921 fnames = (fname1,) + fnames
2921 fnames = (fname1,) + fnames
2922
2922
2923 lock = repo.lock()
2923 lock = repo.lock()
2924 try:
2924 try:
2925 for fname in fnames:
2925 for fname in fnames:
2926 f = url.open(ui, fname)
2926 f = url.open(ui, fname)
2927 gen = changegroup.readbundle(f, fname)
2927 gen = changegroup.readbundle(f, fname)
2928 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2928 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2929 finally:
2929 finally:
2930 lock.release()
2930 lock.release()
2931
2931
2932 return postincoming(ui, repo, modheads, opts.get('update'), None)
2932 return postincoming(ui, repo, modheads, opts.get('update'), None)
2933
2933
2934 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2934 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2935 """update working directory
2935 """update working directory
2936
2936
2937 Update the repository's working directory to the specified
2937 Update the repository's working directory to the specified
2938 revision, or the tip of the current branch if none is specified.
2938 revision, or the tip of the current branch if none is specified.
2939 Use null as the revision to remove the working copy (like 'hg
2939 Use null as the revision to remove the working copy (like 'hg
2940 clone -U').
2940 clone -U').
2941
2941
2942 When the working directory contains no uncommitted changes, it
2942 When the working directory contains no uncommitted changes, it
2943 will be replaced by the state of the requested revision from the
2943 will be replaced by the state of the requested revision from the
2944 repository. When the requested revision is on a different branch,
2944 repository. When the requested revision is on a different branch,
2945 the working directory will additionally be switched to that
2945 the working directory will additionally be switched to that
2946 branch.
2946 branch.
2947
2947
2948 When there are uncommitted changes, use option -C/--clean to
2948 When there are uncommitted changes, use option -C/--clean to
2949 discard them, forcibly replacing the state of the working
2949 discard them, forcibly replacing the state of the working
2950 directory with the requested revision.
2950 directory with the requested revision.
2951
2951
2952 When there are uncommitted changes and option -C/--clean is not
2952 When there are uncommitted changes and option -C/--clean is not
2953 used, and the parent revision and requested revision are on the
2953 used, and the parent revision and requested revision are on the
2954 same branch, and one of them is an ancestor of the other, then the
2954 same branch, and one of them is an ancestor of the other, then the
2955 new working directory will contain the requested revision merged
2955 new working directory will contain the requested revision merged
2956 with the uncommitted changes. Otherwise, the update will fail with
2956 with the uncommitted changes. Otherwise, the update will fail with
2957 a suggestion to use 'merge' or 'update -C' instead.
2957 a suggestion to use 'merge' or 'update -C' instead.
2958
2958
2959 If you want to update just one file to an older revision, use
2959 If you want to update just one file to an older revision, use
2960 revert.
2960 revert.
2961
2961
2962 See 'hg help dates' for a list of formats valid for -d/--date.
2962 See 'hg help dates' for a list of formats valid for -d/--date.
2963 """
2963 """
2964 if rev and node:
2964 if rev and node:
2965 raise util.Abort(_("please specify just one revision"))
2965 raise util.Abort(_("please specify just one revision"))
2966
2966
2967 if not rev:
2967 if not rev:
2968 rev = node
2968 rev = node
2969
2969
2970 if date:
2970 if date:
2971 if rev:
2971 if rev:
2972 raise util.Abort(_("you can't specify a revision and a date"))
2972 raise util.Abort(_("you can't specify a revision and a date"))
2973 rev = cmdutil.finddate(ui, repo, date)
2973 rev = cmdutil.finddate(ui, repo, date)
2974
2974
2975 if clean:
2975 if clean:
2976 return hg.clean(repo, rev)
2976 return hg.clean(repo, rev)
2977 else:
2977 else:
2978 return hg.update(repo, rev)
2978 return hg.update(repo, rev)
2979
2979
2980 def verify(ui, repo):
2980 def verify(ui, repo):
2981 """verify the integrity of the repository
2981 """verify the integrity of the repository
2982
2982
2983 Verify the integrity of the current repository.
2983 Verify the integrity of the current repository.
2984
2984
2985 This will perform an extensive check of the repository's
2985 This will perform an extensive check of the repository's
2986 integrity, validating the hashes and checksums of each entry in
2986 integrity, validating the hashes and checksums of each entry in
2987 the changelog, manifest, and tracked files, as well as the
2987 the changelog, manifest, and tracked files, as well as the
2988 integrity of their crosslinks and indices.
2988 integrity of their crosslinks and indices.
2989 """
2989 """
2990 return hg.verify(repo)
2990 return hg.verify(repo)
2991
2991
2992 def version_(ui):
2992 def version_(ui):
2993 """output version and copyright information"""
2993 """output version and copyright information"""
2994 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2994 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2995 % util.version())
2995 % util.version())
2996 ui.status(_(
2996 ui.status(_(
2997 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
2997 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
2998 "This is free software; see the source for copying conditions. "
2998 "This is free software; see the source for copying conditions. "
2999 "There is NO\nwarranty; "
2999 "There is NO\nwarranty; "
3000 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3000 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3001 ))
3001 ))
3002
3002
3003 # Command options and aliases are listed here, alphabetically
3003 # Command options and aliases are listed here, alphabetically
3004
3004
3005 globalopts = [
3005 globalopts = [
3006 ('R', 'repository', '',
3006 ('R', 'repository', '',
3007 _('repository root directory or symbolic path name')),
3007 _('repository root directory or symbolic path name')),
3008 ('', 'cwd', '', _('change working directory')),
3008 ('', 'cwd', '', _('change working directory')),
3009 ('y', 'noninteractive', None,
3009 ('y', 'noninteractive', None,
3010 _('do not prompt, assume \'yes\' for any required answers')),
3010 _('do not prompt, assume \'yes\' for any required answers')),
3011 ('q', 'quiet', None, _('suppress output')),
3011 ('q', 'quiet', None, _('suppress output')),
3012 ('v', 'verbose', None, _('enable additional output')),
3012 ('v', 'verbose', None, _('enable additional output')),
3013 ('', 'config', [], _('set/override config option')),
3013 ('', 'config', [], _('set/override config option')),
3014 ('', 'debug', None, _('enable debugging output')),
3014 ('', 'debug', None, _('enable debugging output')),
3015 ('', 'debugger', None, _('start debugger')),
3015 ('', 'debugger', None, _('start debugger')),
3016 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3016 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3017 ('', 'encodingmode', encoding.encodingmode,
3017 ('', 'encodingmode', encoding.encodingmode,
3018 _('set the charset encoding mode')),
3018 _('set the charset encoding mode')),
3019 ('', 'traceback', None, _('print traceback on exception')),
3019 ('', 'traceback', None, _('print traceback on exception')),
3020 ('', 'time', None, _('time how long the command takes')),
3020 ('', 'time', None, _('time how long the command takes')),
3021 ('', 'profile', None, _('print command execution profile')),
3021 ('', 'profile', None, _('print command execution profile')),
3022 ('', 'version', None, _('output version information and exit')),
3022 ('', 'version', None, _('output version information and exit')),
3023 ('h', 'help', None, _('display help and exit')),
3023 ('h', 'help', None, _('display help and exit')),
3024 ]
3024 ]
3025
3025
3026 dryrunopts = [('n', 'dry-run', None,
3026 dryrunopts = [('n', 'dry-run', None,
3027 _('do not perform actions, just print output'))]
3027 _('do not perform actions, just print output'))]
3028
3028
3029 remoteopts = [
3029 remoteopts = [
3030 ('e', 'ssh', '', _('specify ssh command to use')),
3030 ('e', 'ssh', '', _('specify ssh command to use')),
3031 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3031 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3032 ]
3032 ]
3033
3033
3034 walkopts = [
3034 walkopts = [
3035 ('I', 'include', [], _('include names matching the given patterns')),
3035 ('I', 'include', [], _('include names matching the given patterns')),
3036 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3036 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3037 ]
3037 ]
3038
3038
3039 commitopts = [
3039 commitopts = [
3040 ('m', 'message', '', _('use <text> as commit message')),
3040 ('m', 'message', '', _('use <text> as commit message')),
3041 ('l', 'logfile', '', _('read commit message from <file>')),
3041 ('l', 'logfile', '', _('read commit message from <file>')),
3042 ]
3042 ]
3043
3043
3044 commitopts2 = [
3044 commitopts2 = [
3045 ('d', 'date', '', _('record datecode as commit date')),
3045 ('d', 'date', '', _('record datecode as commit date')),
3046 ('u', 'user', '', _('record the specified user as committer')),
3046 ('u', 'user', '', _('record the specified user as committer')),
3047 ]
3047 ]
3048
3048
3049 templateopts = [
3049 templateopts = [
3050 ('', 'style', '', _('display using template map file')),
3050 ('', 'style', '', _('display using template map file')),
3051 ('', 'template', '', _('display with template')),
3051 ('', 'template', '', _('display with template')),
3052 ]
3052 ]
3053
3053
3054 logopts = [
3054 logopts = [
3055 ('p', 'patch', None, _('show patch')),
3055 ('p', 'patch', None, _('show patch')),
3056 ('g', 'git', None, _('use git extended diff format')),
3056 ('g', 'git', None, _('use git extended diff format')),
3057 ('l', 'limit', '', _('limit number of changes displayed')),
3057 ('l', 'limit', '', _('limit number of changes displayed')),
3058 ('M', 'no-merges', None, _('do not show merges')),
3058 ('M', 'no-merges', None, _('do not show merges')),
3059 ] + templateopts
3059 ] + templateopts
3060
3060
3061 diffopts = [
3061 diffopts = [
3062 ('a', 'text', None, _('treat all files as text')),
3062 ('a', 'text', None, _('treat all files as text')),
3063 ('g', 'git', None, _('use git extended diff format')),
3063 ('g', 'git', None, _('use git extended diff format')),
3064 ('', 'nodates', None, _("don't include dates in diff headers"))
3064 ('', 'nodates', None, _("don't include dates in diff headers"))
3065 ]
3065 ]
3066
3066
3067 diffopts2 = [
3067 diffopts2 = [
3068 ('p', 'show-function', None, _('show which function each change is in')),
3068 ('p', 'show-function', None, _('show which function each change is in')),
3069 ('w', 'ignore-all-space', None,
3069 ('w', 'ignore-all-space', None,
3070 _('ignore white space when comparing lines')),
3070 _('ignore white space when comparing lines')),
3071 ('b', 'ignore-space-change', None,
3071 ('b', 'ignore-space-change', None,
3072 _('ignore changes in the amount of white space')),
3072 _('ignore changes in the amount of white space')),
3073 ('B', 'ignore-blank-lines', None,
3073 ('B', 'ignore-blank-lines', None,
3074 _('ignore changes whose lines are all blank')),
3074 _('ignore changes whose lines are all blank')),
3075 ('U', 'unified', '', _('number of lines of context to show'))
3075 ('U', 'unified', '', _('number of lines of context to show'))
3076 ]
3076 ]
3077
3077
3078 similarityopts = [
3078 similarityopts = [
3079 ('s', 'similarity', '',
3079 ('s', 'similarity', '',
3080 _('guess renamed files by similarity (0<=s<=100)'))
3080 _('guess renamed files by similarity (0<=s<=100)'))
3081 ]
3081 ]
3082
3082
3083 table = {
3083 table = {
3084 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3084 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3085 "addremove":
3085 "addremove":
3086 (addremove, similarityopts + walkopts + dryrunopts,
3086 (addremove, similarityopts + walkopts + dryrunopts,
3087 _('[OPTION]... [FILE]...')),
3087 _('[OPTION]... [FILE]...')),
3088 "^annotate|blame":
3088 "^annotate|blame":
3089 (annotate,
3089 (annotate,
3090 [('r', 'rev', '', _('annotate the specified revision')),
3090 [('r', 'rev', '', _('annotate the specified revision')),
3091 ('f', 'follow', None, _('follow file copies and renames')),
3091 ('f', 'follow', None, _('follow file copies and renames')),
3092 ('a', 'text', None, _('treat all files as text')),
3092 ('a', 'text', None, _('treat all files as text')),
3093 ('u', 'user', None, _('list the author (long with -v)')),
3093 ('u', 'user', None, _('list the author (long with -v)')),
3094 ('d', 'date', None, _('list the date (short with -q)')),
3094 ('d', 'date', None, _('list the date (short with -q)')),
3095 ('n', 'number', None, _('list the revision number (default)')),
3095 ('n', 'number', None, _('list the revision number (default)')),
3096 ('c', 'changeset', None, _('list the changeset')),
3096 ('c', 'changeset', None, _('list the changeset')),
3097 ('l', 'line-number', None,
3097 ('l', 'line-number', None,
3098 _('show line number at the first appearance'))
3098 _('show line number at the first appearance'))
3099 ] + walkopts,
3099 ] + walkopts,
3100 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3100 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3101 "archive":
3101 "archive":
3102 (archive,
3102 (archive,
3103 [('', 'no-decode', None, _('do not pass files through decoders')),
3103 [('', 'no-decode', None, _('do not pass files through decoders')),
3104 ('p', 'prefix', '', _('directory prefix for files in archive')),
3104 ('p', 'prefix', '', _('directory prefix for files in archive')),
3105 ('r', 'rev', '', _('revision to distribute')),
3105 ('r', 'rev', '', _('revision to distribute')),
3106 ('t', 'type', '', _('type of distribution to create')),
3106 ('t', 'type', '', _('type of distribution to create')),
3107 ] + walkopts,
3107 ] + walkopts,
3108 _('[OPTION]... DEST')),
3108 _('[OPTION]... DEST')),
3109 "backout":
3109 "backout":
3110 (backout,
3110 (backout,
3111 [('', 'merge', None,
3111 [('', 'merge', None,
3112 _('merge with old dirstate parent after backout')),
3112 _('merge with old dirstate parent after backout')),
3113 ('', 'parent', '', _('parent to choose when backing out merge')),
3113 ('', 'parent', '', _('parent to choose when backing out merge')),
3114 ('r', 'rev', '', _('revision to backout')),
3114 ('r', 'rev', '', _('revision to backout')),
3115 ] + walkopts + commitopts + commitopts2,
3115 ] + walkopts + commitopts + commitopts2,
3116 _('[OPTION]... [-r] REV')),
3116 _('[OPTION]... [-r] REV')),
3117 "bisect":
3117 "bisect":
3118 (bisect,
3118 (bisect,
3119 [('r', 'reset', False, _('reset bisect state')),
3119 [('r', 'reset', False, _('reset bisect state')),
3120 ('g', 'good', False, _('mark changeset good')),
3120 ('g', 'good', False, _('mark changeset good')),
3121 ('b', 'bad', False, _('mark changeset bad')),
3121 ('b', 'bad', False, _('mark changeset bad')),
3122 ('s', 'skip', False, _('skip testing changeset')),
3122 ('s', 'skip', False, _('skip testing changeset')),
3123 ('c', 'command', '', _('use command to check changeset state')),
3123 ('c', 'command', '', _('use command to check changeset state')),
3124 ('U', 'noupdate', False, _('do not update to target'))],
3124 ('U', 'noupdate', False, _('do not update to target'))],
3125 _("[-gbsr] [-c CMD] [REV]")),
3125 _("[-gbsr] [-c CMD] [REV]")),
3126 "branch":
3126 "branch":
3127 (branch,
3127 (branch,
3128 [('f', 'force', None,
3128 [('f', 'force', None,
3129 _('set branch name even if it shadows an existing branch')),
3129 _('set branch name even if it shadows an existing branch')),
3130 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3130 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3131 _('[-fC] [NAME]')),
3131 _('[-fC] [NAME]')),
3132 "branches":
3132 "branches":
3133 (branches,
3133 (branches,
3134 [('a', 'active', False,
3134 [('a', 'active', False,
3135 _('show only branches that have unmerged heads'))],
3135 _('show only branches that have unmerged heads'))],
3136 _('[-a]')),
3136 _('[-a]')),
3137 "bundle":
3137 "bundle":
3138 (bundle,
3138 (bundle,
3139 [('f', 'force', None,
3139 [('f', 'force', None,
3140 _('run even when remote repository is unrelated')),
3140 _('run even when remote repository is unrelated')),
3141 ('r', 'rev', [],
3141 ('r', 'rev', [],
3142 _('a changeset up to which you would like to bundle')),
3142 _('a changeset up to which you would like to bundle')),
3143 ('', 'base', [],
3143 ('', 'base', [],
3144 _('a base changeset to specify instead of a destination')),
3144 _('a base changeset to specify instead of a destination')),
3145 ('a', 'all', None, _('bundle all changesets in the repository')),
3145 ('a', 'all', None, _('bundle all changesets in the repository')),
3146 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3146 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3147 ] + remoteopts,
3147 ] + remoteopts,
3148 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3148 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3149 "cat":
3149 "cat":
3150 (cat,
3150 (cat,
3151 [('o', 'output', '', _('print output to file with formatted name')),
3151 [('o', 'output', '', _('print output to file with formatted name')),
3152 ('r', 'rev', '', _('print the given revision')),
3152 ('r', 'rev', '', _('print the given revision')),
3153 ('', 'decode', None, _('apply any matching decode filter')),
3153 ('', 'decode', None, _('apply any matching decode filter')),
3154 ] + walkopts,
3154 ] + walkopts,
3155 _('[OPTION]... FILE...')),
3155 _('[OPTION]... FILE...')),
3156 "^clone":
3156 "^clone":
3157 (clone,
3157 (clone,
3158 [('U', 'noupdate', None,
3158 [('U', 'noupdate', None,
3159 _('the clone will only contain a repository (no working copy)')),
3159 _('the clone will only contain a repository (no working copy)')),
3160 ('r', 'rev', [],
3160 ('r', 'rev', [],
3161 _('a changeset you would like to have after cloning')),
3161 _('a changeset you would like to have after cloning')),
3162 ('', 'pull', None, _('use pull protocol to copy metadata')),
3162 ('', 'pull', None, _('use pull protocol to copy metadata')),
3163 ('', 'uncompressed', None,
3163 ('', 'uncompressed', None,
3164 _('use uncompressed transfer (fast over LAN)')),
3164 _('use uncompressed transfer (fast over LAN)')),
3165 ] + remoteopts,
3165 ] + remoteopts,
3166 _('[OPTION]... SOURCE [DEST]')),
3166 _('[OPTION]... SOURCE [DEST]')),
3167 "^commit|ci":
3167 "^commit|ci":
3168 (commit,
3168 (commit,
3169 [('A', 'addremove', None,
3169 [('A', 'addremove', None,
3170 _('mark new/missing files as added/removed before committing')),
3170 _('mark new/missing files as added/removed before committing')),
3171 ('', 'close-branch', None,
3171 ('', 'close-branch', None,
3172 _('mark a branch as closed, hiding it from the branch list')),
3172 _('mark a branch as closed, hiding it from the branch list')),
3173 ] + walkopts + commitopts + commitopts2,
3173 ] + walkopts + commitopts + commitopts2,
3174 _('[OPTION]... [FILE]...')),
3174 _('[OPTION]... [FILE]...')),
3175 "copy|cp":
3175 "copy|cp":
3176 (copy,
3176 (copy,
3177 [('A', 'after', None, _('record a copy that has already occurred')),
3177 [('A', 'after', None, _('record a copy that has already occurred')),
3178 ('f', 'force', None,
3178 ('f', 'force', None,
3179 _('forcibly copy over an existing managed file')),
3179 _('forcibly copy over an existing managed file')),
3180 ] + walkopts + dryrunopts,
3180 ] + walkopts + dryrunopts,
3181 _('[OPTION]... [SOURCE]... DEST')),
3181 _('[OPTION]... [SOURCE]... DEST')),
3182 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3182 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3183 "debugcheckstate": (debugcheckstate, []),
3183 "debugcheckstate": (debugcheckstate, []),
3184 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3184 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3185 "debugcomplete":
3185 "debugcomplete":
3186 (debugcomplete,
3186 (debugcomplete,
3187 [('o', 'options', None, _('show the command options'))],
3187 [('o', 'options', None, _('show the command options'))],
3188 _('[-o] CMD')),
3188 _('[-o] CMD')),
3189 "debugdate":
3189 "debugdate":
3190 (debugdate,
3190 (debugdate,
3191 [('e', 'extended', None, _('try extended date formats'))],
3191 [('e', 'extended', None, _('try extended date formats'))],
3192 _('[-e] DATE [RANGE]')),
3192 _('[-e] DATE [RANGE]')),
3193 "debugdata": (debugdata, [], _('FILE REV')),
3193 "debugdata": (debugdata, [], _('FILE REV')),
3194 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3194 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3195 "debugindex": (debugindex, [], _('FILE')),
3195 "debugindex": (debugindex, [], _('FILE')),
3196 "debugindexdot": (debugindexdot, [], _('FILE')),
3196 "debugindexdot": (debugindexdot, [], _('FILE')),
3197 "debuginstall": (debuginstall, []),
3197 "debuginstall": (debuginstall, []),
3198 "debugrawcommit|rawcommit":
3198 "debugrawcommit|rawcommit":
3199 (rawcommit,
3199 (rawcommit,
3200 [('p', 'parent', [], _('parent')),
3200 [('p', 'parent', [], _('parent')),
3201 ('F', 'files', '', _('file list'))
3201 ('F', 'files', '', _('file list'))
3202 ] + commitopts + commitopts2,
3202 ] + commitopts + commitopts2,
3203 _('[OPTION]... [FILE]...')),
3203 _('[OPTION]... [FILE]...')),
3204 "debugrebuildstate":
3204 "debugrebuildstate":
3205 (debugrebuildstate,
3205 (debugrebuildstate,
3206 [('r', 'rev', '', _('revision to rebuild to'))],
3206 [('r', 'rev', '', _('revision to rebuild to'))],
3207 _('[-r REV] [REV]')),
3207 _('[-r REV] [REV]')),
3208 "debugrename":
3208 "debugrename":
3209 (debugrename,
3209 (debugrename,
3210 [('r', 'rev', '', _('revision to debug'))],
3210 [('r', 'rev', '', _('revision to debug'))],
3211 _('[-r REV] FILE')),
3211 _('[-r REV] FILE')),
3212 "debugsetparents":
3212 "debugsetparents":
3213 (debugsetparents, [], _('REV1 [REV2]')),
3213 (debugsetparents, [], _('REV1 [REV2]')),
3214 "debugstate":
3214 "debugstate":
3215 (debugstate,
3215 (debugstate,
3216 [('', 'nodates', None, _('do not display the saved mtime'))],
3216 [('', 'nodates', None, _('do not display the saved mtime'))],
3217 _('[OPTION]...')),
3217 _('[OPTION]...')),
3218 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3218 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3219 "^diff":
3219 "^diff":
3220 (diff,
3220 (diff,
3221 [('r', 'rev', [], _('revision')),
3221 [('r', 'rev', [], _('revision')),
3222 ('c', 'change', '', _('change made by revision'))
3222 ('c', 'change', '', _('change made by revision'))
3223 ] + diffopts + diffopts2 + walkopts,
3223 ] + diffopts + diffopts2 + walkopts,
3224 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3224 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3225 "^export":
3225 "^export":
3226 (export,
3226 (export,
3227 [('o', 'output', '', _('print output to file with formatted name')),
3227 [('o', 'output', '', _('print output to file with formatted name')),
3228 ('', 'switch-parent', None, _('diff against the second parent'))
3228 ('', 'switch-parent', None, _('diff against the second parent'))
3229 ] + diffopts,
3229 ] + diffopts,
3230 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3230 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3231 "grep":
3231 "grep":
3232 (grep,
3232 (grep,
3233 [('0', 'print0', None, _('end fields with NUL')),
3233 [('0', 'print0', None, _('end fields with NUL')),
3234 ('', 'all', None, _('print all revisions that match')),
3234 ('', 'all', None, _('print all revisions that match')),
3235 ('f', 'follow', None,
3235 ('f', 'follow', None,
3236 _('follow changeset history, or file history across copies and renames')),
3236 _('follow changeset history, or file history across copies and renames')),
3237 ('i', 'ignore-case', None, _('ignore case when matching')),
3237 ('i', 'ignore-case', None, _('ignore case when matching')),
3238 ('l', 'files-with-matches', None,
3238 ('l', 'files-with-matches', None,
3239 _('print only filenames and revisions that match')),
3239 _('print only filenames and revisions that match')),
3240 ('n', 'line-number', None, _('print matching line numbers')),
3240 ('n', 'line-number', None, _('print matching line numbers')),
3241 ('r', 'rev', [], _('search in given revision range')),
3241 ('r', 'rev', [], _('search in given revision range')),
3242 ('u', 'user', None, _('list the author (long with -v)')),
3242 ('u', 'user', None, _('list the author (long with -v)')),
3243 ('d', 'date', None, _('list the date (short with -q)')),
3243 ('d', 'date', None, _('list the date (short with -q)')),
3244 ] + walkopts,
3244 ] + walkopts,
3245 _('[OPTION]... PATTERN [FILE]...')),
3245 _('[OPTION]... PATTERN [FILE]...')),
3246 "heads":
3246 "heads":
3247 (heads,
3247 (heads,
3248 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3248 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3249 ('a', 'active', False,
3249 ('a', 'active', False,
3250 _('show only the active heads from open branches')),
3250 _('show only the active heads from open branches')),
3251 ] + templateopts,
3251 ] + templateopts,
3252 _('[-r REV] [REV]...')),
3252 _('[-r REV] [REV]...')),
3253 "help": (help_, [], _('[TOPIC]')),
3253 "help": (help_, [], _('[TOPIC]')),
3254 "identify|id":
3254 "identify|id":
3255 (identify,
3255 (identify,
3256 [('r', 'rev', '', _('identify the specified revision')),
3256 [('r', 'rev', '', _('identify the specified revision')),
3257 ('n', 'num', None, _('show local revision number')),
3257 ('n', 'num', None, _('show local revision number')),
3258 ('i', 'id', None, _('show global revision id')),
3258 ('i', 'id', None, _('show global revision id')),
3259 ('b', 'branch', None, _('show branch')),
3259 ('b', 'branch', None, _('show branch')),
3260 ('t', 'tags', None, _('show tags'))],
3260 ('t', 'tags', None, _('show tags'))],
3261 _('[-nibt] [-r REV] [SOURCE]')),
3261 _('[-nibt] [-r REV] [SOURCE]')),
3262 "import|patch":
3262 "import|patch":
3263 (import_,
3263 (import_,
3264 [('p', 'strip', 1,
3264 [('p', 'strip', 1,
3265 _('directory strip option for patch. This has the same '
3265 _('directory strip option for patch. This has the same '
3266 'meaning as the corresponding patch option')),
3266 'meaning as the corresponding patch option')),
3267 ('b', 'base', '', _('base path')),
3267 ('b', 'base', '', _('base path')),
3268 ('f', 'force', None,
3268 ('f', 'force', None,
3269 _('skip check for outstanding uncommitted changes')),
3269 _('skip check for outstanding uncommitted changes')),
3270 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3270 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3271 ('', 'exact', None,
3271 ('', 'exact', None,
3272 _('apply patch to the nodes from which it was generated')),
3272 _('apply patch to the nodes from which it was generated')),
3273 ('', 'import-branch', None,
3273 ('', 'import-branch', None,
3274 _('use any branch information in patch (implied by --exact)'))] +
3274 _('use any branch information in patch (implied by --exact)'))] +
3275 commitopts + commitopts2 + similarityopts,
3275 commitopts + commitopts2 + similarityopts,
3276 _('[OPTION]... PATCH...')),
3276 _('[OPTION]... PATCH...')),
3277 "incoming|in":
3277 "incoming|in":
3278 (incoming,
3278 (incoming,
3279 [('f', 'force', None,
3279 [('f', 'force', None,
3280 _('run even when remote repository is unrelated')),
3280 _('run even when remote repository is unrelated')),
3281 ('n', 'newest-first', None, _('show newest record first')),
3281 ('n', 'newest-first', None, _('show newest record first')),
3282 ('', 'bundle', '', _('file to store the bundles into')),
3282 ('', 'bundle', '', _('file to store the bundles into')),
3283 ('r', 'rev', [],
3283 ('r', 'rev', [],
3284 _('a specific revision up to which you would like to pull')),
3284 _('a specific revision up to which you would like to pull')),
3285 ] + logopts + remoteopts,
3285 ] + logopts + remoteopts,
3286 _('[-p] [-n] [-M] [-f] [-r REV]...'
3286 _('[-p] [-n] [-M] [-f] [-r REV]...'
3287 ' [--bundle FILENAME] [SOURCE]')),
3287 ' [--bundle FILENAME] [SOURCE]')),
3288 "^init":
3288 "^init":
3289 (init,
3289 (init,
3290 remoteopts,
3290 remoteopts,
3291 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3291 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3292 "locate":
3292 "locate":
3293 (locate,
3293 (locate,
3294 [('r', 'rev', '', _('search the repository as it stood at REV')),
3294 [('r', 'rev', '', _('search the repository as it stood at REV')),
3295 ('0', 'print0', None,
3295 ('0', 'print0', None,
3296 _('end filenames with NUL, for use with xargs')),
3296 _('end filenames with NUL, for use with xargs')),
3297 ('f', 'fullpath', None,
3297 ('f', 'fullpath', None,
3298 _('print complete paths from the filesystem root')),
3298 _('print complete paths from the filesystem root')),
3299 ] + walkopts,
3299 ] + walkopts,
3300 _('[OPTION]... [PATTERN]...')),
3300 _('[OPTION]... [PATTERN]...')),
3301 "^log|history":
3301 "^log|history":
3302 (log,
3302 (log,
3303 [('f', 'follow', None,
3303 [('f', 'follow', None,
3304 _('follow changeset history, or file history across copies and renames')),
3304 _('follow changeset history, or file history across copies and renames')),
3305 ('', 'follow-first', None,
3305 ('', 'follow-first', None,
3306 _('only follow the first parent of merge changesets')),
3306 _('only follow the first parent of merge changesets')),
3307 ('d', 'date', '', _('show revisions matching date spec')),
3307 ('d', 'date', '', _('show revisions matching date spec')),
3308 ('C', 'copies', None, _('show copied files')),
3308 ('C', 'copies', None, _('show copied files')),
3309 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3309 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3310 ('r', 'rev', [], _('show the specified revision or range')),
3310 ('r', 'rev', [], _('show the specified revision or range')),
3311 ('', 'removed', None, _('include revisions where files were removed')),
3311 ('', 'removed', None, _('include revisions where files were removed')),
3312 ('m', 'only-merges', None, _('show only merges')),
3312 ('m', 'only-merges', None, _('show only merges')),
3313 ('u', 'user', [], _('revisions committed by user')),
3313 ('u', 'user', [], _('revisions committed by user')),
3314 ('b', 'only-branch', [],
3314 ('b', 'only-branch', [],
3315 _('show only changesets within the given named branch')),
3315 _('show only changesets within the given named branch')),
3316 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3316 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3317 ] + logopts + walkopts,
3317 ] + logopts + walkopts,
3318 _('[OPTION]... [FILE]')),
3318 _('[OPTION]... [FILE]')),
3319 "manifest":
3319 "manifest":
3320 (manifest,
3320 (manifest,
3321 [('r', 'rev', '', _('revision to display'))],
3321 [('r', 'rev', '', _('revision to display'))],
3322 _('[-r REV]')),
3322 _('[-r REV]')),
3323 "^merge":
3323 "^merge":
3324 (merge,
3324 (merge,
3325 [('f', 'force', None, _('force a merge with outstanding changes')),
3325 [('f', 'force', None, _('force a merge with outstanding changes')),
3326 ('r', 'rev', '', _('revision to merge')),
3326 ('r', 'rev', '', _('revision to merge')),
3327 ],
3327 ],
3328 _('[-f] [[-r] REV]')),
3328 _('[-f] [[-r] REV]')),
3329 "outgoing|out":
3329 "outgoing|out":
3330 (outgoing,
3330 (outgoing,
3331 [('f', 'force', None,
3331 [('f', 'force', None,
3332 _('run even when remote repository is unrelated')),
3332 _('run even when remote repository is unrelated')),
3333 ('r', 'rev', [],
3333 ('r', 'rev', [],
3334 _('a specific revision up to which you would like to push')),
3334 _('a specific revision up to which you would like to push')),
3335 ('n', 'newest-first', None, _('show newest record first')),
3335 ('n', 'newest-first', None, _('show newest record first')),
3336 ] + logopts + remoteopts,
3336 ] + logopts + remoteopts,
3337 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3337 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3338 "^parents":
3338 "^parents":
3339 (parents,
3339 (parents,
3340 [('r', 'rev', '', _('show parents from the specified revision')),
3340 [('r', 'rev', '', _('show parents from the specified revision')),
3341 ] + templateopts,
3341 ] + templateopts,
3342 _('hg parents [-r REV] [FILE]')),
3342 _('hg parents [-r REV] [FILE]')),
3343 "paths": (paths, [], _('[NAME]')),
3343 "paths": (paths, [], _('[NAME]')),
3344 "^pull":
3344 "^pull":
3345 (pull,
3345 (pull,
3346 [('u', 'update', None,
3346 [('u', 'update', None,
3347 _('update to new tip if changesets were pulled')),
3347 _('update to new tip if changesets were pulled')),
3348 ('f', 'force', None,
3348 ('f', 'force', None,
3349 _('run even when remote repository is unrelated')),
3349 _('run even when remote repository is unrelated')),
3350 ('r', 'rev', [],
3350 ('r', 'rev', [],
3351 _('a specific revision up to which you would like to pull')),
3351 _('a specific revision up to which you would like to pull')),
3352 ] + remoteopts,
3352 ] + remoteopts,
3353 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3353 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3354 "^push":
3354 "^push":
3355 (push,
3355 (push,
3356 [('f', 'force', None, _('force push')),
3356 [('f', 'force', None, _('force push')),
3357 ('r', 'rev', [],
3357 ('r', 'rev', [],
3358 _('a specific revision up to which you would like to push')),
3358 _('a specific revision up to which you would like to push')),
3359 ] + remoteopts,
3359 ] + remoteopts,
3360 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3360 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3361 "recover": (recover, []),
3361 "recover": (recover, []),
3362 "^remove|rm":
3362 "^remove|rm":
3363 (remove,
3363 (remove,
3364 [('A', 'after', None, _('record delete for missing files')),
3364 [('A', 'after', None, _('record delete for missing files')),
3365 ('f', 'force', None,
3365 ('f', 'force', None,
3366 _('remove (and delete) file even if added or modified')),
3366 _('remove (and delete) file even if added or modified')),
3367 ] + walkopts,
3367 ] + walkopts,
3368 _('[OPTION]... FILE...')),
3368 _('[OPTION]... FILE...')),
3369 "rename|mv":
3369 "rename|mv":
3370 (rename,
3370 (rename,
3371 [('A', 'after', None, _('record a rename that has already occurred')),
3371 [('A', 'after', None, _('record a rename that has already occurred')),
3372 ('f', 'force', None,
3372 ('f', 'force', None,
3373 _('forcibly copy over an existing managed file')),
3373 _('forcibly copy over an existing managed file')),
3374 ] + walkopts + dryrunopts,
3374 ] + walkopts + dryrunopts,
3375 _('[OPTION]... SOURCE... DEST')),
3375 _('[OPTION]... SOURCE... DEST')),
3376 "resolve":
3376 "resolve":
3377 (resolve,
3377 (resolve,
3378 [('a', 'all', None, _('remerge all unresolved files')),
3378 [('a', 'all', None, _('remerge all unresolved files')),
3379 ('l', 'list', None, _('list state of files needing merge')),
3379 ('l', 'list', None, _('list state of files needing merge')),
3380 ('m', 'mark', None, _('mark files as resolved')),
3380 ('m', 'mark', None, _('mark files as resolved')),
3381 ('u', 'unmark', None, _('unmark files as resolved'))]
3381 ('u', 'unmark', None, _('unmark files as resolved'))]
3382 + walkopts,
3382 + walkopts,
3383 _('[OPTION]... [FILE]...')),
3383 _('[OPTION]... [FILE]...')),
3384 "revert":
3384 "revert":
3385 (revert,
3385 (revert,
3386 [('a', 'all', None, _('revert all changes when no arguments given')),
3386 [('a', 'all', None, _('revert all changes when no arguments given')),
3387 ('d', 'date', '', _('tipmost revision matching date')),
3387 ('d', 'date', '', _('tipmost revision matching date')),
3388 ('r', 'rev', '', _('revision to revert to')),
3388 ('r', 'rev', '', _('revision to revert to')),
3389 ('', 'no-backup', None, _('do not save backup copies of files')),
3389 ('', 'no-backup', None, _('do not save backup copies of files')),
3390 ] + walkopts + dryrunopts,
3390 ] + walkopts + dryrunopts,
3391 _('[OPTION]... [-r REV] [NAME]...')),
3391 _('[OPTION]... [-r REV] [NAME]...')),
3392 "rollback": (rollback, []),
3392 "rollback": (rollback, []),
3393 "root": (root, []),
3393 "root": (root, []),
3394 "^serve":
3394 "^serve":
3395 (serve,
3395 (serve,
3396 [('A', 'accesslog', '', _('name of access log file to write to')),
3396 [('A', 'accesslog', '', _('name of access log file to write to')),
3397 ('d', 'daemon', None, _('run server in background')),
3397 ('d', 'daemon', None, _('run server in background')),
3398 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3398 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3399 ('E', 'errorlog', '', _('name of error log file to write to')),
3399 ('E', 'errorlog', '', _('name of error log file to write to')),
3400 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3400 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3401 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3401 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3402 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3402 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3403 ('n', 'name', '',
3403 ('n', 'name', '',
3404 _('name to show in web pages (default: working directory)')),
3404 _('name to show in web pages (default: working directory)')),
3405 ('', 'webdir-conf', '', _('name of the webdir config file'
3405 ('', 'webdir-conf', '', _('name of the webdir config file'
3406 ' (serve more than one repository)')),
3406 ' (serve more than one repository)')),
3407 ('', 'pid-file', '', _('name of file to write process ID to')),
3407 ('', 'pid-file', '', _('name of file to write process ID to')),
3408 ('', 'stdio', None, _('for remote clients')),
3408 ('', 'stdio', None, _('for remote clients')),
3409 ('t', 'templates', '', _('web templates to use')),
3409 ('t', 'templates', '', _('web templates to use')),
3410 ('', 'style', '', _('template style to use')),
3410 ('', 'style', '', _('template style to use')),
3411 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3411 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3412 ('', 'certificate', '', _('SSL certificate file'))],
3412 ('', 'certificate', '', _('SSL certificate file'))],
3413 _('[OPTION]...')),
3413 _('[OPTION]...')),
3414 "showconfig|debugconfig":
3414 "showconfig|debugconfig":
3415 (showconfig,
3415 (showconfig,
3416 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3416 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3417 _('[-u] [NAME]...')),
3417 _('[-u] [NAME]...')),
3418 "^status|st":
3418 "^status|st":
3419 (status,
3419 (status,
3420 [('A', 'all', None, _('show status of all files')),
3420 [('A', 'all', None, _('show status of all files')),
3421 ('m', 'modified', None, _('show only modified files')),
3421 ('m', 'modified', None, _('show only modified files')),
3422 ('a', 'added', None, _('show only added files')),
3422 ('a', 'added', None, _('show only added files')),
3423 ('r', 'removed', None, _('show only removed files')),
3423 ('r', 'removed', None, _('show only removed files')),
3424 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3424 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3425 ('c', 'clean', None, _('show only files without changes')),
3425 ('c', 'clean', None, _('show only files without changes')),
3426 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3426 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3427 ('i', 'ignored', None, _('show only ignored files')),
3427 ('i', 'ignored', None, _('show only ignored files')),
3428 ('n', 'no-status', None, _('hide status prefix')),
3428 ('n', 'no-status', None, _('hide status prefix')),
3429 ('C', 'copies', None, _('show source of copied files')),
3429 ('C', 'copies', None, _('show source of copied files')),
3430 ('0', 'print0', None,
3430 ('0', 'print0', None,
3431 _('end filenames with NUL, for use with xargs')),
3431 _('end filenames with NUL, for use with xargs')),
3432 ('', 'rev', [], _('show difference from revision')),
3432 ('', 'rev', [], _('show difference from revision')),
3433 ] + walkopts,
3433 ] + walkopts,
3434 _('[OPTION]... [FILE]...')),
3434 _('[OPTION]... [FILE]...')),
3435 "tag":
3435 "tag":
3436 (tag,
3436 (tag,
3437 [('f', 'force', None, _('replace existing tag')),
3437 [('f', 'force', None, _('replace existing tag')),
3438 ('l', 'local', None, _('make the tag local')),
3438 ('l', 'local', None, _('make the tag local')),
3439 ('r', 'rev', '', _('revision to tag')),
3439 ('r', 'rev', '', _('revision to tag')),
3440 ('', 'remove', None, _('remove a tag')),
3440 ('', 'remove', None, _('remove a tag')),
3441 # -l/--local is already there, commitopts cannot be used
3441 # -l/--local is already there, commitopts cannot be used
3442 ('m', 'message', '', _('use <text> as commit message')),
3442 ('m', 'message', '', _('use <text> as commit message')),
3443 ] + commitopts2,
3443 ] + commitopts2,
3444 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3444 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3445 "tags": (tags, []),
3445 "tags": (tags, []),
3446 "tip":
3446 "tip":
3447 (tip,
3447 (tip,
3448 [('p', 'patch', None, _('show patch')),
3448 [('p', 'patch', None, _('show patch')),
3449 ('g', 'git', None, _('use git extended diff format')),
3449 ('g', 'git', None, _('use git extended diff format')),
3450 ] + templateopts,
3450 ] + templateopts,
3451 _('[-p]')),
3451 _('[-p]')),
3452 "unbundle":
3452 "unbundle":
3453 (unbundle,
3453 (unbundle,
3454 [('u', 'update', None,
3454 [('u', 'update', None,
3455 _('update to new tip if changesets were unbundled'))],
3455 _('update to new tip if changesets were unbundled'))],
3456 _('[-u] FILE...')),
3456 _('[-u] FILE...')),
3457 "^update|up|checkout|co":
3457 "^update|up|checkout|co":
3458 (update,
3458 (update,
3459 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3459 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3460 ('d', 'date', '', _('tipmost revision matching date')),
3460 ('d', 'date', '', _('tipmost revision matching date')),
3461 ('r', 'rev', '', _('revision'))],
3461 ('r', 'rev', '', _('revision'))],
3462 _('[-C] [-d DATE] [[-r] REV]')),
3462 _('[-C] [-d DATE] [[-r] REV]')),
3463 "verify": (verify, []),
3463 "verify": (verify, []),
3464 "version": (version_, []),
3464 "version": (version_, []),
3465 }
3465 }
3466
3466
3467 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3467 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3468 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3468 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3469 optionalrepo = ("identify paths serve showconfig debugancestor")
3469 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,110 +1,110 b''
1 # fancyopts.py - better command line parsing
1 # fancyopts.py - better command line parsing
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
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 import getopt
8 import getopt
9
9
10 def gnugetopt(args, options, longoptions):
10 def gnugetopt(args, options, longoptions):
11 """Parse options mostly like getopt.gnu_getopt.
11 """Parse options mostly like getopt.gnu_getopt.
12
12
13 This is different from getopt.gnu_getopt in that an argument of - will
13 This is different from getopt.gnu_getopt in that an argument of - will
14 become an argument of - instead of vanishing completely.
14 become an argument of - instead of vanishing completely.
15 """
15 """
16 extraargs = []
16 extraargs = []
17 if '--' in args:
17 if '--' in args:
18 stopindex = args.index('--')
18 stopindex = args.index('--')
19 extraargs = args[stopindex+1:]
19 extraargs = args[stopindex+1:]
20 args = args[:stopindex]
20 args = args[:stopindex]
21 opts, parseargs = getopt.getopt(args, options, longoptions)
21 opts, parseargs = getopt.getopt(args, options, longoptions)
22 args = []
22 args = []
23 while parseargs:
23 while parseargs:
24 arg = parseargs.pop(0)
24 arg = parseargs.pop(0)
25 if arg and arg[0] == '-' and len(arg) > 1:
25 if arg and arg[0] == '-' and len(arg) > 1:
26 parseargs.insert(0, arg)
26 parseargs.insert(0, arg)
27 topts, newparseargs = getopt.getopt(parseargs, options, longoptions)
27 topts, newparseargs = getopt.getopt(parseargs, options, longoptions)
28 opts = opts + topts
28 opts = opts + topts
29 parseargs = newparseargs
29 parseargs = newparseargs
30 else:
30 else:
31 args.append(arg)
31 args.append(arg)
32 args.extend(extraargs)
32 args.extend(extraargs)
33 return opts, args
33 return opts, args
34
34
35
35
36 def fancyopts(args, options, state, gnu=False):
36 def fancyopts(args, options, state, gnu=False):
37 """
37 """
38 read args, parse options, and store options in state
38 read args, parse options, and store options in state
39
39
40 each option is a tuple of:
40 each option is a tuple of:
41
41
42 short option or ''
42 short option or ''
43 long option
43 long option
44 default value
44 default value
45 description
45 description
46
46
47 option types include:
47 option types include:
48
48
49 boolean or none - option sets variable in state to true
49 boolean or none - option sets variable in state to true
50 string - parameter string is stored in state
50 string - parameter string is stored in state
51 list - parameter string is added to a list
51 list - parameter string is added to a list
52 integer - parameter strings is stored as int
52 integer - parameter strings is stored as int
53 function - call function with parameter
53 function - call function with parameter
54
54
55 non-option args are returned
55 non-option args are returned
56 """
56 """
57 namelist = []
57 namelist = []
58 shortlist = ''
58 shortlist = ''
59 argmap = {}
59 argmap = {}
60 defmap = {}
60 defmap = {}
61
61
62 for short, name, default, comment in options:
62 for short, name, default, comment in options:
63 # convert opts to getopt format
63 # convert opts to getopt format
64 oname = name
64 oname = name
65 name = name.replace('-', '_')
65 name = name.replace('-', '_')
66
66
67 argmap['-' + short] = argmap['--' + oname] = name
67 argmap['-' + short] = argmap['--' + oname] = name
68 defmap[name] = default
68 defmap[name] = default
69
69
70 # copy defaults to state
70 # copy defaults to state
71 if isinstance(default, list):
71 if isinstance(default, list):
72 state[name] = default[:]
72 state[name] = default[:]
73 elif callable(default):
73 elif hasattr(default, '__call__'):
74 state[name] = None
74 state[name] = None
75 else:
75 else:
76 state[name] = default
76 state[name] = default
77
77
78 # does it take a parameter?
78 # does it take a parameter?
79 if not (default is None or default is True or default is False):
79 if not (default is None or default is True or default is False):
80 if short: short += ':'
80 if short: short += ':'
81 if oname: oname += '='
81 if oname: oname += '='
82 if short:
82 if short:
83 shortlist += short
83 shortlist += short
84 if name:
84 if name:
85 namelist.append(oname)
85 namelist.append(oname)
86
86
87 # parse arguments
87 # parse arguments
88 if gnu:
88 if gnu:
89 parse = gnugetopt
89 parse = gnugetopt
90 else:
90 else:
91 parse = getopt.getopt
91 parse = getopt.getopt
92 opts, args = parse(args, shortlist, namelist)
92 opts, args = parse(args, shortlist, namelist)
93
93
94 # transfer result to state
94 # transfer result to state
95 for opt, val in opts:
95 for opt, val in opts:
96 name = argmap[opt]
96 name = argmap[opt]
97 t = type(defmap[name])
97 t = type(defmap[name])
98 if t is type(fancyopts):
98 if t is type(fancyopts):
99 state[name] = defmap[name](val)
99 state[name] = defmap[name](val)
100 elif t is type(1):
100 elif t is type(1):
101 state[name] = int(val)
101 state[name] = int(val)
102 elif t is type(''):
102 elif t is type(''):
103 state[name] = val
103 state[name] = val
104 elif t is type([]):
104 elif t is type([]):
105 state[name].append(val)
105 state[name].append(val)
106 elif t is type(None) or t is type(False):
106 elif t is type(None) or t is type(False):
107 state[name] = True
107 state[name] = True
108
108
109 # return unparsed args
109 # return unparsed args
110 return args
110 return args
@@ -1,127 +1,127 b''
1 # hook.py - hook support for mercurial
1 # hook.py - hook support for mercurial
2 #
2 #
3 # Copyright 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 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 i18n import _
8 from i18n import _
9 import os, sys
9 import os, sys
10 import extensions, util
10 import extensions, util
11
11
12 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
12 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
13 '''call python hook. hook is callable object, looked up as
13 '''call python hook. hook is callable object, looked up as
14 name in python module. if callable returns "true", hook
14 name in python module. if callable returns "true", hook
15 fails, else passes. if hook raises exception, treated as
15 fails, else passes. if hook raises exception, treated as
16 hook failure. exception propagates if throw is "true".
16 hook failure. exception propagates if throw is "true".
17
17
18 reason for "true" meaning "hook failed" is so that
18 reason for "true" meaning "hook failed" is so that
19 unmodified commands (e.g. mercurial.commands.update) can
19 unmodified commands (e.g. mercurial.commands.update) can
20 be run as hooks without wrappers to convert return values.'''
20 be run as hooks without wrappers to convert return values.'''
21
21
22 ui.note(_("calling hook %s: %s\n") % (hname, funcname))
22 ui.note(_("calling hook %s: %s\n") % (hname, funcname))
23 obj = funcname
23 obj = funcname
24 if not callable(obj):
24 if not hasattr(obj, '__call__'):
25 d = funcname.rfind('.')
25 d = funcname.rfind('.')
26 if d == -1:
26 if d == -1:
27 raise util.Abort(_('%s hook is invalid ("%s" not in '
27 raise util.Abort(_('%s hook is invalid ("%s" not in '
28 'a module)') % (hname, funcname))
28 'a module)') % (hname, funcname))
29 modname = funcname[:d]
29 modname = funcname[:d]
30 try:
30 try:
31 obj = __import__(modname)
31 obj = __import__(modname)
32 except ImportError:
32 except ImportError:
33 try:
33 try:
34 # extensions are loaded with hgext_ prefix
34 # extensions are loaded with hgext_ prefix
35 obj = __import__("hgext_%s" % modname)
35 obj = __import__("hgext_%s" % modname)
36 except ImportError:
36 except ImportError:
37 raise util.Abort(_('%s hook is invalid '
37 raise util.Abort(_('%s hook is invalid '
38 '(import of "%s" failed)') %
38 '(import of "%s" failed)') %
39 (hname, modname))
39 (hname, modname))
40 try:
40 try:
41 for p in funcname.split('.')[1:]:
41 for p in funcname.split('.')[1:]:
42 obj = getattr(obj, p)
42 obj = getattr(obj, p)
43 except AttributeError:
43 except AttributeError:
44 raise util.Abort(_('%s hook is invalid '
44 raise util.Abort(_('%s hook is invalid '
45 '("%s" is not defined)') %
45 '("%s" is not defined)') %
46 (hname, funcname))
46 (hname, funcname))
47 if not callable(obj):
47 if not hasattr(obj, '__call__'):
48 raise util.Abort(_('%s hook is invalid '
48 raise util.Abort(_('%s hook is invalid '
49 '("%s" is not callable)') %
49 '("%s" is not callable)') %
50 (hname, funcname))
50 (hname, funcname))
51 try:
51 try:
52 r = obj(ui=ui, repo=repo, hooktype=name, **args)
52 r = obj(ui=ui, repo=repo, hooktype=name, **args)
53 except KeyboardInterrupt:
53 except KeyboardInterrupt:
54 raise
54 raise
55 except Exception, exc:
55 except Exception, exc:
56 if isinstance(exc, util.Abort):
56 if isinstance(exc, util.Abort):
57 ui.warn(_('error: %s hook failed: %s\n') %
57 ui.warn(_('error: %s hook failed: %s\n') %
58 (hname, exc.args[0]))
58 (hname, exc.args[0]))
59 else:
59 else:
60 ui.warn(_('error: %s hook raised an exception: '
60 ui.warn(_('error: %s hook raised an exception: '
61 '%s\n') % (hname, exc))
61 '%s\n') % (hname, exc))
62 if throw:
62 if throw:
63 raise
63 raise
64 ui.traceback()
64 ui.traceback()
65 return True
65 return True
66 if r:
66 if r:
67 if throw:
67 if throw:
68 raise util.Abort(_('%s hook failed') % hname)
68 raise util.Abort(_('%s hook failed') % hname)
69 ui.warn(_('warning: %s hook failed\n') % hname)
69 ui.warn(_('warning: %s hook failed\n') % hname)
70 return r
70 return r
71
71
72 def _exthook(ui, repo, name, cmd, args, throw):
72 def _exthook(ui, repo, name, cmd, args, throw):
73 ui.note(_("running hook %s: %s\n") % (name, cmd))
73 ui.note(_("running hook %s: %s\n") % (name, cmd))
74
74
75 env = {}
75 env = {}
76 for k, v in args.iteritems():
76 for k, v in args.iteritems():
77 if callable(v):
77 if hasattr(v, '__call__'):
78 v = v()
78 v = v()
79 env['HG_' + k.upper()] = v
79 env['HG_' + k.upper()] = v
80
80
81 if repo:
81 if repo:
82 cwd = repo.root
82 cwd = repo.root
83 else:
83 else:
84 cwd = os.getcwd()
84 cwd = os.getcwd()
85 r = util.system(cmd, environ=env, cwd=cwd)
85 r = util.system(cmd, environ=env, cwd=cwd)
86 if r:
86 if r:
87 desc, r = util.explain_exit(r)
87 desc, r = util.explain_exit(r)
88 if throw:
88 if throw:
89 raise util.Abort(_('%s hook %s') % (name, desc))
89 raise util.Abort(_('%s hook %s') % (name, desc))
90 ui.warn(_('warning: %s hook %s\n') % (name, desc))
90 ui.warn(_('warning: %s hook %s\n') % (name, desc))
91 return r
91 return r
92
92
93 _redirect = False
93 _redirect = False
94 def redirect(state):
94 def redirect(state):
95 global _redirect
95 global _redirect
96 _redirect = state
96 _redirect = state
97
97
98 def hook(ui, repo, name, throw=False, **args):
98 def hook(ui, repo, name, throw=False, **args):
99 r = False
99 r = False
100
100
101 if _redirect:
101 if _redirect:
102 # temporarily redirect stdout to stderr
102 # temporarily redirect stdout to stderr
103 oldstdout = os.dup(sys.__stdout__.fileno())
103 oldstdout = os.dup(sys.__stdout__.fileno())
104 os.dup2(sys.__stderr__.fileno(), sys.__stdout__.fileno())
104 os.dup2(sys.__stderr__.fileno(), sys.__stdout__.fileno())
105
105
106 try:
106 try:
107 for hname, cmd in ui.configitems('hooks'):
107 for hname, cmd in ui.configitems('hooks'):
108 if hname.split('.')[0] != name or not cmd:
108 if hname.split('.')[0] != name or not cmd:
109 continue
109 continue
110 if callable(cmd):
110 if hasattr(cmd, '__call__'):
111 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
111 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
112 elif cmd.startswith('python:'):
112 elif cmd.startswith('python:'):
113 if cmd.count(':') == 2:
113 if cmd.count(':') == 2:
114 path, cmd = cmd[7:].split(':')
114 path, cmd = cmd[7:].split(':')
115 mod = extensions.loadpath(path, 'hgkook.%s' % hname)
115 mod = extensions.loadpath(path, 'hgkook.%s' % hname)
116 hookfn = getattr(mod, cmd)
116 hookfn = getattr(mod, cmd)
117 else:
117 else:
118 hookfn = cmd[7:].strip()
118 hookfn = cmd[7:].strip()
119 r = _pythonhook(ui, repo, name, hname, hookfn, args, throw) or r
119 r = _pythonhook(ui, repo, name, hname, hookfn, args, throw) or r
120 else:
120 else:
121 r = _exthook(ui, repo, hname, cmd, args, throw) or r
121 r = _exthook(ui, repo, hname, cmd, args, throw) or r
122 finally:
122 finally:
123 if _redirect:
123 if _redirect:
124 os.dup2(oldstdout, sys.__stdout__.fileno())
124 os.dup2(oldstdout, sys.__stdout__.fileno())
125 os.close(oldstdout)
125 os.close(oldstdout)
126
126
127 return r
127 return r
@@ -1,509 +1,501 b''
1 # merge.py - directory-level update/merge handling for Mercurial
1 # merge.py - directory-level update/merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 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 nullid, nullrev, hex, bin
8 from node import nullid, nullrev, hex, bin
9 from i18n import _
9 from i18n import _
10 import util, filemerge, copies
10 import util, filemerge, copies
11 import errno, os, shutil
11 import errno, os, shutil
12
12
13 class mergestate(object):
13 class mergestate(object):
14 '''track 3-way merge state of individual files'''
14 '''track 3-way merge state of individual files'''
15 def __init__(self, repo):
15 def __init__(self, repo):
16 self._repo = repo
16 self._repo = repo
17 self._read()
17 self._read()
18 def reset(self, node=None):
18 def reset(self, node=None):
19 self._state = {}
19 self._state = {}
20 if node:
20 if node:
21 self._local = node
21 self._local = node
22 shutil.rmtree(self._repo.join("merge"), True)
22 shutil.rmtree(self._repo.join("merge"), True)
23 def _read(self):
23 def _read(self):
24 self._state = {}
24 self._state = {}
25 try:
25 try:
26 localnode = None
26 localnode = None
27 f = self._repo.opener("merge/state")
27 f = self._repo.opener("merge/state")
28 for i, l in enumerate(f):
28 for i, l in enumerate(f):
29 if i == 0:
29 if i == 0:
30 localnode = l[:-1]
30 localnode = l[:-1]
31 else:
31 else:
32 bits = l[:-1].split("\0")
32 bits = l[:-1].split("\0")
33 self._state[bits[0]] = bits[1:]
33 self._state[bits[0]] = bits[1:]
34 self._local = bin(localnode)
34 self._local = bin(localnode)
35 except IOError, err:
35 except IOError, err:
36 if err.errno != errno.ENOENT:
36 if err.errno != errno.ENOENT:
37 raise
37 raise
38 def _write(self):
38 def _write(self):
39 f = self._repo.opener("merge/state", "w")
39 f = self._repo.opener("merge/state", "w")
40 f.write(hex(self._local) + "\n")
40 f.write(hex(self._local) + "\n")
41 for d, v in self._state.iteritems():
41 for d, v in self._state.iteritems():
42 f.write("\0".join([d] + v) + "\n")
42 f.write("\0".join([d] + v) + "\n")
43 def add(self, fcl, fco, fca, fd, flags):
43 def add(self, fcl, fco, fca, fd, flags):
44 hash = util.sha1(fcl.path()).hexdigest()
44 hash = util.sha1(fcl.path()).hexdigest()
45 self._repo.opener("merge/" + hash, "w").write(fcl.data())
45 self._repo.opener("merge/" + hash, "w").write(fcl.data())
46 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
46 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
47 hex(fca.filenode()), fco.path(), flags]
47 hex(fca.filenode()), fco.path(), flags]
48 self._write()
48 self._write()
49 def __contains__(self, dfile):
49 def __contains__(self, dfile):
50 return dfile in self._state
50 return dfile in self._state
51 def __getitem__(self, dfile):
51 def __getitem__(self, dfile):
52 return self._state[dfile][0]
52 return self._state[dfile][0]
53 def __iter__(self):
53 def __iter__(self):
54 l = self._state.keys()
54 l = self._state.keys()
55 l.sort()
55 l.sort()
56 for f in l:
56 for f in l:
57 yield f
57 yield f
58 def mark(self, dfile, state):
58 def mark(self, dfile, state):
59 self._state[dfile][0] = state
59 self._state[dfile][0] = state
60 self._write()
60 self._write()
61 def resolve(self, dfile, wctx, octx):
61 def resolve(self, dfile, wctx, octx):
62 if self[dfile] == 'r':
62 if self[dfile] == 'r':
63 return 0
63 return 0
64 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
64 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
65 f = self._repo.opener("merge/" + hash)
65 f = self._repo.opener("merge/" + hash)
66 self._repo.wwrite(dfile, f.read(), flags)
66 self._repo.wwrite(dfile, f.read(), flags)
67 fcd = wctx[dfile]
67 fcd = wctx[dfile]
68 fco = octx[ofile]
68 fco = octx[ofile]
69 fca = self._repo.filectx(afile, fileid=anode)
69 fca = self._repo.filectx(afile, fileid=anode)
70 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
70 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
71 if not r:
71 if not r:
72 self.mark(dfile, 'r')
72 self.mark(dfile, 'r')
73 return r
73 return r
74
74
75 def _checkunknown(wctx, mctx):
75 def _checkunknown(wctx, mctx):
76 "check for collisions between unknown files and files in mctx"
76 "check for collisions between unknown files and files in mctx"
77 for f in wctx.unknown():
77 for f in wctx.unknown():
78 if f in mctx and mctx[f].cmp(wctx[f].data()):
78 if f in mctx and mctx[f].cmp(wctx[f].data()):
79 raise util.Abort(_("untracked file in working directory differs"
79 raise util.Abort(_("untracked file in working directory differs"
80 " from file in requested revision: '%s'") % f)
80 " from file in requested revision: '%s'") % f)
81
81
82 def _checkcollision(mctx):
82 def _checkcollision(mctx):
83 "check for case folding collisions in the destination context"
83 "check for case folding collisions in the destination context"
84 folded = {}
84 folded = {}
85 for fn in mctx:
85 for fn in mctx:
86 fold = fn.lower()
86 fold = fn.lower()
87 if fold in folded:
87 if fold in folded:
88 raise util.Abort(_("case-folding collision between %s and %s")
88 raise util.Abort(_("case-folding collision between %s and %s")
89 % (fn, folded[fold]))
89 % (fn, folded[fold]))
90 folded[fold] = fn
90 folded[fold] = fn
91
91
92 def _forgetremoved(wctx, mctx, branchmerge):
92 def _forgetremoved(wctx, mctx, branchmerge):
93 """
93 """
94 Forget removed files
94 Forget removed files
95
95
96 If we're jumping between revisions (as opposed to merging), and if
96 If we're jumping between revisions (as opposed to merging), and if
97 neither the working directory nor the target rev has the file,
97 neither the working directory nor the target rev has the file,
98 then we need to remove it from the dirstate, to prevent the
98 then we need to remove it from the dirstate, to prevent the
99 dirstate from listing the file when it is no longer in the
99 dirstate from listing the file when it is no longer in the
100 manifest.
100 manifest.
101
101
102 If we're merging, and the other revision has removed a file
102 If we're merging, and the other revision has removed a file
103 that is not present in the working directory, we need to mark it
103 that is not present in the working directory, we need to mark it
104 as removed.
104 as removed.
105 """
105 """
106
106
107 action = []
107 action = []
108 state = branchmerge and 'r' or 'f'
108 state = branchmerge and 'r' or 'f'
109 for f in wctx.deleted():
109 for f in wctx.deleted():
110 if f not in mctx:
110 if f not in mctx:
111 action.append((f, state))
111 action.append((f, state))
112
112
113 if not branchmerge:
113 if not branchmerge:
114 for f in wctx.removed():
114 for f in wctx.removed():
115 if f not in mctx:
115 if f not in mctx:
116 action.append((f, "f"))
116 action.append((f, "f"))
117
117
118 return action
118 return action
119
119
120 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
120 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
121 """
121 """
122 Merge p1 and p2 with ancestor ma and generate merge action list
122 Merge p1 and p2 with ancestor ma and generate merge action list
123
123
124 overwrite = whether we clobber working files
124 overwrite = whether we clobber working files
125 partial = function to filter file lists
125 partial = function to filter file lists
126 """
126 """
127
127
128 repo.ui.note(_("resolving manifests\n"))
128 repo.ui.note(_("resolving manifests\n"))
129 repo.ui.debug(_(" overwrite %s partial %s\n") % (overwrite, bool(partial)))
129 repo.ui.debug(_(" overwrite %s partial %s\n") % (overwrite, bool(partial)))
130 repo.ui.debug(_(" ancestor %s local %s remote %s\n") % (pa, p1, p2))
130 repo.ui.debug(_(" ancestor %s local %s remote %s\n") % (pa, p1, p2))
131
131
132 m1 = p1.manifest()
132 m1 = p1.manifest()
133 m2 = p2.manifest()
133 m2 = p2.manifest()
134 ma = pa.manifest()
134 ma = pa.manifest()
135 backwards = (pa == p2)
135 backwards = (pa == p2)
136 action = []
136 action = []
137 copy, copied, diverge = {}, {}, {}
137 copy, copied, diverge = {}, {}, {}
138
138
139 def fmerge(f, f2=None, fa=None):
139 def fmerge(f, f2=None, fa=None):
140 """merge flags"""
140 """merge flags"""
141 if not f2:
141 if not f2:
142 f2 = f
142 f2 = f
143 fa = f
143 fa = f
144 a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
144 a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
145 if m == n: # flags agree
145 if m == n: # flags agree
146 return m # unchanged
146 return m # unchanged
147 if m and n: # flags are set but don't agree
147 if m and n: # flags are set but don't agree
148 if not a: # both differ from parent
148 if not a: # both differ from parent
149 r = repo.ui.prompt(
149 r = repo.ui.prompt(
150 _(" conflicting flags for %s\n"
150 _(" conflicting flags for %s\n"
151 "(n)one, e(x)ec or sym(l)ink?") % f,
151 "(n)one, e(x)ec or sym(l)ink?") % f,
152 (_("&None"), _("E&xec"), _("Sym&link")), _("n"))
152 (_("&None"), _("E&xec"), _("Sym&link")), _("n"))
153 return r != _("n") and r or ''
153 return r != _("n") and r or ''
154 if m == a:
154 if m == a:
155 return n # changed from m to n
155 return n # changed from m to n
156 return m # changed from n to m
156 return m # changed from n to m
157 if m and m != a: # changed from a to m
157 if m and m != a: # changed from a to m
158 return m
158 return m
159 if n and n != a: # changed from a to n
159 if n and n != a: # changed from a to n
160 return n
160 return n
161 return '' # flag was cleared
161 return '' # flag was cleared
162
162
163 def act(msg, m, f, *args):
163 def act(msg, m, f, *args):
164 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
164 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
165 action.append((f, m) + args)
165 action.append((f, m) + args)
166
166
167 if pa and not (backwards or overwrite):
167 if pa and not (backwards or overwrite):
168 if repo.ui.configbool("merge", "followcopies", True):
168 if repo.ui.configbool("merge", "followcopies", True):
169 dirs = repo.ui.configbool("merge", "followdirs", True)
169 dirs = repo.ui.configbool("merge", "followdirs", True)
170 copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
170 copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
171 copied = set(copy.values())
171 copied = set(copy.values())
172 for of, fl in diverge.iteritems():
172 for of, fl in diverge.iteritems():
173 act("divergent renames", "dr", of, fl)
173 act("divergent renames", "dr", of, fl)
174
174
175 # Compare manifests
175 # Compare manifests
176 for f, n in m1.iteritems():
176 for f, n in m1.iteritems():
177 if partial and not partial(f):
177 if partial and not partial(f):
178 continue
178 continue
179 if f in m2:
179 if f in m2:
180 if overwrite or backwards:
180 if overwrite or backwards:
181 rflags = m2.flags(f)
181 rflags = m2.flags(f)
182 else:
182 else:
183 rflags = fmerge(f)
183 rflags = fmerge(f)
184 # are files different?
184 # are files different?
185 if n != m2[f]:
185 if n != m2[f]:
186 a = ma.get(f, nullid)
186 a = ma.get(f, nullid)
187 # are we clobbering?
187 # are we clobbering?
188 if overwrite:
188 if overwrite:
189 act("clobbering", "g", f, rflags)
189 act("clobbering", "g", f, rflags)
190 # or are we going back in time and clean?
190 # or are we going back in time and clean?
191 elif backwards:
191 elif backwards:
192 if not n[20:] or not p2[f].cmp(p1[f].data()):
192 if not n[20:] or not p2[f].cmp(p1[f].data()):
193 act("reverting", "g", f, rflags)
193 act("reverting", "g", f, rflags)
194 # are both different from the ancestor?
194 # are both different from the ancestor?
195 elif n != a and m2[f] != a:
195 elif n != a and m2[f] != a:
196 act("versions differ", "m", f, f, f, rflags, False)
196 act("versions differ", "m", f, f, f, rflags, False)
197 # is remote's version newer?
197 # is remote's version newer?
198 elif m2[f] != a:
198 elif m2[f] != a:
199 act("remote is newer", "g", f, rflags)
199 act("remote is newer", "g", f, rflags)
200 # local is newer, not overwrite, check mode bits
200 # local is newer, not overwrite, check mode bits
201 elif m1.flags(f) != rflags:
201 elif m1.flags(f) != rflags:
202 act("update permissions", "e", f, rflags)
202 act("update permissions", "e", f, rflags)
203 # contents same, check mode bits
203 # contents same, check mode bits
204 elif m1.flags(f) != rflags:
204 elif m1.flags(f) != rflags:
205 act("update permissions", "e", f, rflags)
205 act("update permissions", "e", f, rflags)
206 elif f in copied:
206 elif f in copied:
207 continue
207 continue
208 elif f in copy:
208 elif f in copy:
209 f2 = copy[f]
209 f2 = copy[f]
210 if f2 not in m2: # directory rename
210 if f2 not in m2: # directory rename
211 act("remote renamed directory to " + f2, "d",
211 act("remote renamed directory to " + f2, "d",
212 f, None, f2, m1.flags(f))
212 f, None, f2, m1.flags(f))
213 elif f2 in m1: # case 2 A,B/B/B
213 elif f2 in m1: # case 2 A,B/B/B
214 act("local copied to " + f2, "m",
214 act("local copied to " + f2, "m",
215 f, f2, f, fmerge(f, f2, f2), False)
215 f, f2, f, fmerge(f, f2, f2), False)
216 else: # case 4,21 A/B/B
216 else: # case 4,21 A/B/B
217 act("local moved to " + f2, "m",
217 act("local moved to " + f2, "m",
218 f, f2, f, fmerge(f, f2, f2), False)
218 f, f2, f, fmerge(f, f2, f2), False)
219 elif f in ma:
219 elif f in ma:
220 if n != ma[f] and not overwrite:
220 if n != ma[f] and not overwrite:
221 if repo.ui.prompt(
221 if repo.ui.prompt(
222 _(" local changed %s which remote deleted\n"
222 _(" local changed %s which remote deleted\n"
223 "use (c)hanged version or (d)elete?") % f,
223 "use (c)hanged version or (d)elete?") % f,
224 (_("&Changed"), _("&Delete")), _("c")) == _("d"):
224 (_("&Changed"), _("&Delete")), _("c")) == _("d"):
225 act("prompt delete", "r", f)
225 act("prompt delete", "r", f)
226 act("prompt keep", "a", f)
226 act("prompt keep", "a", f)
227 else:
227 else:
228 act("other deleted", "r", f)
228 act("other deleted", "r", f)
229 else:
229 else:
230 # file is created on branch or in working directory
230 # file is created on branch or in working directory
231 if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
231 if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
232 act("remote deleted", "r", f)
232 act("remote deleted", "r", f)
233
233
234 for f, n in m2.iteritems():
234 for f, n in m2.iteritems():
235 if partial and not partial(f):
235 if partial and not partial(f):
236 continue
236 continue
237 if f in m1:
237 if f in m1:
238 continue
238 continue
239 if f in copied:
239 if f in copied:
240 continue
240 continue
241 if f in copy:
241 if f in copy:
242 f2 = copy[f]
242 f2 = copy[f]
243 if f2 not in m1: # directory rename
243 if f2 not in m1: # directory rename
244 act("local renamed directory to " + f2, "d",
244 act("local renamed directory to " + f2, "d",
245 None, f, f2, m2.flags(f))
245 None, f, f2, m2.flags(f))
246 elif f2 in m2: # rename case 1, A/A,B/A
246 elif f2 in m2: # rename case 1, A/A,B/A
247 act("remote copied to " + f, "m",
247 act("remote copied to " + f, "m",
248 f2, f, f, fmerge(f2, f, f2), False)
248 f2, f, f, fmerge(f2, f, f2), False)
249 else: # case 3,20 A/B/A
249 else: # case 3,20 A/B/A
250 act("remote moved to " + f, "m",
250 act("remote moved to " + f, "m",
251 f2, f, f, fmerge(f2, f, f2), True)
251 f2, f, f, fmerge(f2, f, f2), True)
252 elif f in ma:
252 elif f in ma:
253 if overwrite or backwards:
253 if overwrite or backwards:
254 act("recreating", "g", f, m2.flags(f))
254 act("recreating", "g", f, m2.flags(f))
255 elif n != ma[f]:
255 elif n != ma[f]:
256 if repo.ui.prompt(
256 if repo.ui.prompt(
257 _("remote changed %s which local deleted\n"
257 _("remote changed %s which local deleted\n"
258 "use (c)hanged version or leave (d)eleted?") % f,
258 "use (c)hanged version or leave (d)eleted?") % f,
259 (_("&Changed"), _("&Deleted")), _("c")) == _("c"):
259 (_("&Changed"), _("&Deleted")), _("c")) == _("c"):
260 act("prompt recreating", "g", f, m2.flags(f))
260 act("prompt recreating", "g", f, m2.flags(f))
261 else:
261 else:
262 act("remote created", "g", f, m2.flags(f))
262 act("remote created", "g", f, m2.flags(f))
263
263
264 return action
264 return action
265
265
266 def actioncmp(a1, a2):
266 def actionkey(a):
267 m1 = a1[1]
267 return a[1] == 'r' and -1 or 0, a
268 m2 = a2[1]
269 if m1 == m2:
270 return cmp(a1, a2)
271 if m1 == 'r':
272 return -1
273 if m2 == 'r':
274 return 1
275 return cmp(a1, a2)
276
268
277 def applyupdates(repo, action, wctx, mctx):
269 def applyupdates(repo, action, wctx, mctx):
278 "apply the merge action list to the working directory"
270 "apply the merge action list to the working directory"
279
271
280 updated, merged, removed, unresolved = 0, 0, 0, 0
272 updated, merged, removed, unresolved = 0, 0, 0, 0
281 ms = mergestate(repo)
273 ms = mergestate(repo)
282 ms.reset(wctx.parents()[0].node())
274 ms.reset(wctx.parents()[0].node())
283 moves = []
275 moves = []
284 action.sort(actioncmp)
276 action.sort(key=actionkey)
285
277
286 # prescan for merges
278 # prescan for merges
287 for a in action:
279 for a in action:
288 f, m = a[:2]
280 f, m = a[:2]
289 if m == 'm': # merge
281 if m == 'm': # merge
290 f2, fd, flags, move = a[2:]
282 f2, fd, flags, move = a[2:]
291 repo.ui.debug(_("preserving %s for resolve of %s\n") % (f, fd))
283 repo.ui.debug(_("preserving %s for resolve of %s\n") % (f, fd))
292 fcl = wctx[f]
284 fcl = wctx[f]
293 fco = mctx[f2]
285 fco = mctx[f2]
294 fca = fcl.ancestor(fco) or repo.filectx(f, fileid=nullrev)
286 fca = fcl.ancestor(fco) or repo.filectx(f, fileid=nullrev)
295 ms.add(fcl, fco, fca, fd, flags)
287 ms.add(fcl, fco, fca, fd, flags)
296 if f != fd and move:
288 if f != fd and move:
297 moves.append(f)
289 moves.append(f)
298
290
299 # remove renamed files after safely stored
291 # remove renamed files after safely stored
300 for f in moves:
292 for f in moves:
301 if util.lexists(repo.wjoin(f)):
293 if util.lexists(repo.wjoin(f)):
302 repo.ui.debug(_("removing %s\n") % f)
294 repo.ui.debug(_("removing %s\n") % f)
303 os.unlink(repo.wjoin(f))
295 os.unlink(repo.wjoin(f))
304
296
305 audit_path = util.path_auditor(repo.root)
297 audit_path = util.path_auditor(repo.root)
306
298
307 for a in action:
299 for a in action:
308 f, m = a[:2]
300 f, m = a[:2]
309 if f and f[0] == "/":
301 if f and f[0] == "/":
310 continue
302 continue
311 if m == "r": # remove
303 if m == "r": # remove
312 repo.ui.note(_("removing %s\n") % f)
304 repo.ui.note(_("removing %s\n") % f)
313 audit_path(f)
305 audit_path(f)
314 try:
306 try:
315 util.unlink(repo.wjoin(f))
307 util.unlink(repo.wjoin(f))
316 except OSError, inst:
308 except OSError, inst:
317 if inst.errno != errno.ENOENT:
309 if inst.errno != errno.ENOENT:
318 repo.ui.warn(_("update failed to remove %s: %s!\n") %
310 repo.ui.warn(_("update failed to remove %s: %s!\n") %
319 (f, inst.strerror))
311 (f, inst.strerror))
320 removed += 1
312 removed += 1
321 elif m == "m": # merge
313 elif m == "m": # merge
322 f2, fd, flags, move = a[2:]
314 f2, fd, flags, move = a[2:]
323 r = ms.resolve(fd, wctx, mctx)
315 r = ms.resolve(fd, wctx, mctx)
324 if r > 0:
316 if r > 0:
325 unresolved += 1
317 unresolved += 1
326 else:
318 else:
327 if r is None:
319 if r is None:
328 updated += 1
320 updated += 1
329 else:
321 else:
330 merged += 1
322 merged += 1
331 util.set_flags(repo.wjoin(fd), 'l' in flags, 'x' in flags)
323 util.set_flags(repo.wjoin(fd), 'l' in flags, 'x' in flags)
332 if f != fd and move and util.lexists(repo.wjoin(f)):
324 if f != fd and move and util.lexists(repo.wjoin(f)):
333 repo.ui.debug(_("removing %s\n") % f)
325 repo.ui.debug(_("removing %s\n") % f)
334 os.unlink(repo.wjoin(f))
326 os.unlink(repo.wjoin(f))
335 elif m == "g": # get
327 elif m == "g": # get
336 flags = a[2]
328 flags = a[2]
337 repo.ui.note(_("getting %s\n") % f)
329 repo.ui.note(_("getting %s\n") % f)
338 t = mctx.filectx(f).data()
330 t = mctx.filectx(f).data()
339 repo.wwrite(f, t, flags)
331 repo.wwrite(f, t, flags)
340 updated += 1
332 updated += 1
341 elif m == "d": # directory rename
333 elif m == "d": # directory rename
342 f2, fd, flags = a[2:]
334 f2, fd, flags = a[2:]
343 if f:
335 if f:
344 repo.ui.note(_("moving %s to %s\n") % (f, fd))
336 repo.ui.note(_("moving %s to %s\n") % (f, fd))
345 t = wctx.filectx(f).data()
337 t = wctx.filectx(f).data()
346 repo.wwrite(fd, t, flags)
338 repo.wwrite(fd, t, flags)
347 util.unlink(repo.wjoin(f))
339 util.unlink(repo.wjoin(f))
348 if f2:
340 if f2:
349 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
341 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
350 t = mctx.filectx(f2).data()
342 t = mctx.filectx(f2).data()
351 repo.wwrite(fd, t, flags)
343 repo.wwrite(fd, t, flags)
352 updated += 1
344 updated += 1
353 elif m == "dr": # divergent renames
345 elif m == "dr": # divergent renames
354 fl = a[2]
346 fl = a[2]
355 repo.ui.warn(_("warning: detected divergent renames of %s to:\n") % f)
347 repo.ui.warn(_("warning: detected divergent renames of %s to:\n") % f)
356 for nf in fl:
348 for nf in fl:
357 repo.ui.warn(" %s\n" % nf)
349 repo.ui.warn(" %s\n" % nf)
358 elif m == "e": # exec
350 elif m == "e": # exec
359 flags = a[2]
351 flags = a[2]
360 util.set_flags(repo.wjoin(f), 'l' in flags, 'x' in flags)
352 util.set_flags(repo.wjoin(f), 'l' in flags, 'x' in flags)
361
353
362 return updated, merged, removed, unresolved
354 return updated, merged, removed, unresolved
363
355
364 def recordupdates(repo, action, branchmerge):
356 def recordupdates(repo, action, branchmerge):
365 "record merge actions to the dirstate"
357 "record merge actions to the dirstate"
366
358
367 for a in action:
359 for a in action:
368 f, m = a[:2]
360 f, m = a[:2]
369 if m == "r": # remove
361 if m == "r": # remove
370 if branchmerge:
362 if branchmerge:
371 repo.dirstate.remove(f)
363 repo.dirstate.remove(f)
372 else:
364 else:
373 repo.dirstate.forget(f)
365 repo.dirstate.forget(f)
374 elif m == "a": # re-add
366 elif m == "a": # re-add
375 if not branchmerge:
367 if not branchmerge:
376 repo.dirstate.add(f)
368 repo.dirstate.add(f)
377 elif m == "f": # forget
369 elif m == "f": # forget
378 repo.dirstate.forget(f)
370 repo.dirstate.forget(f)
379 elif m == "e": # exec change
371 elif m == "e": # exec change
380 repo.dirstate.normallookup(f)
372 repo.dirstate.normallookup(f)
381 elif m == "g": # get
373 elif m == "g": # get
382 if branchmerge:
374 if branchmerge:
383 repo.dirstate.normaldirty(f)
375 repo.dirstate.normaldirty(f)
384 else:
376 else:
385 repo.dirstate.normal(f)
377 repo.dirstate.normal(f)
386 elif m == "m": # merge
378 elif m == "m": # merge
387 f2, fd, flag, move = a[2:]
379 f2, fd, flag, move = a[2:]
388 if branchmerge:
380 if branchmerge:
389 # We've done a branch merge, mark this file as merged
381 # We've done a branch merge, mark this file as merged
390 # so that we properly record the merger later
382 # so that we properly record the merger later
391 repo.dirstate.merge(fd)
383 repo.dirstate.merge(fd)
392 if f != f2: # copy/rename
384 if f != f2: # copy/rename
393 if move:
385 if move:
394 repo.dirstate.remove(f)
386 repo.dirstate.remove(f)
395 if f != fd:
387 if f != fd:
396 repo.dirstate.copy(f, fd)
388 repo.dirstate.copy(f, fd)
397 else:
389 else:
398 repo.dirstate.copy(f2, fd)
390 repo.dirstate.copy(f2, fd)
399 else:
391 else:
400 # We've update-merged a locally modified file, so
392 # We've update-merged a locally modified file, so
401 # we set the dirstate to emulate a normal checkout
393 # we set the dirstate to emulate a normal checkout
402 # of that file some time in the past. Thus our
394 # of that file some time in the past. Thus our
403 # merge will appear as a normal local file
395 # merge will appear as a normal local file
404 # modification.
396 # modification.
405 repo.dirstate.normallookup(fd)
397 repo.dirstate.normallookup(fd)
406 if move:
398 if move:
407 repo.dirstate.forget(f)
399 repo.dirstate.forget(f)
408 elif m == "d": # directory rename
400 elif m == "d": # directory rename
409 f2, fd, flag = a[2:]
401 f2, fd, flag = a[2:]
410 if not f2 and f not in repo.dirstate:
402 if not f2 and f not in repo.dirstate:
411 # untracked file moved
403 # untracked file moved
412 continue
404 continue
413 if branchmerge:
405 if branchmerge:
414 repo.dirstate.add(fd)
406 repo.dirstate.add(fd)
415 if f:
407 if f:
416 repo.dirstate.remove(f)
408 repo.dirstate.remove(f)
417 repo.dirstate.copy(f, fd)
409 repo.dirstate.copy(f, fd)
418 if f2:
410 if f2:
419 repo.dirstate.copy(f2, fd)
411 repo.dirstate.copy(f2, fd)
420 else:
412 else:
421 repo.dirstate.normal(fd)
413 repo.dirstate.normal(fd)
422 if f:
414 if f:
423 repo.dirstate.forget(f)
415 repo.dirstate.forget(f)
424
416
425 def update(repo, node, branchmerge, force, partial):
417 def update(repo, node, branchmerge, force, partial):
426 """
418 """
427 Perform a merge between the working directory and the given node
419 Perform a merge between the working directory and the given node
428
420
429 branchmerge = whether to merge between branches
421 branchmerge = whether to merge between branches
430 force = whether to force branch merging or file overwriting
422 force = whether to force branch merging or file overwriting
431 partial = a function to filter file lists (dirstate not updated)
423 partial = a function to filter file lists (dirstate not updated)
432 """
424 """
433
425
434 wlock = repo.wlock()
426 wlock = repo.wlock()
435 try:
427 try:
436 wc = repo[None]
428 wc = repo[None]
437 if node is None:
429 if node is None:
438 # tip of current branch
430 # tip of current branch
439 try:
431 try:
440 node = repo.branchtags()[wc.branch()]
432 node = repo.branchtags()[wc.branch()]
441 except KeyError:
433 except KeyError:
442 if wc.branch() == "default": # no default branch!
434 if wc.branch() == "default": # no default branch!
443 node = repo.lookup("tip") # update to tip
435 node = repo.lookup("tip") # update to tip
444 else:
436 else:
445 raise util.Abort(_("branch %s not found") % wc.branch())
437 raise util.Abort(_("branch %s not found") % wc.branch())
446 overwrite = force and not branchmerge
438 overwrite = force and not branchmerge
447 pl = wc.parents()
439 pl = wc.parents()
448 p1, p2 = pl[0], repo[node]
440 p1, p2 = pl[0], repo[node]
449 pa = p1.ancestor(p2)
441 pa = p1.ancestor(p2)
450 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
442 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
451 fastforward = False
443 fastforward = False
452
444
453 ### check phase
445 ### check phase
454 if not overwrite and len(pl) > 1:
446 if not overwrite and len(pl) > 1:
455 raise util.Abort(_("outstanding uncommitted merges"))
447 raise util.Abort(_("outstanding uncommitted merges"))
456 if branchmerge:
448 if branchmerge:
457 if pa == p2:
449 if pa == p2:
458 raise util.Abort(_("can't merge with ancestor"))
450 raise util.Abort(_("can't merge with ancestor"))
459 elif pa == p1:
451 elif pa == p1:
460 if p1.branch() != p2.branch():
452 if p1.branch() != p2.branch():
461 fastforward = True
453 fastforward = True
462 else:
454 else:
463 raise util.Abort(_("nothing to merge (use 'hg update'"
455 raise util.Abort(_("nothing to merge (use 'hg update'"
464 " or check 'hg heads')"))
456 " or check 'hg heads')"))
465 if not force and (wc.files() or wc.deleted()):
457 if not force and (wc.files() or wc.deleted()):
466 raise util.Abort(_("outstanding uncommitted changes"))
458 raise util.Abort(_("outstanding uncommitted changes"))
467 elif not overwrite:
459 elif not overwrite:
468 if pa == p1 or pa == p2: # linear
460 if pa == p1 or pa == p2: # linear
469 pass # all good
461 pass # all good
470 elif p1.branch() == p2.branch():
462 elif p1.branch() == p2.branch():
471 if wc.files() or wc.deleted():
463 if wc.files() or wc.deleted():
472 raise util.Abort(_("crosses branches (use 'hg merge' or "
464 raise util.Abort(_("crosses branches (use 'hg merge' or "
473 "'hg update -C' to discard changes)"))
465 "'hg update -C' to discard changes)"))
474 raise util.Abort(_("crosses branches (use 'hg merge' "
466 raise util.Abort(_("crosses branches (use 'hg merge' "
475 "or 'hg update -C')"))
467 "or 'hg update -C')"))
476 elif wc.files() or wc.deleted():
468 elif wc.files() or wc.deleted():
477 raise util.Abort(_("crosses named branches (use "
469 raise util.Abort(_("crosses named branches (use "
478 "'hg update -C' to discard changes)"))
470 "'hg update -C' to discard changes)"))
479 else:
471 else:
480 # Allow jumping branches if there are no changes
472 # Allow jumping branches if there are no changes
481 overwrite = True
473 overwrite = True
482
474
483 ### calculate phase
475 ### calculate phase
484 action = []
476 action = []
485 if not force:
477 if not force:
486 _checkunknown(wc, p2)
478 _checkunknown(wc, p2)
487 if not util.checkcase(repo.path):
479 if not util.checkcase(repo.path):
488 _checkcollision(p2)
480 _checkcollision(p2)
489 action += _forgetremoved(wc, p2, branchmerge)
481 action += _forgetremoved(wc, p2, branchmerge)
490 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
482 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
491
483
492 ### apply phase
484 ### apply phase
493 if not branchmerge: # just jump to the new rev
485 if not branchmerge: # just jump to the new rev
494 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
486 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
495 if not partial:
487 if not partial:
496 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
488 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
497
489
498 stats = applyupdates(repo, action, wc, p2)
490 stats = applyupdates(repo, action, wc, p2)
499
491
500 if not partial:
492 if not partial:
501 recordupdates(repo, action, branchmerge)
493 recordupdates(repo, action, branchmerge)
502 repo.dirstate.setparents(fp1, fp2)
494 repo.dirstate.setparents(fp1, fp2)
503 if not branchmerge and not fastforward:
495 if not branchmerge and not fastforward:
504 repo.dirstate.setbranch(p2.branch())
496 repo.dirstate.setbranch(p2.branch())
505 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
497 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
506
498
507 return stats
499 return stats
508 finally:
500 finally:
509 wlock.release()
501 wlock.release()
@@ -1,222 +1,222 b''
1 # templater.py - template expansion for output
1 # templater.py - template expansion for output
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms 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 i18n import _
8 from i18n import _
9 import re, sys, os
9 import re, sys, os
10 import util, config, templatefilters
10 import util, config, templatefilters
11
11
12 path = ['templates', '../templates']
12 path = ['templates', '../templates']
13 stringify = templatefilters.stringify
13 stringify = templatefilters.stringify
14
14
15 def parsestring(s, quoted=True):
15 def parsestring(s, quoted=True):
16 '''parse a string using simple c-like syntax.
16 '''parse a string using simple c-like syntax.
17 string must be in quotes if quoted is True.'''
17 string must be in quotes if quoted is True.'''
18 if quoted:
18 if quoted:
19 if len(s) < 2 or s[0] != s[-1]:
19 if len(s) < 2 or s[0] != s[-1]:
20 raise SyntaxError(_('unmatched quotes'))
20 raise SyntaxError(_('unmatched quotes'))
21 return s[1:-1].decode('string_escape')
21 return s[1:-1].decode('string_escape')
22
22
23 return s.decode('string_escape')
23 return s.decode('string_escape')
24
24
25 class engine(object):
25 class engine(object):
26 '''template expansion engine.
26 '''template expansion engine.
27
27
28 template expansion works like this. a map file contains key=value
28 template expansion works like this. a map file contains key=value
29 pairs. if value is quoted, it is treated as string. otherwise, it
29 pairs. if value is quoted, it is treated as string. otherwise, it
30 is treated as name of template file.
30 is treated as name of template file.
31
31
32 templater is asked to expand a key in map. it looks up key, and
32 templater is asked to expand a key in map. it looks up key, and
33 looks for strings like this: {foo}. it expands {foo} by looking up
33 looks for strings like this: {foo}. it expands {foo} by looking up
34 foo in map, and substituting it. expansion is recursive: it stops
34 foo in map, and substituting it. expansion is recursive: it stops
35 when there is no more {foo} to replace.
35 when there is no more {foo} to replace.
36
36
37 expansion also allows formatting and filtering.
37 expansion also allows formatting and filtering.
38
38
39 format uses key to expand each item in list. syntax is
39 format uses key to expand each item in list. syntax is
40 {key%format}.
40 {key%format}.
41
41
42 filter uses function to transform value. syntax is
42 filter uses function to transform value. syntax is
43 {key|filter1|filter2|...}.'''
43 {key|filter1|filter2|...}.'''
44
44
45 template_re = re.compile(r"(?:(?:#(?=[\w\|%]+#))|(?:{(?=[\w\|%]+})))"
45 template_re = re.compile(r"(?:(?:#(?=[\w\|%]+#))|(?:{(?=[\w\|%]+})))"
46 r"(\w+)(?:(?:%(\w+))|((?:\|\w+)*))[#}]")
46 r"(\w+)(?:(?:%(\w+))|((?:\|\w+)*))[#}]")
47
47
48 def __init__(self, loader, filters={}, defaults={}):
48 def __init__(self, loader, filters={}, defaults={}):
49 self.loader = loader
49 self.loader = loader
50 self.filters = filters
50 self.filters = filters
51 self.defaults = defaults
51 self.defaults = defaults
52
52
53 def process(self, t, map):
53 def process(self, t, map):
54 '''Perform expansion. t is name of map element to expand. map contains
54 '''Perform expansion. t is name of map element to expand. map contains
55 added elements for use during expansion. Is a generator.'''
55 added elements for use during expansion. Is a generator.'''
56 tmpl = self.loader(t)
56 tmpl = self.loader(t)
57 iters = [self._process(tmpl, map)]
57 iters = [self._process(tmpl, map)]
58 while iters:
58 while iters:
59 try:
59 try:
60 item = iters[0].next()
60 item = iters[0].next()
61 except StopIteration:
61 except StopIteration:
62 iters.pop(0)
62 iters.pop(0)
63 continue
63 continue
64 if isinstance(item, str):
64 if isinstance(item, str):
65 yield item
65 yield item
66 elif item is None:
66 elif item is None:
67 yield ''
67 yield ''
68 elif hasattr(item, '__iter__'):
68 elif hasattr(item, '__iter__'):
69 iters.insert(0, iter(item))
69 iters.insert(0, iter(item))
70 else:
70 else:
71 yield str(item)
71 yield str(item)
72
72
73 def _process(self, tmpl, map):
73 def _process(self, tmpl, map):
74 '''Render a template. Returns a generator.'''
74 '''Render a template. Returns a generator.'''
75 while tmpl:
75 while tmpl:
76 m = self.template_re.search(tmpl)
76 m = self.template_re.search(tmpl)
77 if not m:
77 if not m:
78 yield tmpl
78 yield tmpl
79 break
79 break
80
80
81 start, end = m.span(0)
81 start, end = m.span(0)
82 key, format, fl = m.groups()
82 key, format, fl = m.groups()
83
83
84 if start:
84 if start:
85 yield tmpl[:start]
85 yield tmpl[:start]
86 tmpl = tmpl[end:]
86 tmpl = tmpl[end:]
87
87
88 if key in map:
88 if key in map:
89 v = map[key]
89 v = map[key]
90 else:
90 else:
91 v = self.defaults.get(key, "")
91 v = self.defaults.get(key, "")
92 if callable(v):
92 if hasattr(v, '__call__'):
93 v = v(**map)
93 v = v(**map)
94 if format:
94 if format:
95 if not hasattr(v, '__iter__'):
95 if not hasattr(v, '__iter__'):
96 raise SyntaxError(_("Error expanding '%s%%%s'")
96 raise SyntaxError(_("Error expanding '%s%%%s'")
97 % (key, format))
97 % (key, format))
98 lm = map.copy()
98 lm = map.copy()
99 for i in v:
99 for i in v:
100 lm.update(i)
100 lm.update(i)
101 yield self.process(format, lm)
101 yield self.process(format, lm)
102 else:
102 else:
103 if fl:
103 if fl:
104 for f in fl.split("|")[1:]:
104 for f in fl.split("|")[1:]:
105 v = self.filters[f](v)
105 v = self.filters[f](v)
106 yield v
106 yield v
107
107
108 engines = {'default': engine}
108 engines = {'default': engine}
109
109
110 class templater(object):
110 class templater(object):
111
111
112 def __init__(self, mapfile, filters={}, defaults={}, cache={},
112 def __init__(self, mapfile, filters={}, defaults={}, cache={},
113 minchunk=1024, maxchunk=65536):
113 minchunk=1024, maxchunk=65536):
114 '''set up template engine.
114 '''set up template engine.
115 mapfile is name of file to read map definitions from.
115 mapfile is name of file to read map definitions from.
116 filters is dict of functions. each transforms a value into another.
116 filters is dict of functions. each transforms a value into another.
117 defaults is dict of default map definitions.'''
117 defaults is dict of default map definitions.'''
118 self.mapfile = mapfile or 'template'
118 self.mapfile = mapfile or 'template'
119 self.cache = cache.copy()
119 self.cache = cache.copy()
120 self.map = {}
120 self.map = {}
121 self.base = (mapfile and os.path.dirname(mapfile)) or ''
121 self.base = (mapfile and os.path.dirname(mapfile)) or ''
122 self.filters = templatefilters.filters.copy()
122 self.filters = templatefilters.filters.copy()
123 self.filters.update(filters)
123 self.filters.update(filters)
124 self.defaults = defaults
124 self.defaults = defaults
125 self.minchunk, self.maxchunk = minchunk, maxchunk
125 self.minchunk, self.maxchunk = minchunk, maxchunk
126 self.engines = {}
126 self.engines = {}
127
127
128 if not mapfile:
128 if not mapfile:
129 return
129 return
130 if not os.path.exists(mapfile):
130 if not os.path.exists(mapfile):
131 raise util.Abort(_('style not found: %s') % mapfile)
131 raise util.Abort(_('style not found: %s') % mapfile)
132
132
133 conf = config.config()
133 conf = config.config()
134 conf.read(mapfile)
134 conf.read(mapfile)
135
135
136 for key, val in conf[''].items():
136 for key, val in conf[''].items():
137 if val[0] in "'\"":
137 if val[0] in "'\"":
138 try:
138 try:
139 self.cache[key] = parsestring(val)
139 self.cache[key] = parsestring(val)
140 except SyntaxError, inst:
140 except SyntaxError, inst:
141 raise SyntaxError('%s: %s' %
141 raise SyntaxError('%s: %s' %
142 (conf.source('', key), inst.args[0]))
142 (conf.source('', key), inst.args[0]))
143 else:
143 else:
144 val = 'default', val
144 val = 'default', val
145 if ':' in val[1]:
145 if ':' in val[1]:
146 val = val[1].split(':', 1)
146 val = val[1].split(':', 1)
147 self.map[key] = val[0], os.path.join(self.base, val[1])
147 self.map[key] = val[0], os.path.join(self.base, val[1])
148
148
149 def __contains__(self, key):
149 def __contains__(self, key):
150 return key in self.cache or key in self.map
150 return key in self.cache or key in self.map
151
151
152 def load(self, t):
152 def load(self, t):
153 '''Get the template for the given template name. Use a local cache.'''
153 '''Get the template for the given template name. Use a local cache.'''
154 if not t in self.cache:
154 if not t in self.cache:
155 try:
155 try:
156 self.cache[t] = open(self.map[t][1]).read()
156 self.cache[t] = open(self.map[t][1]).read()
157 except IOError, inst:
157 except IOError, inst:
158 raise IOError(inst.args[0], _('template file %s: %s') %
158 raise IOError(inst.args[0], _('template file %s: %s') %
159 (self.map[t][1], inst.args[1]))
159 (self.map[t][1], inst.args[1]))
160 return self.cache[t]
160 return self.cache[t]
161
161
162 def __call__(self, t, **map):
162 def __call__(self, t, **map):
163 ttype = t in self.map and self.map[t][0] or 'default'
163 ttype = t in self.map and self.map[t][0] or 'default'
164 proc = self.engines.get(ttype)
164 proc = self.engines.get(ttype)
165 if proc is None:
165 if proc is None:
166 proc = engines[ttype](self.load, self.filters, self.defaults)
166 proc = engines[ttype](self.load, self.filters, self.defaults)
167 self.engines[ttype] = proc
167 self.engines[ttype] = proc
168
168
169 stream = proc.process(t, map)
169 stream = proc.process(t, map)
170 if self.minchunk:
170 if self.minchunk:
171 stream = util.increasingchunks(stream, min=self.minchunk,
171 stream = util.increasingchunks(stream, min=self.minchunk,
172 max=self.maxchunk)
172 max=self.maxchunk)
173 return stream
173 return stream
174
174
175 def templatepath(name=None):
175 def templatepath(name=None):
176 '''return location of template file or directory (if no name).
176 '''return location of template file or directory (if no name).
177 returns None if not found.'''
177 returns None if not found.'''
178 normpaths = []
178 normpaths = []
179
179
180 # executable version (py2exe) doesn't support __file__
180 # executable version (py2exe) doesn't support __file__
181 if hasattr(sys, 'frozen'):
181 if hasattr(sys, 'frozen'):
182 module = sys.executable
182 module = sys.executable
183 else:
183 else:
184 module = __file__
184 module = __file__
185 for f in path:
185 for f in path:
186 if f.startswith('/'):
186 if f.startswith('/'):
187 p = f
187 p = f
188 else:
188 else:
189 fl = f.split('/')
189 fl = f.split('/')
190 p = os.path.join(os.path.dirname(module), *fl)
190 p = os.path.join(os.path.dirname(module), *fl)
191 if name:
191 if name:
192 p = os.path.join(p, name)
192 p = os.path.join(p, name)
193 if name and os.path.exists(p):
193 if name and os.path.exists(p):
194 return os.path.normpath(p)
194 return os.path.normpath(p)
195 elif os.path.isdir(p):
195 elif os.path.isdir(p):
196 normpaths.append(os.path.normpath(p))
196 normpaths.append(os.path.normpath(p))
197
197
198 return normpaths
198 return normpaths
199
199
200 def stylemap(style, paths=None):
200 def stylemap(style, paths=None):
201 """Return path to mapfile for a given style.
201 """Return path to mapfile for a given style.
202
202
203 Searches mapfile in the following locations:
203 Searches mapfile in the following locations:
204 1. templatepath/style/map
204 1. templatepath/style/map
205 2. templatepath/map-style
205 2. templatepath/map-style
206 3. templatepath/map
206 3. templatepath/map
207 """
207 """
208
208
209 if paths is None:
209 if paths is None:
210 paths = templatepath()
210 paths = templatepath()
211 elif isinstance(paths, str):
211 elif isinstance(paths, str):
212 paths = [paths]
212 paths = [paths]
213
213
214 locations = style and [os.path.join(style, "map"), "map-" + style] or []
214 locations = style and [os.path.join(style, "map"), "map-" + style] or []
215 locations.append("map")
215 locations.append("map")
216 for path in paths:
216 for path in paths:
217 for location in locations:
217 for location in locations:
218 mapfile = os.path.join(path, location)
218 mapfile = os.path.join(path, location)
219 if os.path.isfile(mapfile):
219 if os.path.isfile(mapfile):
220 return mapfile
220 return mapfile
221
221
222 raise RuntimeError("No hgweb templates found in %r" % paths)
222 raise RuntimeError("No hgweb templates found in %r" % paths)
General Comments 0
You need to be logged in to leave comments. Login now